summaryrefslogtreecommitdiff
path: root/absl
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2020-02-25 22:27:31 +0100
committerGravatar CJ Johnson <johnsoncj@google.com>2020-02-25 17:56:58 -0500
commitb832dce8489ef7b6231384909fd9b68d5a5ff2b7 (patch)
tree3ad4be9a9a4105366be714da9458e076a77be18f /absl
parentaa844899c937bde5d2b24f276b59997e5b668bde (diff)
Creation of LTS branch "lts_2020_02_25"20200225
- 0033c9ea91a52ade7c6b725aa2ef3cbe15463421 Fix build on FreeBSD/powerpc (#616) by kgotlinux <60880393+kgotlinux@users.noreply.github.com> - 0d5ce2797eb695aee7e019e25323251ef6ffc277 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - b69c7d880caddfc25bf348dbcfe9d45fdd8bc6e6 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 2a5633fc077a58528cdbfe78720f3f6bfdc6044d Merge "Export of internal Abseil changes" by Xiaoyi Zhang <zhangxy@google.com> - f9b3d6e493c1b6ab3dbdab71c5f8fa849db4abaf Add RISCV support to GetProgramCounter() (#621) by Khem Raj <raj.khem@gmail.com> - 0232c87f21c26718aa3eb2d86678070f3b498a4e Add missing ABSL_HAVE_VDSO_SUPPORT conditional (#622) by Sinan Kaya <41809318+franksinankaya@users.noreply.github.com> - 3c814105108680997d0821077694f663693b5382 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - c44657f55692eddf5504156645d1f4ec7b3acabd Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 98eb410c93ad059f9bba1bf43f5bb916fc92a5ea Export of internal Abseil changes by Abseil Team <absl-team@google.com> - bf78e977309c4cb946914b456404141ddac1c302 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - d95d1567165d449e4c213ea31a15cbb112a9865f Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 24713a7036a81498334807fa5c7ad3cb7c643711 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 72382c21fefed981b4b8a2a1b82e2d231c2c2e39 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 08a7e7bf972c8451855a5022f2faf3d3655db015 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 36bcd9599b3f48c99357ba61cf33584889306d6a Fix pointer format specifier in documentation (#614) by Andre Nguyen <andre-nguyen@users.noreply.github.com> - 0f86336b6939ea673cc1cbe29189286cae67d63a Export of internal Abseil changes by Abseil Team <absl-team@google.com> - c512f118dde6ffd51cb7d8ac8804bbaf4d266c3a Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 37dd2562ec830d547a1524bb306be313ac3f2556 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 44427702614d7b86b064ba06a390f5eb2f85dbf6 fix: Add support for more ARM processors detection (#608) by Andre Nguyen <andre-nguyen@users.noreply.github.com> - 159bf2bf6d1cc8087e02468d071e94d1177d1bae Export of internal Abseil changes by Abseil Team <absl-team@google.com> - a2e6adecc294dc4cd98cc285a9134ce58e0f2ad0 Use https links. (#586) by nlewycky <nicholas@mxc.ca> - 564001ae506a17c51fa1223684a78f05f91d3d91 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - b3aaac8a37c467a1125c794196caa90d0957bdc3 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 63ee2f8877915a3565c29707dba8fe4d7822596a Export of internal Abseil changes by Abseil Team <absl-team@google.com> - a048203a881f11f4b7b8df5fb563aec85522f8db Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 1de0166368e2ae67347f92099d6dca3ab3a4a496 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - ad904b6cd3906ddf79878003d92b7bc08d7786ae Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 29235139149790f5afc430c11cec8f1eb1677607 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - bf86cfe165ef7d70dfe68f0b8fc0c018bc79a577 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 12bc53e0318d80569270a5b26ccbc62b52022b89 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 1e39f8626a4dadec1f56920b999dd4c3cfae333e Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 77f87009a34c745255bd84d8f2647040d831a2b3 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - d659fe54b35ab9b8e35c72e50a4b8814167d5a84 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - a4b757b5d42694306a9de853cee0a5fba9c7bbe9 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 0514227d2547793b23e209809276375e41c76617 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 7f4fe64af80fe3c84db8ea938276c3690573c45e Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 16d9fd58a51c6083234e2e9f8f50e49ed5ed02e4 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - bcaae6009c0833b73c6fa7bdd972921d8081a724 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 8ba96a8244bbe334d09542e92d566673a65c1f78 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 2103fd9acdf58279f739860bff3f8c9f4b845605 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 3df7b52a6ada51a72a23796b844549a7b282f1b8 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - fa8c75182fbfdeddb2485fc0d53baeda3f40b7a3 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 85092b4b648ca729c6263c4a302a41dfff28705e Fix Conan builds (#400) by Adrian Ostrowski <adr.ostrowski@gmail.com> - e96ae2203b80d5ae2e0b7abe0c77b722b224b16d Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 20de2db748ca0471cfb61cb53e813dd12938c12b Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 846e5dbedac123d12455adcfe6f53c8b5dcbfeef Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 8207907f4f7fbaeeaa2b7340c76875e06fd345ba Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 078b89b3c046d230ef3ad39494e5852184eb528b Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 19b021cb3ff23048dfbe236a4e611925d8930831 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - ecc0033b54847f6c9ee37dbb0be8aa17e5b6d37b Always enable proper symbolize implementation on Windows ... by Loo Rong Jie <loorongjie@gmail.com> - 2796d500aea5a31d26b8b24a33fab7a1c8fa2f32 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - e4c8d0eb8ef4acb5d7a4252b3b87feb391ef7e41 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - a15364ce4d88534ae2295127e5d8e32aefb6b446 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - ab3552a18964e7063c8324f45b3896a6a20b08a8 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - e9f9000c7c80993cb589d011616b7a8016e42f4a Fix ABSL_WAITER_MODE detection for mingw (#342) by Joe Sylve <Joe.Sylve@gmail.com> - abea769b551f7a100f540967cb95debdb0080df8 Fix ABSL_HAVE_ALARM check on mingw (#341) by Joe Sylve <Joe.Sylve@gmail.com> - 25597bdfc148e91e27678ec30efa52f4fc8c164f Export of internal Abseil changes by Abseil Team <absl-team@google.com> - aad33fefaa8f744d71ce747a53717b835bdf8e84 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 8fe7214fe2d7a45ecc4d85f6a524c6b1532426de Export of internal Abseil changes by Abseil Team <absl-team@google.com> - debac94cfb5a0fa75d1d97c399682bd1c72e3191 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 882b3501a31eb0e4ae4213afb91a0e43feda7d5f Fix spelling errors (#384) by Sungmann Cho <55860394+chosungmann@users.noreply.github.com> - 502efe6d7841bff82b1aeef5500491fe9a48c635 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - ccdd1d57b6386ebc26fb0c7d99b604672437c124 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - ddf8e52a2918dd0ccec75d3e2426125fa3926724 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 6ec136281086b71da32b5fb068bd6e46b78a5c79 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - ac78ffc3bc0a8b295cab9a03817760fd460df2a1 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 5374c56e5196320681993869e3126b51edac2a43 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 97c1664b4bbab5f78fac2b151ab02656268fb34b Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 325fd7b042ff4ec34f7dd32e602cd81ad0e24b22 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 83c1d65c90a92aa49632b9ac5a793214bb0768bc Export of internal Abseil changes by Abseil Team <absl-team@google.com> - eb6b7bd23bc0815bfd784e1a74021ce166765280 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 9ddac555b7861dc178d0dbe758a1cfbed718784b Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 1948f6f967e34db9793cfa8b4bcbaf370d039fd8 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - a0d1e098c2f99694fa399b175a7ccf920762030e Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 2d2d7fbc283315b676159716376e739d3d23ed94 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 0302d1e5fa4fcdd1763b7d1bb3212943b1ae911d supppress unused variable warning for gcc (#372) by Martin <pizzard@users.noreply.github.com> - 262d74ba81b1fc4d71f459555cde8ecb39786d68 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - f0afae0d49af3e15a7169e019634d7719143d94d Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 0e7afdcbd24c7e5b7cab4e0217d8886f1525b520 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 9a41ffdd3a0ccbcdd29c4e3886b28e06f2cd9c66 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 36910d3d7e9fccadd6603f232d0c4f54dcd47c7e [bazel] Add fixes for --incompatible_load_cc_rules_from_b... by Yannic <contact@yannic-bonenberger.com> - aae8143cf9aa611f70d7ea9b95b8b8b383b2271a Export of internal Abseil changes by Abseil Team <absl-team@google.com> - d9aa92d7fb324314f9df487ac23d32a25650b742 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 321ab5303023c86cd15d9ddc5740fb4b4fde32e1 Export of internal Abseil changes by Abseil Team <absl-team@google.com> - 4ef574064e75b86f115549e9eb4c7e806781b3ab Export of internal Abseil changes by Abseil Team <absl-team@google.com> GitOrigin-RevId: 0033c9ea91a52ade7c6b725aa2ef3cbe15463421 Change-Id: I8a2b70063cb3ab40c6943a6db0fe40cae71ed8d7
Diffstat (limited to 'absl')
-rw-r--r--absl/BUILD.bazel7
-rw-r--r--absl/CMakeLists.txt8
-rwxr-xr-xabsl/abseil.podspec.gen.py229
-rw-r--r--absl/algorithm/BUILD.bazel2
-rw-r--r--absl/algorithm/CMakeLists.txt2
-rw-r--r--absl/algorithm/algorithm.h17
-rw-r--r--absl/algorithm/container.h25
-rw-r--r--absl/algorithm/container_test.cc6
-rw-r--r--absl/base/BUILD.bazel191
-rw-r--r--absl/base/CMakeLists.txt150
-rw-r--r--absl/base/attributes.h23
-rw-r--r--absl/base/bit_cast_test.cc4
-rw-r--r--absl/base/call_once.h18
-rw-r--r--absl/base/call_once_test.cc16
-rw-r--r--absl/base/casts.h4
-rw-r--r--absl/base/config.h211
-rw-r--r--absl/base/const_init.h6
-rw-r--r--absl/base/exception_safety_testing_test.cc22
-rw-r--r--absl/base/inline_variable_test.cc4
-rw-r--r--absl/base/inline_variable_test_a.cc4
-rw-r--r--absl/base/inline_variable_test_b.cc4
-rw-r--r--absl/base/internal/atomic_hook.h57
-rw-r--r--absl/base/internal/atomic_hook_test.cc33
-rw-r--r--absl/base/internal/atomic_hook_test_helper.cc (renamed from absl/random/internal/seed_salting_sequence_generator_empty_sequence.cc)28
-rw-r--r--absl/base/internal/atomic_hook_test_helper.h (renamed from absl/random/internal/seed_salting_sequence_generator.cc)30
-rw-r--r--absl/base/internal/bits.h41
-rw-r--r--absl/base/internal/cycleclock.cc4
-rw-r--r--absl/base/internal/cycleclock.h6
-rw-r--r--absl/base/internal/direct_mmap.h8
-rw-r--r--absl/base/internal/endian.h14
-rw-r--r--absl/base/internal/endian_test.cc4
-rw-r--r--absl/base/internal/errno_saver.h43
-rw-r--r--absl/base/internal/errno_saver_test.cc44
-rw-r--r--absl/base/internal/exception_safety_testing.cc4
-rw-r--r--absl/base/internal/exception_safety_testing.h7
-rw-r--r--absl/base/internal/exponential_biased.cc93
-rw-r--r--absl/base/internal/exponential_biased.h130
-rw-r--r--absl/base/internal/exponential_biased_test.cc199
-rw-r--r--absl/base/internal/hide_ptr.h6
-rw-r--r--absl/base/internal/identity.h6
-rw-r--r--absl/base/internal/inline_variable_testing.h4
-rw-r--r--absl/base/internal/invoke.h4
-rw-r--r--absl/base/internal/low_level_alloc.cc49
-rw-r--r--absl/base/internal/low_level_alloc.h4
-rw-r--r--absl/base/internal/low_level_alloc_test.cc4
-rw-r--r--absl/base/internal/low_level_scheduling.h4
-rw-r--r--absl/base/internal/periodic_sampler.cc53
-rw-r--r--absl/base/internal/periodic_sampler.h211
-rw-r--r--absl/base/internal/periodic_sampler_benchmark.cc79
-rw-r--r--absl/base/internal/periodic_sampler_test.cc177
-rw-r--r--absl/base/internal/raw_logging.cc23
-rw-r--r--absl/base/internal/raw_logging.h24
-rw-r--r--absl/base/internal/scheduling_mode.h6
-rw-r--r--absl/base/internal/scoped_set_env.cc4
-rw-r--r--absl/base/internal/scoped_set_env.h6
-rw-r--r--absl/base/internal/spinlock.cc8
-rw-r--r--absl/base/internal/spinlock.h21
-rw-r--r--absl/base/internal/spinlock_linux.inc5
-rw-r--r--absl/base/internal/spinlock_posix.inc6
-rw-r--r--absl/base/internal/spinlock_wait.cc4
-rw-r--r--absl/base/internal/spinlock_wait.h4
-rw-r--r--absl/base/internal/sysinfo.cc60
-rw-r--r--absl/base/internal/sysinfo.h15
-rw-r--r--absl/base/internal/sysinfo_test.cc8
-rw-r--r--absl/base/internal/thread_identity.cc29
-rw-r--r--absl/base/internal/thread_identity.h24
-rw-r--r--absl/base/internal/thread_identity_test.cc4
-rw-r--r--absl/base/internal/throw_delegate.cc4
-rw-r--r--absl/base/internal/throw_delegate.h6
-rw-r--r--absl/base/internal/unaligned_access.h10
-rw-r--r--absl/base/internal/unscaledcycleclock.cc41
-rw-r--r--absl/base/internal/unscaledcycleclock.h4
-rw-r--r--absl/base/invoke_test.cc4
-rw-r--r--absl/base/log_severity.cc4
-rw-r--r--absl/base/log_severity.h58
-rw-r--r--absl/base/log_severity_test.cc163
-rw-r--r--absl/base/macros.h31
-rw-r--r--absl/base/optimization.h2
-rw-r--r--absl/base/options.h211
-rw-r--r--absl/base/policy_checks.h10
-rw-r--r--absl/base/spinlock_test_common.cc4
-rw-r--r--absl/base/thread_annotations.h5
-rw-r--r--absl/base/throw_delegate_test.cc13
-rw-r--r--absl/container/BUILD.bazel157
-rw-r--r--absl/container/CMakeLists.txt132
-rw-r--r--absl/container/btree_benchmark.cc707
-rw-r--r--absl/container/btree_map.h759
-rw-r--r--absl/container/btree_set.h683
-rw-r--r--absl/container/btree_test.cc2404
-rw-r--r--absl/container/btree_test.h155
-rw-r--r--absl/container/fixed_array.h32
-rw-r--r--absl/container/fixed_array_exception_safety_test.cc96
-rw-r--r--absl/container/fixed_array_test.cc7
-rw-r--r--absl/container/flat_hash_map.h19
-rw-r--r--absl/container/flat_hash_map_test.cc80
-rw-r--r--absl/container/flat_hash_set.h12
-rw-r--r--absl/container/flat_hash_set_test.cc40
-rw-r--r--absl/container/inlined_vector.h19
-rw-r--r--absl/container/inlined_vector_benchmark.cc37
-rw-r--r--absl/container/inlined_vector_exception_safety_test.cc39
-rw-r--r--absl/container/inlined_vector_test.cc28
-rw-r--r--absl/container/internal/btree.h2614
-rw-r--r--absl/container/internal/btree_container.h672
-rw-r--r--absl/container/internal/common.h20
-rw-r--r--absl/container/internal/compressed_tuple.h4
-rw-r--r--absl/container/internal/compressed_tuple_test.cc8
-rw-r--r--absl/container/internal/container_memory.h4
-rw-r--r--absl/container/internal/container_memory_test.cc4
-rw-r--r--absl/container/internal/counting_allocator.h6
-rw-r--r--absl/container/internal/hash_function_defaults.h4
-rw-r--r--absl/container/internal/hash_function_defaults_test.cc8
-rw-r--r--absl/container/internal/hash_generator_testing.cc4
-rw-r--r--absl/container/internal/hash_generator_testing.h13
-rw-r--r--absl/container/internal/hash_policy_testing.h4
-rw-r--r--absl/container/internal/hash_policy_testing_test.cc4
-rw-r--r--absl/container/internal/hash_policy_traits.h4
-rw-r--r--absl/container/internal/hash_policy_traits_test.cc4
-rw-r--r--absl/container/internal/hashtable_debug.h4
-rw-r--r--absl/container/internal/hashtable_debug_hooks.h6
-rw-r--r--absl/container/internal/hashtablez_sampler.cc117
-rw-r--r--absl/container/internal/hashtablez_sampler.h31
-rw-r--r--absl/container/internal/hashtablez_sampler_force_weak_definition.cc9
-rw-r--r--absl/container/internal/hashtablez_sampler_test.cc6
-rw-r--r--absl/container/internal/inlined_vector.h201
-rw-r--r--absl/container/internal/layout.h4
-rw-r--r--absl/container/internal/layout_test.cc4
-rw-r--r--absl/container/internal/node_hash_policy.h6
-rw-r--r--absl/container/internal/node_hash_policy_test.cc4
-rw-r--r--absl/container/internal/raw_hash_map.h7
-rw-r--r--absl/container/internal/raw_hash_set.cc4
-rw-r--r--absl/container/internal/raw_hash_set.h80
-rw-r--r--absl/container/internal/raw_hash_set_allocator_test.cc4
-rw-r--r--absl/container/internal/raw_hash_set_test.cc55
-rw-r--r--absl/container/internal/test_instance_tracker.cc4
-rw-r--r--absl/container/internal/test_instance_tracker.h4
-rw-r--r--absl/container/internal/tracked.h7
-rw-r--r--absl/container/internal/unordered_map_constructor_test.h4
-rw-r--r--absl/container/internal/unordered_map_lookup_test.h4
-rw-r--r--absl/container/internal/unordered_map_members_test.h4
-rw-r--r--absl/container/internal/unordered_map_modifiers_test.h45
-rw-r--r--absl/container/internal/unordered_map_test.cc12
-rw-r--r--absl/container/internal/unordered_set_constructor_test.h4
-rw-r--r--absl/container/internal/unordered_set_lookup_test.h4
-rw-r--r--absl/container/internal/unordered_set_members_test.h4
-rw-r--r--absl/container/internal/unordered_set_modifiers_test.h4
-rw-r--r--absl/container/internal/unordered_set_test.cc4
-rw-r--r--absl/container/node_hash_map.h17
-rw-r--r--absl/container/node_hash_map_test.cc42
-rw-r--r--absl/container/node_hash_set.h16
-rw-r--r--absl/container/node_hash_set_test.cc40
-rw-r--r--absl/copts/AbseilConfigureCopts.cmake40
-rw-r--r--absl/copts/GENERATED_AbseilCopts.cmake25
-rw-r--r--absl/copts/GENERATED_copts.bzl25
-rw-r--r--absl/copts/configure_copts.bzl15
-rw-r--r--absl/copts/copts.py20
-rw-r--r--absl/debugging/BUILD.bazel68
-rw-r--r--absl/debugging/CMakeLists.txt47
-rw-r--r--absl/debugging/failure_signal_handler.cc21
-rw-r--r--absl/debugging/failure_signal_handler.h6
-rw-r--r--absl/debugging/failure_signal_handler_test.cc13
-rw-r--r--absl/debugging/internal/address_is_readable.cc13
-rw-r--r--absl/debugging/internal/address_is_readable.h6
-rw-r--r--absl/debugging/internal/demangle.cc38
-rw-r--r--absl/debugging/internal/demangle.h6
-rw-r--r--absl/debugging/internal/demangle_test.cc4
-rw-r--r--absl/debugging/internal/elf_mem_image.cc4
-rw-r--r--absl/debugging/internal/elf_mem_image.h6
-rw-r--r--absl/debugging/internal/examine_stack.cc6
-rw-r--r--absl/debugging/internal/examine_stack.h6
-rw-r--r--absl/debugging/internal/stack_consumption.cc16
-rw-r--r--absl/debugging/internal/stack_consumption.h6
-rw-r--r--absl/debugging/internal/stack_consumption_test.cc4
-rw-r--r--absl/debugging/internal/stacktrace_aarch64-inl.inc4
-rw-r--r--absl/debugging/internal/stacktrace_arm-inl.inc4
-rw-r--r--absl/debugging/internal/stacktrace_generic-inl.inc7
-rw-r--r--absl/debugging/internal/stacktrace_powerpc-inl.inc4
-rw-r--r--absl/debugging/internal/stacktrace_unimplemented-inl.inc4
-rw-r--r--absl/debugging/internal/stacktrace_win32-inl.inc6
-rw-r--r--absl/debugging/internal/stacktrace_x86-inl.inc16
-rw-r--r--absl/debugging/internal/symbolize.h10
-rw-r--r--absl/debugging/internal/vdso_support.cc4
-rw-r--r--absl/debugging/internal/vdso_support.h4
-rw-r--r--absl/debugging/leak_check.cc8
-rw-r--r--absl/debugging/leak_check.h6
-rw-r--r--absl/debugging/stacktrace.cc4
-rw-r--r--absl/debugging/stacktrace.h36
-rw-r--r--absl/debugging/symbolize.cc9
-rw-r--r--absl/debugging/symbolize.h8
-rw-r--r--absl/debugging/symbolize_elf.inc17
-rw-r--r--absl/debugging/symbolize_test.cc41
-rw-r--r--absl/debugging/symbolize_unimplemented.inc4
-rw-r--r--absl/debugging/symbolize_win32.inc17
-rw-r--r--absl/flags/BUILD.bazel125
-rw-r--r--absl/flags/CMakeLists.txt93
-rw-r--r--absl/flags/config.h19
-rw-r--r--absl/flags/declare.h10
-rw-r--r--absl/flags/flag.cc40
-rw-r--r--absl/flags/flag.h170
-rw-r--r--absl/flags/flag_benchmark.cc111
-rw-r--r--absl/flags/flag_test.cc130
-rw-r--r--absl/flags/flag_test_defs.cc2
-rw-r--r--absl/flags/internal/commandlineflag.cc496
-rw-r--r--absl/flags/internal/commandlineflag.h414
-rw-r--r--absl/flags/internal/commandlineflag_test.cc35
-rw-r--r--absl/flags/internal/flag.cc382
-rw-r--r--absl/flags/internal/flag.h628
-rw-r--r--absl/flags/internal/parse.h5
-rw-r--r--absl/flags/internal/path_util.h5
-rw-r--r--absl/flags/internal/program_name.cc11
-rw-r--r--absl/flags/internal/program_name.h5
-rw-r--r--absl/flags/internal/program_name_test.cc3
-rw-r--r--absl/flags/internal/registry.cc258
-rw-r--r--absl/flags/internal/registry.h57
-rw-r--r--absl/flags/internal/type_erased.cc38
-rw-r--r--absl/flags/internal/type_erased.h17
-rw-r--r--absl/flags/internal/type_erased_test.cc14
-rw-r--r--absl/flags/internal/usage.cc42
-rw-r--r--absl/flags/internal/usage.h5
-rw-r--r--absl/flags/internal/usage_test.cc84
-rw-r--r--absl/flags/marshalling.cc53
-rw-r--r--absl/flags/marshalling.h21
-rw-r--r--absl/flags/marshalling_test.cc5
-rw-r--r--absl/flags/parse.cc35
-rw-r--r--absl/flags/parse.h5
-rw-r--r--absl/flags/parse_test.cc23
-rw-r--r--absl/flags/usage.cc13
-rw-r--r--absl/flags/usage.h5
-rw-r--r--absl/flags/usage_config.cc14
-rw-r--r--absl/flags/usage_config.h7
-rw-r--r--absl/flags/usage_config_test.cc3
-rw-r--r--absl/functional/BUILD.bazel93
-rw-r--r--absl/functional/CMakeLists.txt72
-rw-r--r--absl/functional/bind_front.h184
-rw-r--r--absl/functional/bind_front_test.cc231
-rw-r--r--absl/functional/function_ref.h139
-rw-r--r--absl/functional/function_ref_benchmark.cc142
-rw-r--r--absl/functional/function_ref_test.cc257
-rw-r--r--absl/functional/internal/front_binder.h95
-rw-r--r--absl/functional/internal/function_ref.h106
-rw-r--r--absl/hash/BUILD.bazel5
-rw-r--r--absl/hash/hash.h4
-rw-r--r--absl/hash/hash_test.cc148
-rw-r--r--absl/hash/hash_testing.h4
-rw-r--r--absl/hash/internal/city.cc4
-rw-r--r--absl/hash/internal/city.h7
-rw-r--r--absl/hash/internal/city_test.cc4
-rw-r--r--absl/hash/internal/hash.cc34
-rw-r--r--absl/hash/internal/hash.h141
-rw-r--r--absl/hash/internal/spy_hash_state.h17
-rw-r--r--absl/memory/BUILD.bazel11
-rw-r--r--absl/memory/CMakeLists.txt5
-rw-r--r--absl/memory/memory.h13
-rw-r--r--absl/memory/memory_exception_safety_test.cc13
-rw-r--r--absl/meta/BUILD.bazel17
-rw-r--r--absl/meta/type_traits.h65
-rw-r--r--absl/meta/type_traits_test.cc43
-rw-r--r--absl/numeric/BUILD.bazel1
-rw-r--r--absl/numeric/int128.cc137
-rw-r--r--absl/numeric/int128.h379
-rw-r--r--absl/numeric/int128_have_intrinsic.inc284
-rw-r--r--absl/numeric/int128_no_intrinsic.inc290
-rw-r--r--absl/numeric/int128_stream_test.cc729
-rw-r--r--absl/numeric/int128_test.cc743
-rw-r--r--absl/random/BUILD.bazel140
-rw-r--r--absl/random/CMakeLists.txt226
-rw-r--r--absl/random/bernoulli_distribution.h4
-rw-r--r--absl/random/beta_distribution.h37
-rw-r--r--absl/random/beta_distribution_test.cc2
-rw-r--r--absl/random/bit_gen_ref.h153
-rw-r--r--absl/random/bit_gen_ref_test.cc101
-rw-r--r--absl/random/discrete_distribution.cc4
-rw-r--r--absl/random/discrete_distribution.h4
-rw-r--r--absl/random/distribution_format_traits.h31
-rw-r--r--absl/random/distributions.h91
-rw-r--r--absl/random/distributions_test.cc19
-rw-r--r--absl/random/exponential_distribution.h18
-rw-r--r--absl/random/exponential_distribution_test.cc6
-rw-r--r--absl/random/gaussian_distribution.cc4
-rw-r--r--absl/random/gaussian_distribution.h29
-rw-r--r--absl/random/gaussian_distribution_test.cc2
-rw-r--r--absl/random/internal/BUILD.bazel135
-rw-r--r--absl/random/internal/chi_square.cc4
-rw-r--r--absl/random/internal/chi_square.h6
-rw-r--r--absl/random/internal/distribution_caller.h6
-rw-r--r--absl/random/internal/distribution_impl.h262
-rw-r--r--absl/random/internal/distribution_test_util.cc4
-rw-r--r--absl/random/internal/distribution_test_util.h4
-rw-r--r--absl/random/internal/distributions.h36
-rw-r--r--absl/random/internal/explicit_seed_seq.h6
-rw-r--r--absl/random/internal/fast_uniform_bits.h6
-rw-r--r--absl/random/internal/fast_uniform_bits_test.cc4
-rw-r--r--absl/random/internal/fastmath.h4
-rw-r--r--absl/random/internal/gaussian_distribution_gentables.cc4
-rw-r--r--absl/random/internal/generate_real.h146
-rw-r--r--absl/random/internal/generate_real_test.cc (renamed from absl/random/internal/distribution_impl_test.cc)153
-rw-r--r--absl/random/internal/iostream_state_saver.h4
-rw-r--r--absl/random/internal/iostream_state_saver_test.cc10
-rw-r--r--absl/random/internal/mock_overload_set.h91
-rw-r--r--absl/random/internal/mocking_bit_gen_base.h120
-rw-r--r--absl/random/internal/nanobenchmark.cc14
-rw-r--r--absl/random/internal/nanobenchmark.h6
-rw-r--r--absl/random/internal/nanobenchmark_test.cc4
-rw-r--r--absl/random/internal/nonsecure_base.h4
-rw-r--r--absl/random/internal/nonsecure_base_test.cc7
-rw-r--r--absl/random/internal/pcg_engine.h4
-rw-r--r--absl/random/internal/platform.h3
-rw-r--r--absl/random/internal/pool_urbg.cc14
-rw-r--r--absl/random/internal/pool_urbg.h4
-rw-r--r--absl/random/internal/randen.cc4
-rw-r--r--absl/random/internal/randen.h4
-rw-r--r--absl/random/internal/randen_detect.cc4
-rw-r--r--absl/random/internal/randen_detect.h6
-rw-r--r--absl/random/internal/randen_engine.h4
-rw-r--r--absl/random/internal/randen_hwaes.cc142
-rw-r--r--absl/random/internal/randen_hwaes.h6
-rw-r--r--absl/random/internal/randen_slow.cc14
-rw-r--r--absl/random/internal/randen_slow.h6
-rw-r--r--absl/random/internal/randen_traits.h6
-rw-r--r--absl/random/internal/salted_seed_seq.h4
-rw-r--r--absl/random/internal/seed_material.cc16
-rw-r--r--absl/random/internal/seed_material.h4
-rw-r--r--absl/random/internal/sequence_urbg.h6
-rw-r--r--absl/random/internal/traits.h4
-rw-r--r--absl/random/internal/uniform_helper.h74
-rw-r--r--absl/random/internal/wide_multiply.h111
-rw-r--r--absl/random/internal/wide_multiply_test.cc66
-rw-r--r--absl/random/log_uniform_int_distribution.h20
-rw-r--r--absl/random/log_uniform_int_distribution_test.cc2
-rw-r--r--absl/random/mock_distributions.h261
-rw-r--r--absl/random/mock_distributions_test.cc72
-rw-r--r--absl/random/mocking_bit_gen.cc (renamed from absl/random/internal/named_generator.cc)24
-rw-r--r--absl/random/mocking_bit_gen.h196
-rw-r--r--absl/random/mocking_bit_gen_test.cc347
-rw-r--r--absl/random/poisson_distribution.h24
-rw-r--r--absl/random/poisson_distribution_test.cc4
-rw-r--r--absl/random/random.h4
-rw-r--r--absl/random/seed_gen_exception.cc4
-rw-r--r--absl/random/seed_gen_exception.h6
-rw-r--r--absl/random/seed_sequences.cc4
-rw-r--r--absl/random/seed_sequences.h4
-rw-r--r--absl/random/uniform_int_distribution.h6
-rw-r--r--absl/random/uniform_real_distribution.h21
-rw-r--r--absl/random/uniform_real_distribution_test.cc12
-rw-r--r--absl/random/zipf_distribution.h4
-rw-r--r--absl/status/BUILD.bazel65
-rw-r--r--absl/status/CMakeLists.txt52
-rw-r--r--absl/status/status.cc439
-rw-r--r--absl/status/status.h428
-rw-r--r--absl/status/status_payload_printer.cc43
-rw-r--r--absl/status/status_payload_printer.h51
-rw-r--r--absl/status/status_test.cc401
-rw-r--r--absl/strings/BUILD.bazel109
-rw-r--r--absl/strings/CMakeLists.txt80
-rw-r--r--absl/strings/ascii.cc10
-rw-r--r--absl/strings/ascii.h11
-rw-r--r--absl/strings/charconv.cc5
-rw-r--r--absl/strings/charconv.h6
-rw-r--r--absl/strings/charconv_test.cc7
-rw-r--r--absl/strings/cord.cc2019
-rw-r--r--absl/strings/cord.h1121
-rw-r--r--absl/strings/cord_test.cc1526
-rw-r--r--absl/strings/cord_test_helpers.h60
-rw-r--r--absl/strings/escaping.cc234
-rw-r--r--absl/strings/escaping.h4
-rw-r--r--absl/strings/internal/char_map.h4
-rw-r--r--absl/strings/internal/charconv_bigint.cc8
-rw-r--r--absl/strings/internal/charconv_bigint.h10
-rw-r--r--absl/strings/internal/charconv_bigint_test.cc4
-rw-r--r--absl/strings/internal/charconv_parse.cc10
-rw-r--r--absl/strings/internal/charconv_parse.h5
-rw-r--r--absl/strings/internal/cord_internal.h151
-rw-r--r--absl/strings/internal/escaping.cc180
-rw-r--r--absl/strings/internal/escaping.h58
-rw-r--r--absl/strings/internal/escaping_test_common.h4
-rw-r--r--absl/strings/internal/memutil.cc4
-rw-r--r--absl/strings/internal/memutil.h4
-rw-r--r--absl/strings/internal/numbers_test_common.h11
-rw-r--r--absl/strings/internal/ostringstream.cc4
-rw-r--r--absl/strings/internal/ostringstream.h4
-rw-r--r--absl/strings/internal/pow10_helper.cc4
-rw-r--r--absl/strings/internal/pow10_helper.h6
-rw-r--r--absl/strings/internal/pow10_helper_test.cc4
-rw-r--r--absl/strings/internal/resize_uninitialized.h7
-rw-r--r--absl/strings/internal/resize_uninitialized_test.cc14
-rw-r--r--absl/strings/internal/stl_type_traits.h4
-rw-r--r--absl/strings/internal/str_format/arg.cc55
-rw-r--r--absl/strings/internal/str_format/arg.h36
-rw-r--r--absl/strings/internal/str_format/arg_test.cc12
-rw-r--r--absl/strings/internal/str_format/bind.cc43
-rw-r--r--absl/strings/internal/str_format/bind.h18
-rw-r--r--absl/strings/internal/str_format/bind_test.cc28
-rw-r--r--absl/strings/internal/str_format/checker.h13
-rw-r--r--absl/strings/internal/str_format/checker_test.cc8
-rw-r--r--absl/strings/internal/str_format/convert_test.cc153
-rw-r--r--absl/strings/internal/str_format/extension.cc39
-rw-r--r--absl/strings/internal/str_format/extension.h366
-rw-r--r--absl/strings/internal/str_format/float_conversion.cc24
-rw-r--r--absl/strings/internal/str_format/float_conversion.h4
-rw-r--r--absl/strings/internal/str_format/output.cc4
-rw-r--r--absl/strings/internal/str_format/output.h9
-rw-r--r--absl/strings/internal/str_format/output_test.cc4
-rw-r--r--absl/strings/internal/str_format/parser.cc47
-rw-r--r--absl/strings/internal/str_format/parser.h29
-rw-r--r--absl/strings/internal/str_format/parser_test.cc50
-rw-r--r--absl/strings/internal/str_join_internal.h4
-rw-r--r--absl/strings/internal/str_split_internal.h4
-rw-r--r--absl/strings/internal/utf8.cc4
-rw-r--r--absl/strings/internal/utf8.h6
-rw-r--r--absl/strings/match.cc4
-rw-r--r--absl/strings/match.h6
-rw-r--r--absl/strings/numbers.cc152
-rw-r--r--absl/strings/numbers.h99
-rw-r--r--absl/strings/numbers_benchmark.cc23
-rw-r--r--absl/strings/numbers_test.cc110
-rw-r--r--absl/strings/str_cat.cc49
-rw-r--r--absl/strings/str_cat.h7
-rw-r--r--absl/strings/str_cat_test.cc47
-rw-r--r--absl/strings/str_format.h10
-rw-r--r--absl/strings/str_format_test.cc6
-rw-r--r--absl/strings/str_join.h4
-rw-r--r--absl/strings/str_replace.cc4
-rw-r--r--absl/strings/str_replace.h4
-rw-r--r--absl/strings/str_split.cc4
-rw-r--r--absl/strings/str_split.h4
-rw-r--r--absl/strings/string_view.cc8
-rw-r--r--absl/strings/string_view.h154
-rw-r--r--absl/strings/string_view_benchmark.cc52
-rw-r--r--absl/strings/string_view_test.cc117
-rw-r--r--absl/strings/strip.h4
-rw-r--r--absl/strings/substitute.cc15
-rw-r--r--absl/strings/substitute.h11
-rw-r--r--absl/strings/substitute_test.cc10
-rw-r--r--absl/synchronization/BUILD.bazel29
-rw-r--r--absl/synchronization/CMakeLists.txt24
-rw-r--r--absl/synchronization/barrier.cc4
-rw-r--r--absl/synchronization/barrier.h8
-rw-r--r--absl/synchronization/blocking_counter.cc4
-rw-r--r--absl/synchronization/blocking_counter.h8
-rw-r--r--absl/synchronization/blocking_counter_test.cc4
-rw-r--r--absl/synchronization/internal/create_thread_identity.cc9
-rw-r--r--absl/synchronization/internal/create_thread_identity.h8
-rw-r--r--absl/synchronization/internal/graphcycles.cc4
-rw-r--r--absl/synchronization/internal/graphcycles.h8
-rw-r--r--absl/synchronization/internal/graphcycles_test.cc4
-rw-r--r--absl/synchronization/internal/kernel_timeout.h4
-rw-r--r--absl/synchronization/internal/mutex_nonprod.cc4
-rw-r--r--absl/synchronization/internal/mutex_nonprod.inc8
-rw-r--r--absl/synchronization/internal/per_thread_sem.cc10
-rw-r--r--absl/synchronization/internal/per_thread_sem.h9
-rw-r--r--absl/synchronization/internal/per_thread_sem_test.cc4
-rw-r--r--absl/synchronization/internal/thread_pool.h8
-rw-r--r--absl/synchronization/internal/waiter.cc211
-rw-r--r--absl/synchronization/internal/waiter.h64
-rw-r--r--absl/synchronization/lifetime_test.cc10
-rw-r--r--absl/synchronization/mutex.cc37
-rw-r--r--absl/synchronization/mutex.h94
-rw-r--r--absl/synchronization/mutex_test.cc16
-rw-r--r--absl/synchronization/notification.cc13
-rw-r--r--absl/synchronization/notification.h13
-rw-r--r--absl/synchronization/notification_test.cc4
-rw-r--r--absl/time/BUILD.bazel6
-rw-r--r--absl/time/CMakeLists.txt24
-rw-r--r--absl/time/civil_time.cc94
-rw-r--r--absl/time/civil_time.h59
-rw-r--r--absl/time/civil_time_benchmark.cc20
-rw-r--r--absl/time/civil_time_test.cc158
-rw-r--r--absl/time/clock.cc22
-rw-r--r--absl/time/clock.h4
-rw-r--r--absl/time/duration.cc13
-rw-r--r--absl/time/duration_test.cc7
-rw-r--r--absl/time/format.cc23
-rw-r--r--absl/time/internal/cctz/BUILD.bazel32
-rw-r--r--absl/time/internal/cctz/include/cctz/civil_time.h7
-rw-r--r--absl/time/internal/cctz/include/cctz/civil_time_detail.h64
-rw-r--r--absl/time/internal/cctz/include/cctz/time_zone.h23
-rw-r--r--absl/time/internal/cctz/include/cctz/zone_info_source.h16
-rw-r--r--absl/time/internal/cctz/src/cctz_benchmark.cc1188
-rw-r--r--absl/time/internal/cctz/src/civil_time_detail.cc6
-rw-r--r--absl/time/internal/cctz/src/civil_time_test.cc23
-rw-r--r--absl/time/internal/cctz/src/time_zone_fixed.cc40
-rw-r--r--absl/time/internal/cctz/src/time_zone_fixed.h5
-rw-r--r--absl/time/internal/cctz/src/time_zone_format.cc29
-rw-r--r--absl/time/internal/cctz/src/time_zone_format_test.cc45
-rw-r--r--absl/time/internal/cctz/src/time_zone_if.cc6
-rw-r--r--absl/time/internal/cctz/src/time_zone_if.h14
-rw-r--r--absl/time/internal/cctz/src/time_zone_impl.cc33
-rw-r--r--absl/time/internal/cctz/src/time_zone_impl.h5
-rw-r--r--absl/time/internal/cctz/src/time_zone_info.cc111
-rw-r--r--absl/time/internal/cctz/src/time_zone_info.h12
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.cc25
-rw-r--r--absl/time/internal/cctz/src/time_zone_libc.h8
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup.cc14
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc1259
-rw-r--r--absl/time/internal/cctz/src/time_zone_posix.cc6
-rw-r--r--absl/time/internal/cctz/src/time_zone_posix.h6
-rw-r--r--absl/time/internal/cctz/src/tzfile.h45
-rw-r--r--absl/time/internal/cctz/src/zone_info_source.cc66
-rw-r--r--absl/time/internal/cctz/testdata/version2
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Detroitbin2174 -> 2230 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Edmontonbin2388 -> 2332 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_Citybin1726 -> 1684 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisvillebin2772 -> 2772 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Louisvillebin2772 -> 2772 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/America/Vancouverbin2892 -> 2892 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kongbin1193 -> 1203 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbulbin2143 -> 1947 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoulbin493 -> 617 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountainbin2388 -> 2332 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacificbin2892 -> 2892 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Brusselsbin2933 -> 2933 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbulbin2143 -> 1947 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningradbin1479 -> 1493 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Europe/Viennabin2200 -> 2200 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Hongkongbin1193 -> 1203 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fijibin1078 -> 1077 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolkbin294 -> 880 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/ROKbin493 -> 617 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/Turkeybin2143 -> 1947 bytes
-rw-r--r--absl/time/internal/cctz/testdata/zoneinfo/US/Michiganbin2174 -> 2230 bytes
-rw-r--r--absl/time/internal/get_current_time_chrono.inc4
-rw-r--r--absl/time/internal/get_current_time_posix.inc4
-rw-r--r--absl/time/internal/test_util.cc11
-rw-r--r--absl/time/internal/test_util.h4
-rw-r--r--absl/time/time.cc18
-rw-r--r--absl/time/time.h39
-rw-r--r--absl/time/time_test.cc69
-rw-r--r--absl/types/BUILD.bazel118
-rw-r--r--absl/types/CMakeLists.txt90
-rw-r--r--absl/types/any.h30
-rw-r--r--absl/types/any_exception_safety_test.cc11
-rw-r--r--absl/types/any_test.cc11
-rw-r--r--absl/types/bad_any_cast.cc8
-rw-r--r--absl/types/bad_any_cast.h14
-rw-r--r--absl/types/bad_optional_access.cc8
-rw-r--r--absl/types/bad_optional_access.h14
-rw-r--r--absl/types/bad_variant_access.cc8
-rw-r--r--absl/types/bad_variant_access.h14
-rw-r--r--absl/types/compare.h194
-rw-r--r--absl/types/compare_test.cc122
-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.h376
-rw-r--r--absl/types/internal/conformance_testing_test.cc1186
-rw-r--r--absl/types/internal/optional.h8
-rw-r--r--absl/types/internal/span.h4
-rw-r--r--absl/types/internal/variant.h8
-rw-r--r--absl/types/optional.h14
-rw-r--r--absl/types/optional_exception_safety_test.cc12
-rw-r--r--absl/types/optional_test.cc27
-rw-r--r--absl/types/span.h4
-rw-r--r--absl/types/span_test.cc2
-rw-r--r--absl/types/variant.h22
-rw-r--r--absl/types/variant_benchmark.cc4
-rw-r--r--absl/types/variant_exception_safety_test.cc25
-rw-r--r--absl/types/variant_test.cc45
-rw-r--r--absl/utility/BUILD.bazel21
-rw-r--r--absl/utility/utility.h18
557 files changed, 35126 insertions, 6548 deletions
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel
index 853330d4..5a03acf8 100644
--- a/absl/BUILD.bazel
+++ b/absl/BUILD.bazel
@@ -14,12 +14,15 @@
# limitations under the License.
#
+load(
+ ":compiler_config_setting.bzl",
+ "create_llvm_config",
+)
+
package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
-load(":compiler_config_setting.bzl", "create_llvm_config")
-
create_llvm_config(
name = "llvm_compiler",
visibility = [":__subpackages__"],
diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt
index 3e78397c..fbfa7822 100644
--- a/absl/CMakeLists.txt
+++ b/absl/CMakeLists.txt
@@ -14,20 +14,24 @@
# limitations under the License.
#
-
-
add_subdirectory(base)
add_subdirectory(algorithm)
add_subdirectory(container)
add_subdirectory(debugging)
add_subdirectory(flags)
+add_subdirectory(functional)
add_subdirectory(hash)
add_subdirectory(memory)
add_subdirectory(meta)
add_subdirectory(numeric)
add_subdirectory(random)
+add_subdirectory(status)
add_subdirectory(strings)
add_subdirectory(synchronization)
add_subdirectory(time)
add_subdirectory(types)
add_subdirectory(utility)
+
+if (${ABSL_BUILD_DLL})
+ absl_make_dll()
+endif()
diff --git a/absl/abseil.podspec.gen.py b/absl/abseil.podspec.gen.py
new file mode 100755
index 00000000..6aefb794
--- /dev/null
+++ b/absl/abseil.podspec.gen.py
@@ -0,0 +1,229 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""This script generates abseil.podspec from all BUILD.bazel files.
+
+This is expected to run on abseil git repository with Bazel 1.0 on Linux.
+It recursively analyzes BUILD.bazel files using query command of Bazel to
+dump its build rules in XML format. From these rules, it constructs podspec
+structure.
+"""
+
+import argparse
+import collections
+import os
+import re
+import subprocess
+import xml.etree.ElementTree
+
+# Template of root podspec.
+SPEC_TEMPLATE = """
+# This file has been automatically generated from a script.
+# Please make modifications to `abseil.podspec.gen.py` instead.
+Pod::Spec.new do |s|
+ s.name = 'abseil'
+ s.version = '${version}'
+ s.summary = 'Abseil Common Libraries (C++) from Google'
+ s.homepage = 'https://abseil.io'
+ s.license = 'Apache License, Version 2.0'
+ s.authors = { 'Abseil Team' => 'abseil-io@googlegroups.com' }
+ s.source = {
+ :git => 'https://github.com/abseil/abseil-cpp.git',
+ :tag => '${tag}',
+ }
+ s.module_name = 'absl'
+ s.header_mappings_dir = 'absl'
+ s.header_dir = 'absl'
+ s.libraries = 'c++'
+ s.compiler_flags = '-Wno-everything'
+ s.pod_target_xcconfig = {
+ 'USER_HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_TARGET_SRCROOT)"',
+ 'USE_HEADERMAP' => 'NO',
+ 'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+ }
+ s.ios.deployment_target = '7.0'
+ s.osx.deployment_target = '10.9'
+ s.tvos.deployment_target = '9.0'
+ s.watchos.deployment_target = '2.0'
+"""
+
+# Rule object representing the rule of Bazel BUILD.
+Rule = collections.namedtuple(
+ "Rule", "type name package srcs hdrs textual_hdrs deps visibility testonly")
+
+
+def get_elem_value(elem, name):
+ """Returns the value of XML element with the given name."""
+ for child in elem:
+ if child.attrib.get("name") != name:
+ continue
+ if child.tag == "string":
+ return child.attrib.get("value")
+ if child.tag == "boolean":
+ return child.attrib.get("value") == "true"
+ if child.tag == "list":
+ return [nested_child.attrib.get("value") for nested_child in child]
+ raise "Cannot recognize tag: " + child.tag
+ return None
+
+
+def normalize_paths(paths):
+ """Returns the list of normalized path."""
+ # e.g. ["//absl/strings:dir/header.h"] -> ["absl/strings/dir/header.h"]
+ return [path.lstrip("/").replace(":", "/") for path in paths]
+
+
+def parse_rule(elem, package):
+ """Returns a rule from bazel XML rule."""
+ return Rule(
+ type=elem.attrib["class"],
+ name=get_elem_value(elem, "name"),
+ package=package,
+ srcs=normalize_paths(get_elem_value(elem, "srcs") or []),
+ hdrs=normalize_paths(get_elem_value(elem, "hdrs") or []),
+ textual_hdrs=normalize_paths(get_elem_value(elem, "textual_hdrs") or []),
+ deps=get_elem_value(elem, "deps") or [],
+ visibility=get_elem_value(elem, "visibility") or [],
+ testonly=get_elem_value(elem, "testonly") or False)
+
+
+def read_build(package):
+ """Runs bazel query on given package file and returns all cc rules."""
+ result = subprocess.check_output(
+ ["bazel", "query", package + ":all", "--output", "xml"])
+ root = xml.etree.ElementTree.fromstring(result)
+ return [
+ parse_rule(elem, package)
+ for elem in root
+ if elem.tag == "rule" and elem.attrib["class"].startswith("cc_")
+ ]
+
+
+def collect_rules(root_path):
+ """Collects and returns all rules from root path recursively."""
+ rules = []
+ for cur, _, _ in os.walk(root_path):
+ build_path = os.path.join(cur, "BUILD.bazel")
+ if os.path.exists(build_path):
+ rules.extend(read_build("//" + cur))
+ return rules
+
+
+def relevant_rule(rule):
+ """Returns true if a given rule is relevant when generating a podspec."""
+ return (
+ # cc_library only (ignore cc_test, cc_binary)
+ rule.type == "cc_library" and
+ # ignore empty rule
+ (rule.hdrs + rule.textual_hdrs + rule.srcs) and
+ # ignore test-only rule
+ not rule.testonly)
+
+
+def get_spec_var(depth):
+ """Returns the name of variable for spec with given depth."""
+ return "s" if depth == 0 else "s{}".format(depth)
+
+
+def get_spec_name(label):
+ """Converts the label of bazel rule to the name of podspec."""
+ assert label.startswith("//absl/"), "{} doesn't start with //absl/".format(
+ label)
+ # e.g. //absl/apple/banana -> abseil/apple/banana
+ return "abseil/" + label[7:]
+
+
+def write_podspec(f, rules, args):
+ """Writes a podspec from given rules and args."""
+ rule_dir = build_rule_directory(rules)["abseil"]
+ # Write root part with given arguments
+ spec = re.sub(r"\$\{(\w+)\}", lambda x: args[x.group(1)],
+ SPEC_TEMPLATE).lstrip()
+ f.write(spec)
+ # Write all target rules
+ write_podspec_map(f, rule_dir, 0)
+ f.write("end\n")
+
+
+def build_rule_directory(rules):
+ """Builds a tree-style rule directory from given rules."""
+ rule_dir = {}
+ for rule in rules:
+ cur = rule_dir
+ for frag in get_spec_name(rule.package).split("/"):
+ cur = cur.setdefault(frag, {})
+ cur[rule.name] = rule
+ return rule_dir
+
+
+def write_podspec_map(f, cur_map, depth):
+ """Writes podspec from rule map recursively."""
+ for key, value in sorted(cur_map.items()):
+ indent = " " * (depth + 1)
+ f.write("{indent}{var0}.subspec '{key}' do |{var1}|\n".format(
+ indent=indent,
+ key=key,
+ var0=get_spec_var(depth),
+ var1=get_spec_var(depth + 1)))
+ if isinstance(value, dict):
+ write_podspec_map(f, value, depth + 1)
+ else:
+ write_podspec_rule(f, value, depth + 1)
+ f.write("{indent}end\n".format(indent=indent))
+
+
+def write_podspec_rule(f, rule, depth):
+ """Writes podspec from given rule."""
+ indent = " " * (depth + 1)
+ spec_var = get_spec_var(depth)
+ # Puts all files in hdrs, textual_hdrs, and srcs into source_files.
+ # Since CocoaPods treats header_files a bit differently from bazel,
+ # this won't generate a header_files field so that all source_files
+ # are considered as header files.
+ srcs = sorted(set(rule.hdrs + rule.textual_hdrs + rule.srcs))
+ write_indented_list(
+ f, "{indent}{var}.source_files = ".format(indent=indent, var=spec_var),
+ srcs)
+ # Writes dependencies of this rule.
+ for dep in sorted(rule.deps):
+ name = get_spec_name(dep.replace(":", "/"))
+ f.write("{indent}{var}.dependency '{dep}'\n".format(
+ indent=indent, var=spec_var, dep=name))
+
+
+def write_indented_list(f, leading, values):
+ """Writes leading values in an indented style."""
+ f.write(leading)
+ f.write((",\n" + " " * len(leading)).join("'{}'".format(v) for v in values))
+ f.write("\n")
+
+
+def generate(args):
+ """Generates a podspec file from all BUILD files under absl directory."""
+ rules = filter(relevant_rule, collect_rules("absl"))
+ with open(args.output, "wt") as f:
+ write_podspec(f, rules, vars(args))
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Generates abseil.podspec from BUILD.bazel")
+ parser.add_argument(
+ "-v", "--version", help="The version of podspec", required=True)
+ parser.add_argument(
+ "-t",
+ "--tag",
+ default=None,
+ help="The name of git tag (default: version)")
+ parser.add_argument(
+ "-o",
+ "--output",
+ default="abseil.podspec",
+ help="The name of output file (default: abseil.podspec)")
+ args = parser.parse_args()
+ if args.tag is None:
+ args.tag = args.version
+ generate(args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel
index c506f3b9..6a96420b 100644
--- a/absl/algorithm/BUILD.bazel
+++ b/absl/algorithm/BUILD.bazel
@@ -14,6 +14,7 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@@ -30,6 +31,7 @@ cc_library(
hdrs = ["algorithm.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = ["//absl/base:config"],
)
cc_test(
diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt
index 9fbe36f6..56cd0fb8 100644
--- a/absl/algorithm/CMakeLists.txt
+++ b/absl/algorithm/CMakeLists.txt
@@ -21,6 +21,8 @@ absl_cc_library(
"algorithm.h"
COPTS
${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
PUBLIC
)
diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h
index 7c2b787e..e9b47338 100644
--- a/absl/algorithm/algorithm.h
+++ b/absl/algorithm/algorithm.h
@@ -26,8 +26,10 @@
#include <iterator>
#include <type_traits>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace algorithm_internal {
@@ -85,6 +87,8 @@ It RotateImpl(It first, It middle, It last, std::false_type) {
} // namespace algorithm_internal
+// equal()
+//
// 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
@@ -105,8 +109,8 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
typename std::iterator_traits<InputIter2>::iterator_category{});
}
-// Performs comparison of two ranges specified by pairs of iterators using
-// operator==.
+// 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) {
@@ -114,6 +118,8 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,
algorithm_internal::EqualTo{});
}
+// linear_search()
+//
// Performs a linear search for `value` using the iterator `first` up to
// but not including `last`, returning true if [`first`, `last`) contains an
// element equal to `value`.
@@ -127,6 +133,8 @@ 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
@@ -136,7 +144,6 @@ bool linear_search(InputIterator first, InputIterator last,
// 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) {
@@ -146,7 +153,7 @@ ForwardIterator rotate(ForwardIterator first, ForwardIterator middle,
ForwardIterator>());
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_ALGORITHM_ALGORITHM_H_
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 16389be7..d72532de 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -55,7 +55,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_algorithm_internal {
// NOTE: it is important to defer to ADL lookup for building with C++ modules,
@@ -113,6 +113,18 @@ 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
@@ -257,7 +269,8 @@ container_algorithm_internal::ContainerIter<Sequence1> c_find_end(
// c_find_first_of()
//
// Container-based version of the <algorithm> `std::find_first_of()` function to
-// find the first elements in an ordered set within a container.
+// find the first element within the container that is also within the options
+// container.
template <typename C1, typename C2>
container_algorithm_internal::ContainerIter<C1> c_find_first_of(C1& container,
C2& options) {
@@ -366,7 +379,8 @@ c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) {
template <typename C1, typename C2>
bool c_equal(const C1& c1, const C2& c2) {
- return ((c1.size() == c2.size()) &&
+ 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)));
@@ -376,7 +390,8 @@ bool c_equal(const C1& c1, const C2& c2) {
// the function's test condition.
template <typename C1, typename C2, typename BinaryPredicate>
bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) {
- return ((c1.size() == c2.size()) &&
+ 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),
@@ -1706,7 +1721,7 @@ OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first,
output_first, std::forward<BinaryOp>(op));
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_ALGORITHM_CONTAINER_H_
diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc
index 86bf9d3e..0a4abe94 100644
--- a/absl/algorithm/container_test.cc
+++ b/absl/algorithm/container_test.cc
@@ -163,23 +163,29 @@ TEST_F(NonMutatingTest, MismatchWithPredicate) {
TEST_F(NonMutatingTest, Equal) {
EXPECT_TRUE(absl::c_equal(vector_, sequence_));
EXPECT_TRUE(absl::c_equal(sequence_, vector_));
+ EXPECT_TRUE(absl::c_equal(sequence_, array_));
+ EXPECT_TRUE(absl::c_equal(array_, vector_));
// Test that behavior appropriately differs from that of equal().
std::vector<int> vector_plus = {1, 2, 3};
vector_plus.push_back(4);
EXPECT_FALSE(absl::c_equal(vector_plus, sequence_));
EXPECT_FALSE(absl::c_equal(sequence_, vector_plus));
+ EXPECT_FALSE(absl::c_equal(array_, vector_plus));
}
TEST_F(NonMutatingTest, EqualWithPredicate) {
EXPECT_TRUE(absl::c_equal(vector_, sequence_, Equals));
EXPECT_TRUE(absl::c_equal(sequence_, vector_, Equals));
+ EXPECT_TRUE(absl::c_equal(array_, sequence_, Equals));
+ EXPECT_TRUE(absl::c_equal(vector_, array_, Equals));
// Test that behavior appropriately differs from that of equal().
std::vector<int> vector_plus = {1, 2, 3};
vector_plus.push_back(4);
EXPECT_FALSE(absl::c_equal(vector_plus, sequence_, Equals));
EXPECT_FALSE(absl::c_equal(sequence_, vector_plus, Equals));
+ EXPECT_FALSE(absl::c_equal(vector_plus, array_, Equals));
}
TEST_F(NonMutatingTest, IsPermutation) {
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index a512272a..bae79427 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -14,12 +14,11 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
"ABSL_DEFAULT_LINKOPTS",
- "ABSL_EXCEPTIONS_FLAG",
- "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
"ABSL_TEST_COPTS",
)
@@ -35,6 +34,21 @@ cc_library(
visibility = [
"//absl:__subpackages__",
],
+ deps = [
+ ":config",
+ ":core_headers",
+ ],
+)
+
+cc_library(
+ name = "errno_saver",
+ hdrs = ["internal/errno_saver.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = [
+ "//absl:__subpackages__",
+ ],
+ deps = [":config"],
)
cc_library(
@@ -43,16 +57,27 @@ cc_library(
hdrs = ["log_severity.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
- deps = [":core_headers"],
+ deps = [
+ ":config",
+ ":core_headers",
+ ],
)
cc_library(
name = "raw_logging_internal",
+ srcs = ["internal/raw_logging.cc"],
+ hdrs = ["internal/raw_logging.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [
"//absl:__subpackages__",
],
+ deps = [
+ ":atomic_hook",
+ ":config",
+ ":core_headers",
+ ":log_severity",
+ ],
)
cc_library(
@@ -64,22 +89,24 @@ cc_library(
"internal/spinlock_wait.cc",
"internal/spinlock_win32.inc",
],
- hdrs = [
- "internal/scheduling_mode.h",
- "internal/spinlock_wait.h",
- ],
+ hdrs = ["internal/spinlock_wait.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [
"//absl/base:__pkg__",
],
- deps = [":core_headers"],
+ deps = [
+ ":base_internal",
+ ":core_headers",
+ ":errno_saver",
+ ],
)
cc_library(
name = "config",
hdrs = [
"config.h",
+ "options.h",
"policy_checks.h",
],
copts = ABSL_DEFAULT_COPTS,
@@ -130,14 +157,15 @@ cc_library(
"//conditions:default": ["-pthread"],
}) + ABSL_DEFAULT_LINKOPTS,
visibility = [
- "//absl:__subpackages__",
+ "//visibility:public",
],
deps = [
":base",
+ ":base_internal",
":config",
":core_headers",
":dynamic_annotations",
- ":spinlock_wait",
+ ":raw_logging_internal",
],
)
@@ -156,6 +184,7 @@ cc_library(
"//absl:__subpackages__",
],
deps = [
+ ":config",
"//absl/meta:type_traits",
],
)
@@ -164,7 +193,6 @@ cc_library(
name = "base",
srcs = [
"internal/cycleclock.cc",
- "internal/raw_logging.cc",
"internal/spinlock.cc",
"internal/sysinfo.cc",
"internal/thread_identity.cc",
@@ -176,7 +204,6 @@ cc_library(
"internal/cycleclock.h",
"internal/low_level_scheduling.h",
"internal/per_thread_tls.h",
- "internal/raw_logging.h",
"internal/spinlock.h",
"internal/sysinfo.h",
"internal/thread_identity.h",
@@ -185,7 +212,9 @@ cc_library(
],
copts = ABSL_DEFAULT_COPTS,
linkopts = select({
- "//absl:windows": [],
+ "//absl:windows": [
+ "-DEFAULTLIB:advapi32.lib",
+ ],
"//conditions:default": ["-pthread"],
}) + ABSL_DEFAULT_LINKOPTS,
deps = [
@@ -195,11 +224,25 @@ cc_library(
":core_headers",
":dynamic_annotations",
":log_severity",
+ ":raw_logging_internal",
":spinlock_wait",
"//absl/meta:type_traits",
],
)
+cc_library(
+ name = "atomic_hook_test_helper",
+ testonly = 1,
+ srcs = ["internal/atomic_hook_test_helper.cc"],
+ hdrs = ["internal/atomic_hook_test_helper.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":atomic_hook",
+ ":core_headers",
+ ],
+)
+
cc_test(
name = "atomic_hook_test",
size = "small",
@@ -208,6 +251,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":atomic_hook",
+ ":atomic_hook_test_helper",
":core_headers",
"@com_google_googletest//:gtest_main",
],
@@ -232,28 +276,41 @@ cc_library(
name = "throw_delegate",
srcs = ["internal/throw_delegate.cc"],
hdrs = ["internal/throw_delegate.h"],
- copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = [
"//absl:__subpackages__",
],
deps = [
- ":base",
":config",
+ ":raw_logging_internal",
],
)
cc_test(
name = "throw_delegate_test",
srcs = ["throw_delegate_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ ":config",
":throw_delegate",
"@com_google_googletest//:gtest_main",
],
)
+cc_test(
+ name = "errno_saver_test",
+ size = "small",
+ srcs = ["internal/errno_saver_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":errno_saver",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
cc_library(
name = "exception_testing",
testonly = 1,
@@ -281,8 +338,8 @@ cc_library(
testonly = 1,
srcs = ["internal/exception_safety_testing.cc"],
hdrs = ["internal/exception_safety_testing.h"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
":pretty_function",
@@ -297,8 +354,8 @@ cc_library(
cc_test(
name = "exception_safety_testing_test",
srcs = ["exception_safety_testing_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":exception_safety_testing",
"//absl/memory",
@@ -347,8 +404,8 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":base",
+ ":base_internal",
":core_headers",
- ":spinlock_wait",
"//absl/synchronization",
"@com_google_googletest//:gtest",
],
@@ -363,8 +420,8 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":base",
+ ":base_internal",
":core_headers",
- ":spinlock_wait",
"//absl/synchronization",
"@com_google_googletest//:gtest_main",
],
@@ -382,6 +439,7 @@ cc_library(
deps = [
":base",
":base_internal",
+ ":raw_logging_internal",
"//absl/synchronization",
"@com_github_google_benchmark//:benchmark_main",
],
@@ -455,7 +513,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":base",
+ ":raw_logging_internal",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
@@ -519,7 +577,10 @@ cc_library(
visibility = [
"//absl:__subpackages__",
],
- deps = [":core_headers"],
+ deps = [
+ ":config",
+ ":core_headers",
+ ],
)
cc_test(
@@ -535,6 +596,75 @@ cc_test(
)
cc_library(
+ name = "exponential_biased",
+ srcs = ["internal/exponential_biased.cc"],
+ hdrs = ["internal/exponential_biased.h"],
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = [
+ "//absl:__subpackages__",
+ ],
+ deps = [
+ ":config",
+ ":core_headers",
+ ],
+)
+
+cc_test(
+ name = "exponential_biased_test",
+ size = "small",
+ srcs = ["internal/exponential_biased_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":exponential_biased",
+ "//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "periodic_sampler",
+ srcs = ["internal/periodic_sampler.cc"],
+ hdrs = ["internal/periodic_sampler.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":core_headers",
+ ":exponential_biased",
+ ],
+)
+
+cc_test(
+ name = "periodic_sampler_test",
+ size = "small",
+ srcs = ["internal/periodic_sampler_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":core_headers",
+ ":periodic_sampler",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_binary(
+ name = "periodic_sampler_benchmark",
+ testonly = 1,
+ srcs = ["internal/periodic_sampler_benchmark.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":core_headers",
+ ":periodic_sampler",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
+cc_library(
name = "scoped_set_env",
testonly = 1,
srcs = ["internal/scoped_set_env.cc"],
@@ -543,7 +673,10 @@ cc_library(
visibility = [
"//absl:__subpackages__",
],
- deps = [":base"],
+ deps = [
+ ":config",
+ ":raw_logging_internal",
+ ],
)
cc_test(
@@ -565,8 +698,10 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":base",
":log_severity",
+ "//absl/flags:flag_internal",
+ "//absl/flags:marshalling",
+ "//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt
index cc7960e3..14c52eab 100644
--- a/absl/base/CMakeLists.txt
+++ b/absl/base/CMakeLists.txt
@@ -14,11 +14,27 @@
# limitations under the License.
#
+find_library(LIBRT rt)
+
absl_cc_library(
NAME
atomic_hook
HDRS
"internal/atomic_hook.h"
+ DEPS
+ absl::config
+ absl::core_headers
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+)
+
+absl_cc_library(
+ NAME
+ errno_saver
+ HDRS
+ "internal/errno_saver.h"
+ DEPS
+ absl::config
COPTS
${ABSL_DEFAULT_COPTS}
)
@@ -39,6 +55,15 @@ absl_cc_library(
absl_cc_library(
NAME
raw_logging_internal
+ HDRS
+ "internal/raw_logging.h"
+ SRCS
+ "internal/raw_logging.cc"
+ DEPS
+ absl::atomic_hook
+ absl::config
+ absl::core_headers
+ absl::log_severity
COPTS
${ABSL_DEFAULT_COPTS}
)
@@ -47,7 +72,6 @@ absl_cc_library(
NAME
spinlock_wait
HDRS
- "internal/scheduling_mode.h"
"internal/spinlock_wait.h"
SRCS
"internal/spinlock_akaros.inc"
@@ -58,7 +82,9 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
+ absl::base_internal
absl::core_headers
+ absl::errno_saver
)
absl_cc_library(
@@ -66,6 +92,7 @@ absl_cc_library(
config
HDRS
"config.h"
+ "options.h"
"policy_checks.h"
COPTS
${ABSL_DEFAULT_COPTS}
@@ -116,10 +143,11 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::base
+ absl::base_internal
absl::config
absl::core_headers
absl::dynamic_annotations
- absl::spinlock_wait
+ absl::raw_logging_internal
Threads::Threads
)
@@ -131,9 +159,11 @@ absl_cc_library(
"internal/identity.h"
"internal/inline_variable.h"
"internal/invoke.h"
+ "internal/scheduling_mode.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
+ absl::config
absl::type_traits
)
@@ -146,23 +176,23 @@ absl_cc_library(
"internal/cycleclock.h"
"internal/low_level_scheduling.h"
"internal/per_thread_tls.h"
- "internal/raw_logging.h"
"internal/spinlock.h"
"internal/sysinfo.h"
"internal/thread_identity.h"
"internal/tsan_mutex_interface.h"
"internal/unscaledcycleclock.h"
- "log_severity.h"
SRCS
"internal/cycleclock.cc"
- "internal/raw_logging.cc"
"internal/spinlock.cc"
"internal/sysinfo.cc"
"internal/thread_identity.cc"
"internal/unscaledcycleclock.cc"
- "log_severity.cc"
COPTS
${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ $<$<BOOL:${LIBRT}>:${LIBRT}>
+ $<$<BOOL:${MINGW}>:"advapi32">
DEPS
absl::atomic_hook
absl::base_internal
@@ -170,6 +200,7 @@ absl_cc_library(
absl::core_headers
absl::dynamic_annotations
absl::log_severity
+ absl::raw_logging_internal
absl::spinlock_wait
absl::type_traits
Threads::Threads
@@ -185,9 +216,9 @@ absl_cc_library(
"internal/throw_delegate.cc"
COPTS
${ABSL_DEFAULT_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
DEPS
- absl::base
+ absl::config
+ absl::raw_logging_internal
)
absl_cc_library(
@@ -221,9 +252,6 @@ absl_cc_library(
"internal/exception_safety_testing.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::config
absl::pretty_function
@@ -242,15 +270,25 @@ absl_cc_test(
"exception_safety_testing_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::exception_safety_testing
absl::memory
gtest_main
)
+absl_cc_library(
+ NAME
+ atomic_hook_test_helper
+ SRCS
+ "internal/atomic_hook_test_helper.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::atomic_hook
+ absl::core_headers
+ TESTONLY
+)
+
absl_cc_test(
NAME
atomic_hook_test
@@ -259,8 +297,10 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
+ absl::atomic_hook_test_helper
absl::atomic_hook
absl::core_headers
+ gmock
gtest_main
)
@@ -279,6 +319,19 @@ absl_cc_test(
absl_cc_test(
NAME
+ errno_saver_test
+ SRCS
+ "internal/errno_saver_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::errno_saver
+ gmock
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
throw_delegate_test
SRCS
"throw_delegate_test.cc"
@@ -286,6 +339,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::base
+ absl::config
absl::throw_delegate
gtest_main
)
@@ -329,8 +383,8 @@ absl_cc_library(
${ABSL_TEST_COPTS}
DEPS
absl::base
+ absl::base_internal
absl::core_headers
- absl::spinlock_wait
absl::synchronization
gtest
TESTONLY
@@ -346,8 +400,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::base
+ absl::base_internal
absl::core_headers
- absl::spinlock_wait
absl::synchronization
gtest_main
)
@@ -415,7 +469,7 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::base
+ absl::raw_logging_internal
absl::strings
gtest_main
)
@@ -468,6 +522,7 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
+ absl::config
absl::core_headers
)
@@ -485,6 +540,60 @@ absl_cc_test(
absl_cc_library(
NAME
+ exponential_biased
+ SRCS
+ "internal/exponential_biased.cc"
+ HDRS
+ "internal/exponential_biased.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ absl::core_headers
+)
+
+absl_cc_test(
+ NAME
+ exponential_biased_test
+ SRCS
+ "internal/exponential_biased_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::exponential_biased
+ absl::strings
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ periodic_sampler
+ SRCS
+ "internal/periodic_sampler.cc"
+ HDRS
+ "internal/periodic_sampler.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+ absl::exponential_biased
+)
+
+absl_cc_test(
+ NAME
+ periodic_sampler_test
+ SRCS
+ "internal/periodic_sampler_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::core_headers
+ absl::periodic_sampler
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
scoped_set_env
SRCS
"internal/scoped_set_env.cc"
@@ -493,7 +602,8 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
- absl::base
+ absl::config
+ absl::raw_logging_internal
)
absl_cc_test(
@@ -525,8 +635,10 @@ absl_cc_test(
SRCS
"log_severity_test.cc"
DEPS
- absl::base
+ absl::flags_internal
+ absl::flags_marshalling
absl::log_severity
+ absl::strings
gmock
gtest_main
)
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index a7da62af..ff138629 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -157,10 +157,12 @@
// Tags a function as weak for the purposes of compilation and linking.
// Weak attributes currently do not work properly in LLVM's Windows backend,
// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598
-// for futher information.
-#if (ABSL_HAVE_ATTRIBUTE(weak) || \
+// for further information.
+// The MinGW compiler doesn't complain about the weak attribute until the link
+// step, presumably because Windows doesn't use ELF binaries.
+#if (ABSL_HAVE_ATTRIBUTE(weak) || \
(defined(__GNUC__) && !defined(__clang__))) && \
- !(defined(__llvm__) && defined(_WIN32))
+ !(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__)
#undef ABSL_ATTRIBUTE_WEAK
#define ABSL_ATTRIBUTE_WEAK __attribute__((weak))
#define ABSL_HAVE_ATTRIBUTE_WEAK 1
@@ -561,7 +563,19 @@
// ABSL_ATTRIBUTE_PACKED
//
-// Prevents the compiler from padding a structure to natural alignment
+// Instructs the compiler not to use natural alignment for a tagged data
+// structure, but instead to reduce its alignment to 1. This attribute can
+// either be applied to members of a structure or to a structure in its
+// entirety. Applying this attribute (judiciously) to a structure in its
+// entirety to optimize the memory footprint of very commonly-used structs is
+// fine. Do not apply this attribute to a structure in its entirety if the
+// purpose is to control the offsets of the members in the structure. Instead,
+// apply this attribute only to structure members that need it.
+//
+// When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the
+// natural alignment of structure members not annotated is preserved. Aligned
+// member accesses are faster than non-aligned member accesses even if the
+// targeted microprocessor supports non-aligned accesses.
#if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__))
#define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__))
#else
@@ -599,7 +613,6 @@
//
// Note that this attribute is redundant if the variable is declared constexpr.
#if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization)
-// NOLINTNEXTLINE(whitespace/braces)
#define ABSL_CONST_INIT [[clang::require_constant_initialization]]
#else
#define ABSL_CONST_INIT
diff --git a/absl/base/bit_cast_test.cc b/absl/base/bit_cast_test.cc
index be201856..8a3a41ea 100644
--- a/absl/base/bit_cast_test.cc
+++ b/absl/base/bit_cast_test.cc
@@ -22,7 +22,7 @@
#include "absl/base/macros.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
template <int N>
@@ -105,5 +105,5 @@ TEST(BitCast, Double) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 373f015f..bc5ec937 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -41,7 +41,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
class once_flag;
@@ -143,12 +143,13 @@ 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) {
#ifndef NDEBUG
{
- uint32_t old_control = control->load(std::memory_order_acquire);
+ uint32_t old_control = control->load(std::memory_order_relaxed);
if (old_control != kOnceInit &&
old_control != kOnceRunning &&
old_control != kOnceWaiter &&
@@ -166,14 +167,23 @@ void CallOnceImpl(std::atomic<uint32_t>* control,
// Must do this before potentially modifying control word's state.
base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode);
// Short circuit the simplest case to avoid procedure call overhead.
+ // The base_internal::SpinLockWait() call returns either kOnceInit or
+ // kOnceDone. If it returns kOnceDone, it must have loaded the control word
+ // with std::memory_order_acquire and seen a value of kOnceDone.
uint32_t old_control = kOnceInit;
if (control->compare_exchange_strong(old_control, kOnceRunning,
- std::memory_order_acquire,
std::memory_order_relaxed) ||
base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans,
scheduling_mode) == kOnceInit) {
base_internal::Invoke(std::forward<Callable>(fn),
std::forward<Args>(args)...);
+ // The call to SpinLockWake below is an optimization, because the waiter
+ // in SpinLockWait is waiting with a short timeout. The atomic load/store
+ // sequence is slightly faster than an atomic exchange:
+ // old_control = control->exchange(base_internal::kOnceDone,
+ // std::memory_order_release);
+ // We opt for a slightly faster case when there are no waiters, in spite
+ // of longer tail latency when there are waiters.
old_control = control->load(std::memory_order_relaxed);
control->store(base_internal::kOnceDone, std::memory_order_release);
if (old_control == base_internal::kOnceWaiter) {
@@ -210,7 +220,7 @@ void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) {
}
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_CALL_ONCE_H_
diff --git a/absl/base/call_once_test.cc b/absl/base/call_once_test.cc
index 0e89bd24..11d26c44 100644
--- a/absl/base/call_once_test.cc
+++ b/absl/base/call_once_test.cc
@@ -24,18 +24,18 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
absl::once_flag once;
ABSL_CONST_INIT Mutex counters_mu(absl::kConstInit);
-int running_thread_count GUARDED_BY(counters_mu) = 0;
-int call_once_invoke_count GUARDED_BY(counters_mu) = 0;
-int call_once_finished_count GUARDED_BY(counters_mu) = 0;
-int call_once_return_count GUARDED_BY(counters_mu) = 0;
-bool done_blocking GUARDED_BY(counters_mu) = false;
+int running_thread_count ABSL_GUARDED_BY(counters_mu) = 0;
+int call_once_invoke_count ABSL_GUARDED_BY(counters_mu) = 0;
+int call_once_finished_count ABSL_GUARDED_BY(counters_mu) = 0;
+int call_once_return_count ABSL_GUARDED_BY(counters_mu) = 0;
+bool done_blocking ABSL_GUARDED_BY(counters_mu) = false;
// Function to be called from absl::call_once. Waits for a notification.
void WaitAndIncrement() {
@@ -61,7 +61,7 @@ void ThreadBody() {
}
// Returns true if all threads are set up for the test.
-bool ThreadsAreSetup(void*) EXCLUSIVE_LOCKS_REQUIRED(counters_mu) {
+bool ThreadsAreSetup(void*) ABSL_EXCLUSIVE_LOCKS_REQUIRED(counters_mu) {
// All ten threads must be running, and WaitAndIncrement should be blocked.
return running_thread_count == 10 && call_once_invoke_count == 1;
}
@@ -103,5 +103,5 @@ TEST(CallOnceTest, ExecutionCount) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/casts.h b/absl/base/casts.h
index b67b047c..322cc1d2 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -34,7 +34,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace internal_casts {
@@ -178,7 +178,7 @@ inline Dest bit_cast(const Source& source) {
return dest;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_CASTS_H_
diff --git a/absl/base/config.h b/absl/base/config.h
index 1c3cb08e..ee99f946 100644
--- a/absl/base/config.h
+++ b/absl/base/config.h
@@ -63,8 +63,72 @@
#include <TargetConditionals.h>
#endif
+#include "absl/base/options.h"
#include "absl/base/policy_checks.h"
+// Helper macro to convert a CPP variable to a string literal.
+#define ABSL_INTERNAL_DO_TOKEN_STR(x) #x
+#define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x)
+
+// -----------------------------------------------------------------------------
+// Abseil namespace annotations
+// -----------------------------------------------------------------------------
+
+// ABSL_NAMESPACE_BEGIN/ABSL_NAMESPACE_END
+//
+// An annotation placed at the beginning/end of each `namespace absl` scope.
+// This is used to inject an inline namespace.
+//
+// The proper way to write Abseil code in the `absl` namespace is:
+//
+// namespace absl {
+// ABSL_NAMESPACE_BEGIN
+//
+// void Foo(); // absl::Foo().
+//
+// ABSL_NAMESPACE_END
+// } // namespace absl
+//
+// Users of Abseil should not use these macros, because users of Abseil should
+// not write `namespace absl {` in their own code for any reason. (Abseil does
+// not support forward declarations of its own types, nor does it support
+// user-provided specialization of Abseil templates. Code that violates these
+// rules may be broken without warning.)
+#if !defined(ABSL_OPTION_USE_INLINE_NAMESPACE) || \
+ !defined(ABSL_OPTION_INLINE_NAMESPACE_NAME)
+#error options.h is misconfigured.
+#endif
+
+// Check that ABSL_OPTION_INLINE_NAMESPACE_NAME is neither "head" nor ""
+#if defined(__cplusplus) && ABSL_OPTION_USE_INLINE_NAMESPACE == 1
+
+#define ABSL_INTERNAL_INLINE_NAMESPACE_STR \
+ ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME)
+
+static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != '\0',
+ "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must "
+ "not be empty.");
+static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
+ ABSL_INTERNAL_INLINE_NAMESPACE_STR[1] != 'e' ||
+ ABSL_INTERNAL_INLINE_NAMESPACE_STR[2] != 'a' ||
+ ABSL_INTERNAL_INLINE_NAMESPACE_STR[3] != 'd' ||
+ ABSL_INTERNAL_INLINE_NAMESPACE_STR[4] != '\0',
+ "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must "
+ "be changed to a new, unique identifier name.");
+
+#endif
+
+#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0
+#define ABSL_NAMESPACE_BEGIN
+#define ABSL_NAMESPACE_END
+#elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1
+#define ABSL_NAMESPACE_BEGIN \
+ inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME {
+#define ABSL_NAMESPACE_END }
+#else
+#error options.h is misconfigured.
+#endif
+
// -----------------------------------------------------------------------------
// Compiler Feature Checks
// -----------------------------------------------------------------------------
@@ -84,6 +148,12 @@
#define ABSL_HAVE_BUILTIN(x) 0
#endif
+#if defined(__is_identifier)
+#define ABSL_INTERNAL_HAS_KEYWORD(x) !(__is_identifier(x))
+#else
+#define ABSL_INTERNAL_HAS_KEYWORD(x) 0
+#endif
+
// ABSL_HAVE_TLS is defined to 1 when __thread should be supported.
// We assume __thread is supported on Linux when compiled with Clang or compiled
// against libstdc++ with _GLIBCXX_HAVE_TLS defined.
@@ -125,13 +195,24 @@
#error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \
(!defined(__clang__) && defined(__GNUC__) && \
- (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \
+ (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4)) && \
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
(defined(_MSC_VER) && !defined(__NVCC__))
#define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
#define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
#endif
+// ABSL_HAVE_SOURCE_LOCATION_CURRENT
+//
+// Indicates whether `absl::SourceLocation::current()` will return useful
+// information in some contexts.
+#ifndef ABSL_HAVE_SOURCE_LOCATION_CURRENT
+#if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \
+ ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE)
+#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1
+#endif
+#endif
+
// ABSL_HAVE_THREAD_LOCAL
//
// Checks whether C++11's `thread_local` storage duration specifier is
@@ -235,13 +316,19 @@
#error ABSL_HAVE_EXCEPTIONS cannot be directly set.
#elif defined(__clang__)
-// TODO(calabrese)
-// Switch to using __cpp_exceptions when we no longer support versions < 3.6.
-// For details on this check, see:
-// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro
+
+#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6)
+// Clang >= 3.6
+#if __has_feature(cxx_exceptions)
+#define ABSL_HAVE_EXCEPTIONS 1
+#endif // __has_feature(cxx_exceptions)
+#else
+// Clang < 3.6
+// http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro
#if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
#define ABSL_HAVE_EXCEPTIONS 1
#endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
+#endif // __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6)
// Handle remaining special cases and default to exceptions being supported.
#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \
@@ -307,7 +394,7 @@
// ABSL_HAVE_SEMAPHORE_H
//
-// Checks whether the platform supports the <semaphore.h> header and sem_open(3)
+// Checks whether the platform supports the <semaphore.h> header and sem_init(3)
// family of functions as standardized in POSIX.1-2001.
//
// Note: While Apple provides <semaphore.h> for both iOS and macOS, it is
@@ -334,8 +421,15 @@
#define ABSL_HAVE_ALARM 1
#elif defined(_MSC_VER)
// feature tests for Microsoft's library
+#elif defined(__MINGW32__)
+// mingw32 doesn't provide alarm(2):
+// https://osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.2-trunk/mingwrt/include/unistd.h
+// mingw-w64 provides a no-op implementation:
+// 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(__Fuchsia__)
+// Signals don't exist on fuchsia.
#elif defined(__native_client__)
#else
// other standard libraries
@@ -461,6 +555,68 @@
#define ABSL_HAVE_STD_STRING_VIEW 1
#endif
+// ABSL_USES_STD_ANY
+//
+// Indicates whether absl::any is an alias for std::any.
+#if !defined(ABSL_OPTION_USE_STD_ANY)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_ANY == 0 || \
+ (ABSL_OPTION_USE_STD_ANY == 2 && !defined(ABSL_HAVE_STD_ANY))
+#undef ABSL_USES_STD_ANY
+#elif ABSL_OPTION_USE_STD_ANY == 1 || \
+ (ABSL_OPTION_USE_STD_ANY == 2 && defined(ABSL_HAVE_STD_ANY))
+#define ABSL_USES_STD_ANY 1
+#else
+#error options.h is misconfigured.
+#endif
+
+// ABSL_USES_STD_OPTIONAL
+//
+// Indicates whether absl::optional is an alias for std::optional.
+#if !defined(ABSL_OPTION_USE_STD_OPTIONAL)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_OPTIONAL == 0 || \
+ (ABSL_OPTION_USE_STD_OPTIONAL == 2 && !defined(ABSL_HAVE_STD_OPTIONAL))
+#undef ABSL_USES_STD_OPTIONAL
+#elif ABSL_OPTION_USE_STD_OPTIONAL == 1 || \
+ (ABSL_OPTION_USE_STD_OPTIONAL == 2 && defined(ABSL_HAVE_STD_OPTIONAL))
+#define ABSL_USES_STD_OPTIONAL 1
+#else
+#error options.h is misconfigured.
+#endif
+
+// ABSL_USES_STD_VARIANT
+//
+// Indicates whether absl::variant is an alias for std::variant.
+#if !defined(ABSL_OPTION_USE_STD_VARIANT)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_VARIANT == 0 || \
+ (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT))
+#undef ABSL_USES_STD_VARIANT
+#elif ABSL_OPTION_USE_STD_VARIANT == 1 || \
+ (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT))
+#define ABSL_USES_STD_VARIANT 1
+#else
+#error options.h is misconfigured.
+#endif
+
+// ABSL_USES_STD_STRING_VIEW
+//
+// Indicates whether absl::string_view is an alias for std::string_view.
+#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW)
+#error options.h is misconfigured.
+#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \
+ (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \
+ !defined(ABSL_HAVE_STD_STRING_VIEW))
+#undef ABSL_USES_STD_STRING_VIEW
+#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \
+ (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \
+ defined(ABSL_HAVE_STD_STRING_VIEW))
+#define ABSL_USES_STD_STRING_VIEW 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
@@ -469,4 +625,47 @@
#define ABSL_INTERNAL_MSVC_2017_DBG_MODE
#endif
+// ABSL_INTERNAL_MANGLED_NS
+// ABSL_INTERNAL_MANGLED_BACKREFERENCE
+//
+// Internal macros for building up mangled names in our internal fork of CCTZ.
+// This implementation detail is only needed and provided for the MSVC build.
+//
+// These macros both expand to string literals. ABSL_INTERNAL_MANGLED_NS is
+// the mangled spelling of the `absl` namespace, and
+// ABSL_INTERNAL_MANGLED_BACKREFERENCE is a back-reference integer representing
+// the proper count to skip past the CCTZ fork namespace names. (This number
+// is one larger when there is an inline namespace name to skip.)
+#if defined(_MSC_VER)
+#if ABSL_OPTION_USE_INLINE_NAMESPACE == 0
+#define ABSL_INTERNAL_MANGLED_NS "absl"
+#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "5"
+#else
+#define ABSL_INTERNAL_MANGLED_NS \
+ ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) "@absl"
+#define ABSL_INTERNAL_MANGLED_BACKREFERENCE "6"
+#endif
+#endif
+
+#undef ABSL_INTERNAL_HAS_KEYWORD
+
+// ABSL_DLL
+//
+// When building Abseil as a DLL, this macro expands to `__declspec(dllexport)`
+// so we can annotate symbols appropriately as being exported. When used in
+// headers consuming a DLL, this macro expands to `__declspec(dllimport)` so
+// that consumers know the symbol is defined inside the DLL. In all other cases,
+// the macro expands to nothing.
+#if defined(_MSC_VER)
+#if defined(ABSL_BUILD_DLL)
+#define ABSL_DLL __declspec(dllexport)
+#elif defined(ABSL_CONSUME_DLL)
+#define ABSL_DLL __declspec(dllimport)
+#else
+#define ABSL_DLL
+#endif
+#else
+#define ABSL_DLL
+#endif // defined(_MSC_VER)
+
#endif // ABSL_BASE_CONFIG_H_
diff --git a/absl/base/const_init.h b/absl/base/const_init.h
index 30d6a3ca..16520b61 100644
--- a/absl/base/const_init.h
+++ b/absl/base/const_init.h
@@ -22,6 +22,8 @@
#ifndef ABSL_BASE_CONST_INIT_H_
#define ABSL_BASE_CONST_INIT_H_
+#include "absl/base/config.h"
+
// In general, objects with static storage duration (such as global variables)
// can trigger tricky object lifetime situations. Attempting to access them
// from the constructors or destructors of other global objects can result in
@@ -62,13 +64,13 @@
// or thread_local storage duration.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
enum ConstInitType {
kConstInit,
};
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_CONST_INIT_H_
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc
index 2ed38606..a59be29e 100644
--- a/absl/base/exception_safety_testing_test.cc
+++ b/absl/base/exception_safety_testing_test.cc
@@ -14,6 +14,8 @@
#include "absl/base/internal/exception_safety_testing.h"
+#ifdef ABSL_HAVE_EXCEPTIONS
+
#include <cstddef>
#include <exception>
#include <iostream>
@@ -326,17 +328,15 @@ TEST(ThrowingValueTest, NonThrowingDelete) {
UnsetCountdown();
}
-using Storage =
- absl::aligned_storage_t<sizeof(ThrowingValue<>), alignof(ThrowingValue<>)>;
-
TEST(ThrowingValueTest, NonThrowingPlacementDelete) {
constexpr int kArrayLen = 2;
// We intentionally create extra space to store the tag allocated by placement
// new[].
constexpr int kStorageLen = 4;
- Storage buf;
- Storage array_buf[kStorageLen];
+ alignas(ThrowingValue<>) unsigned char buf[sizeof(ThrowingValue<>)];
+ alignas(ThrowingValue<>) unsigned char
+ array_buf[sizeof(ThrowingValue<>[kStorageLen])];
auto* placed = new (&buf) ThrowingValue<>(1);
auto placed_array = new (&array_buf) ThrowingValue<>[kArrayLen];
@@ -900,12 +900,12 @@ TEST(ConstructorTrackerTest, CreatedAfter) {
}
TEST(ConstructorTrackerTest, NotDestroyedAfter) {
- absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
+ alignas(Tracked) unsigned char storage[sizeof(Tracked)];
EXPECT_NONFATAL_FAILURE(
{
exceptions_internal::ConstructorTracker ct(
exceptions_internal::countdown);
- new (&storage) Tracked;
+ new (&storage) Tracked();
},
"not destroyed");
}
@@ -922,11 +922,11 @@ TEST(ConstructorTrackerTest, DestroyedTwice) {
TEST(ConstructorTrackerTest, ConstructedTwice) {
exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
- absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
+ alignas(Tracked) unsigned char storage[sizeof(Tracked)];
EXPECT_NONFATAL_FAILURE(
{
- new (&storage) Tracked;
- new (&storage) Tracked;
+ new (&storage) Tracked();
+ new (&storage) Tracked();
reinterpret_cast<Tracked*>(&storage)->~Tracked();
},
"re-constructed");
@@ -952,3 +952,5 @@ TEST(ThrowingAllocatorTraitsTest, Assignablility) {
} // namespace
} // namespace testing
+
+#endif // ABSL_HAVE_EXCEPTIONS
diff --git a/absl/base/inline_variable_test.cc b/absl/base/inline_variable_test.cc
index 40e8b57b..37a40e1e 100644
--- a/absl/base/inline_variable_test.cc
+++ b/absl/base/inline_variable_test.cc
@@ -20,7 +20,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace inline_variable_testing_internal {
namespace {
@@ -60,5 +60,5 @@ TEST(InlineVariableTest, FunPtrType) {
} // namespace
} // namespace inline_variable_testing_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/inline_variable_test_a.cc b/absl/base/inline_variable_test_a.cc
index c19a6e55..f96a58d9 100644
--- a/absl/base/inline_variable_test_a.cc
+++ b/absl/base/inline_variable_test_a.cc
@@ -15,7 +15,7 @@
#include "absl/base/internal/inline_variable_testing.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace inline_variable_testing_internal {
const Foo& get_foo_a() { return inline_variable_foo; }
@@ -23,5 +23,5 @@ const Foo& get_foo_a() { return inline_variable_foo; }
const int& get_int_a() { return inline_variable_int; }
} // namespace inline_variable_testing_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/inline_variable_test_b.cc b/absl/base/inline_variable_test_b.cc
index ae8da104..038adc30 100644
--- a/absl/base/inline_variable_test_b.cc
+++ b/absl/base/inline_variable_test_b.cc
@@ -15,7 +15,7 @@
#include "absl/base/internal/inline_variable_testing.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace inline_variable_testing_internal {
const Foo& get_foo_b() { return inline_variable_foo; }
@@ -23,5 +23,5 @@ const Foo& get_foo_b() { return inline_variable_foo; }
const int& get_int_b() { return inline_variable_int; }
} // namespace inline_variable_testing_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h
index 6757e75b..ae21cd7f 100644
--- a/absl/base/internal/atomic_hook.h
+++ b/absl/base/internal/atomic_hook.h
@@ -11,7 +11,6 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-//
#ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
#define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
@@ -21,29 +20,51 @@
#include <cstdint>
#include <utility>
-#ifdef _MSC_FULL_VER
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+
+#if defined(_MSC_VER) && !defined(__clang__)
+#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0
+#else
+#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1
+#endif
+
+#if defined(_MSC_VER)
#define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
#else
#define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
template <typename T>
class AtomicHook;
-// AtomicHook is a helper class, templatized on a raw function pointer type, for
-// implementing Abseil customization hooks. It is a callable object that
-// dispatches to the registered hook.
+// To workaround AtomicHook not being constant-initializable on some platforms,
+// prefer to annotate instances with `ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES`
+// instead of `ABSL_CONST_INIT`.
+#if ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
+#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_CONST_INIT
+#else
+#define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
+#endif
+
+// `AtomicHook` is a helper class, templatized on a raw function pointer type,
+// for implementing Abseil customization hooks. It is a callable object that
+// dispatches to the registered hook. Objects of type `AtomicHook` must have
+// static or thread storage duration.
//
// A default constructed object performs a no-op (and returns a default
// constructed object) if no hook has been registered.
//
-// Hooks can be pre-registered via constant initialization, for example,
-// ABSL_CONST_INIT static AtomicHook<void(*)()> my_hook(DefaultAction);
-// and then changed at runtime via a call to Store().
+// Hooks can be pre-registered via constant initialization, for example:
+//
+// ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static AtomicHook<void(*)()>
+// my_hook(DefaultAction);
+//
+// and then changed at runtime via a call to `Store()`.
//
// Reads and writes guarantee memory_order_acquire/memory_order_release
// semantics.
@@ -58,12 +79,23 @@ class AtomicHook<ReturnType (*)(Args...)> {
// Constructs an object that by default dispatches to/returns the
// pre-registered default_fn when no hook has been registered at runtime.
-#if ABSL_HAVE_WORKING_ATOMIC_POINTER
+#if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
explicit constexpr AtomicHook(FnPtr default_fn)
: hook_(default_fn), default_fn_(default_fn) {}
-#else
+#elif ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
explicit constexpr AtomicHook(FnPtr default_fn)
: hook_(kUninitialized), default_fn_(default_fn) {}
+#else
+ // As of January 2020, on all known versions of MSVC this constructor runs in
+ // the global constructor sequence. If `Store()` is called by a dynamic
+ // initializer, we want to preserve the value, even if this constructor runs
+ // after the call to `Store()`. If not, `hook_` will be
+ // zero-initialized by the linker and we have no need to set it.
+ // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html
+ explicit constexpr AtomicHook(FnPtr default_fn)
+ : /* hook_(deliberately omitted), */ default_fn_(default_fn) {
+ static_assert(kUninitialized == 0, "here we rely on zero-initialization");
+ }
#endif
// Stores the provided function pointer as the value for this hook.
@@ -159,9 +191,10 @@ class AtomicHook<ReturnType (*)(Args...)> {
};
#undef ABSL_HAVE_WORKING_ATOMIC_POINTER
+#undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
diff --git a/absl/base/internal/atomic_hook_test.cc b/absl/base/internal/atomic_hook_test.cc
index ecc80406..e577a8fd 100644
--- a/absl/base/internal/atomic_hook_test.cc
+++ b/absl/base/internal/atomic_hook_test.cc
@@ -14,16 +14,22 @@
#include "absl/base/internal/atomic_hook.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/attributes.h"
+#include "absl/base/internal/atomic_hook_test_helper.h"
namespace {
+using ::testing::Eq;
+
int value = 0;
void TestHook(int x) { value = x; }
TEST(AtomicHookTest, NoDefaultFunction) {
- ABSL_CONST_INIT static absl::base_internal::AtomicHook<void(*)(int)> hook;
+ ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
+ void (*)(int)>
+ hook;
value = 0;
// Test the default DummyFunction.
@@ -49,8 +55,9 @@ TEST(AtomicHookTest, NoDefaultFunction) {
TEST(AtomicHookTest, WithDefaultFunction) {
// Set the default value to TestHook at compile-time.
- ABSL_CONST_INIT static absl::base_internal::AtomicHook<void (*)(int)> hook(
- TestHook);
+ ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
+ void (*)(int)>
+ hook(TestHook);
value = 0;
// Test the default value is TestHook.
@@ -67,4 +74,24 @@ TEST(AtomicHookTest, WithDefaultFunction) {
EXPECT_EQ(value, 2);
}
+ABSL_CONST_INIT int override_func_calls = 0;
+void OverrideFunc() { override_func_calls++; }
+static struct OverrideInstaller {
+ OverrideInstaller() { absl::atomic_hook_internal::func.Store(OverrideFunc); }
+} override_installer;
+
+TEST(AtomicHookTest, DynamicInitFromAnotherTU) {
+ // MSVC 14.2 doesn't do constexpr static init correctly; in particular it
+ // tends to sequence static init (i.e. defaults) of `AtomicHook` objects
+ // after their dynamic init (i.e. overrides), overwriting whatever value was
+ // written during dynamic init. This regression test validates the fix.
+ // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html
+ EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0));
+ EXPECT_THAT(override_func_calls, Eq(0));
+ absl::atomic_hook_internal::func();
+ EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0));
+ EXPECT_THAT(override_func_calls, Eq(1));
+ EXPECT_THAT(absl::atomic_hook_internal::func.Load(), Eq(OverrideFunc));
+}
+
} // namespace
diff --git a/absl/random/internal/seed_salting_sequence_generator_empty_sequence.cc b/absl/base/internal/atomic_hook_test_helper.cc
index 8797e2e7..537d47cd 100644
--- a/absl/random/internal/seed_salting_sequence_generator_empty_sequence.cc
+++ b/absl/base/internal/atomic_hook_test_helper.cc
@@ -12,19 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <iostream>
-#include <random>
+#include "absl/base/internal/atomic_hook_test_helper.h"
-#include "absl/random/random.h"
+#include "absl/base/attributes.h"
+#include "absl/base/internal/atomic_hook.h"
-// This program is used in integration tests.
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace atomic_hook_internal {
-int main() {
- std::seed_seq seed_seq{};
- absl::BitGen rng(seed_seq);
- constexpr size_t kSequenceLength = 8;
- for (size_t i = 0; i < kSequenceLength; i++) {
- std::cout << rng() << "\n";
- }
- return 0;
-}
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<VoidF>
+ func(DefaultFunc);
+ABSL_CONST_INIT int default_func_calls = 0;
+void DefaultFunc() { default_func_calls++; }
+void RegisterFunc(VoidF f) { func.Store(f); }
+
+} // namespace atomic_hook_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/random/internal/seed_salting_sequence_generator.cc b/absl/base/internal/atomic_hook_test_helper.h
index 31fdcfe1..3e72b497 100644
--- a/absl/random/internal/seed_salting_sequence_generator.cc
+++ b/absl/base/internal/atomic_hook_test_helper.h
@@ -12,19 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <iostream>
-#include <random>
+#ifndef ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_
+#define ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_
-#include "absl/random/random.h"
+#include "absl/base/internal/atomic_hook.h"
-// This program is used in integration tests.
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace atomic_hook_internal {
-int main() {
- std::seed_seq seed_seq{1234};
- absl::BitGen rng(seed_seq);
- constexpr size_t kSequenceLength = 8;
- for (size_t i = 0; i < kSequenceLength; i++) {
- std::cout << rng() << "\n";
- }
- return 0;
-}
+using VoidF = void (*)();
+extern absl::base_internal::AtomicHook<VoidF> func;
+extern int default_func_calls;
+void DefaultFunc();
+void RegisterFunc(VoidF func);
+
+} // namespace atomic_hook_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_
diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h
index ef978e9b..8b03453c 100644
--- a/absl/base/internal/bits.h
+++ b/absl/base/internal/bits.h
@@ -20,6 +20,8 @@
#include <cstdint>
+#include "absl/base/config.h"
+
// Clang on Windows has __builtin_clzll; otherwise we need to use the
// windows intrinsic functions.
#if defined(_MSC_VER)
@@ -46,15 +48,27 @@
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) {
int zeroes = 60;
- if (n >> 32) zeroes -= 32, n >>= 32;
- if (n >> 16) zeroes -= 16, n >>= 16;
- if (n >> 8) zeroes -= 8, n >>= 8;
- if (n >> 4) zeroes -= 4, n >>= 4;
+ if (n >> 32) {
+ zeroes -= 32;
+ n >>= 32;
+ }
+ if (n >> 16) {
+ zeroes -= 16;
+ n >>= 16;
+ }
+ if (n >> 8) {
+ zeroes -= 8;
+ n >>= 8;
+ }
+ if (n >> 4) {
+ zeroes -= 4;
+ n >>= 4;
+ }
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
}
@@ -96,9 +110,18 @@ ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) {
ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) {
int zeroes = 28;
- if (n >> 16) zeroes -= 16, n >>= 16;
- if (n >> 8) zeroes -= 8, n >>= 8;
- if (n >> 4) zeroes -= 4, n >>= 4;
+ if (n >> 16) {
+ zeroes -= 16;
+ n >>= 16;
+ }
+ if (n >> 8) {
+ zeroes -= 8;
+ n >>= 8;
+ }
+ if (n >> 4) {
+ zeroes -= 4;
+ n >>= 4;
+ }
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
}
@@ -189,7 +212,7 @@ ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) {
#undef ABSL_BASE_INTERNAL_FORCEINLINE
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_BITS_H_
diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc
index 9868e549..0e65005b 100644
--- a/absl/base/internal/cycleclock.cc
+++ b/absl/base/internal/cycleclock.cc
@@ -28,7 +28,7 @@
#include "absl/base/internal/unscaledcycleclock.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
#if ABSL_USE_UNSCALED_CYCLECLOCK
@@ -103,5 +103,5 @@ double CycleClock::Frequency() {
#endif
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/cycleclock.h b/absl/base/internal/cycleclock.h
index 39bce06a..a18b5844 100644
--- a/absl/base/internal/cycleclock.h
+++ b/absl/base/internal/cycleclock.h
@@ -44,8 +44,10 @@
#include <cstdint>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// -----------------------------------------------------------------------------
@@ -86,7 +88,7 @@ class CycleClockSource {
};
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_CYCLECLOCK_H_
diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h
index 2e5e422c..5618867b 100644
--- a/absl/base/internal/direct_mmap.h
+++ b/absl/base/internal/direct_mmap.h
@@ -62,7 +62,7 @@ extern "C" void* __mmap2(void*, size_t, int, int, int, size_t);
#endif // __BIONIC__
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// Platform specific logic extracted from
@@ -129,7 +129,7 @@ inline int DirectMunmap(void* start, size_t length) {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#else // !__linux__
@@ -138,7 +138,7 @@ inline int DirectMunmap(void* start, size_t length) {
// actual mmap()/munmap() methods.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd,
@@ -151,7 +151,7 @@ inline int DirectMunmap(void* start, size_t length) {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // __linux__
diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h
index 8643bffa..9677530e 100644
--- a/absl/base/internal/endian.h
+++ b/absl/base/internal/endian.h
@@ -19,9 +19,6 @@
// The following guarantees declaration of the byte swap functions
#ifdef _MSC_VER
#include <stdlib.h> // NOLINT(build/include)
-#elif defined(__APPLE__)
-// macOS / Darwin features
-#include <libkern/OSByteOrder.h>
#elif defined(__FreeBSD__)
#include <sys/endian.h>
#elif defined(__GLIBC__)
@@ -34,7 +31,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Use compiler byte-swapping intrinsics if they are available. 32-bit
// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0.
@@ -64,11 +61,6 @@ inline uint16_t gbswap_16(uint16_t host_int) {
return _byteswap_ushort(host_int);
}
-#elif defined(__APPLE__)
-inline uint64_t gbswap_64(uint64_t host_int) { return OSSwapInt16(host_int); }
-inline uint32_t gbswap_32(uint32_t host_int) { return OSSwapInt32(host_int); }
-inline uint16_t gbswap_16(uint16_t host_int) { return OSSwapInt64(host_int); }
-
#else
inline uint64_t gbswap_64(uint64_t host_int) {
#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__)
@@ -114,7 +106,7 @@ inline uint16_t gbswap_16(uint16_t host_int) {
#endif
}
-#endif // intrinics available
+#endif // intrinsics available
#ifdef ABSL_IS_LITTLE_ENDIAN
@@ -268,7 +260,7 @@ inline void Store64(void *p, uint64_t v) {
} // namespace big_endian
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_ENDIAN_H_
diff --git a/absl/base/internal/endian_test.cc b/absl/base/internal/endian_test.cc
index 0c683286..aa6b8496 100644
--- a/absl/base/internal/endian_test.cc
+++ b/absl/base/internal/endian_test.cc
@@ -24,7 +24,7 @@
#include "absl/base/config.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
const uint64_t kInitialNumber{0x0123456789abcdef};
@@ -261,5 +261,5 @@ TEST(EndianessTest, big_endian) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/errno_saver.h b/absl/base/internal/errno_saver.h
new file mode 100644
index 00000000..251de510
--- /dev/null
+++ b/absl/base/internal/errno_saver.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_BASE_INTERNAL_ERRNO_SAVER_H_
+#define ABSL_BASE_INTERNAL_ERRNO_SAVER_H_
+
+#include <cerrno>
+
+#include "absl/base/config.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+
+// `ErrnoSaver` captures the value of `errno` upon construction and restores it
+// upon deletion. It is used in low-level code and must be super fast. Do not
+// add instrumentation, even in debug modes.
+class ErrnoSaver {
+ public:
+ ErrnoSaver() : saved_errno_(errno) {}
+ ~ErrnoSaver() { errno = saved_errno_; }
+ int operator()() const { return saved_errno_; }
+
+ private:
+ const int saved_errno_;
+};
+
+} // namespace base_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_BASE_INTERNAL_ERRNO_SAVER_H_
diff --git a/absl/base/internal/errno_saver_test.cc b/absl/base/internal/errno_saver_test.cc
new file mode 100644
index 00000000..b845e2dd
--- /dev/null
+++ b/absl/base/internal/errno_saver_test.cc
@@ -0,0 +1,44 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/errno_saver.h"
+
+#include <cerrno>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+using ::testing::Eq;
+
+struct ErrnoPrinter {
+ int no;
+};
+std::ostream &operator<<(std::ostream &os, ErrnoPrinter ep) {
+ return os << strerror(ep.no) << " [" << ep.no << "]";
+}
+bool operator==(ErrnoPrinter one, ErrnoPrinter two) { return one.no == two.no; }
+
+TEST(ErrnoSaverTest, Works) {
+ errno = EDOM;
+ {
+ absl::base_internal::ErrnoSaver errno_saver;
+ EXPECT_THAT(ErrnoPrinter{errno}, Eq(ErrnoPrinter{EDOM}));
+ errno = ERANGE;
+ EXPECT_THAT(ErrnoPrinter{errno}, Eq(ErrnoPrinter{ERANGE}));
+ EXPECT_THAT(ErrnoPrinter{errno_saver()}, Eq(ErrnoPrinter{EDOM}));
+ }
+ EXPECT_THAT(ErrnoPrinter{errno}, Eq(ErrnoPrinter{EDOM}));
+}
+} // namespace
diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc
index 6ef4325c..6ccac418 100644
--- a/absl/base/internal/exception_safety_testing.cc
+++ b/absl/base/internal/exception_safety_testing.cc
@@ -14,6 +14,8 @@
#include "absl/base/internal/exception_safety_testing.h"
+#ifdef ABSL_HAVE_EXCEPTIONS
+
#include "gtest/gtest.h"
#include "absl/meta/type_traits.h"
@@ -73,3 +75,5 @@ std::string GetSpecString(AllocSpec spec) {
} // namespace exceptions_internal
} // namespace testing
+
+#endif // ABSL_HAVE_EXCEPTIONS
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index be38ba54..6ba89d05 100644
--- a/absl/base/internal/exception_safety_testing.h
+++ b/absl/base/internal/exception_safety_testing.h
@@ -17,6 +17,10 @@
#ifndef ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
#define ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_EXCEPTIONS
+
#include <cstddef>
#include <cstdint>
#include <functional>
@@ -27,7 +31,6 @@
#include <unordered_map>
#include "gtest/gtest.h"
-#include "absl/base/config.h"
#include "absl/base/internal/pretty_function.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
@@ -1093,4 +1096,6 @@ class ExceptionSafetyTestBuilder {
} // namespace testing
+#endif // ABSL_HAVE_EXCEPTIONS
+
#endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
diff --git a/absl/base/internal/exponential_biased.cc b/absl/base/internal/exponential_biased.cc
new file mode 100644
index 00000000..1b30c061
--- /dev/null
+++ b/absl/base/internal/exponential_biased.cc
@@ -0,0 +1,93 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/exponential_biased.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <atomic>
+#include <cmath>
+#include <limits>
+
+#include "absl/base/attributes.h"
+#include "absl/base/optimization.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+
+// The algorithm generates a random number between 0 and 1 and applies the
+// inverse cumulative distribution function for an exponential. Specifically:
+// Let m be the inverse of the sample period, then the probability
+// distribution function is m*exp(-mx) so the CDF is
+// p = 1 - exp(-mx), so
+// q = 1 - p = exp(-mx)
+// log_e(q) = -mx
+// -log_e(q)/m = x
+// log_2(q) * (-log_e(2) * 1/m) = x
+// In the code, q is actually in the range 1 to 2**26, hence the -26 below
+int64_t ExponentialBiased::GetSkipCount(int64_t mean) {
+ if (ABSL_PREDICT_FALSE(!initialized_)) {
+ Initialize();
+ }
+
+ uint64_t rng = NextRandom(rng_);
+ rng_ = rng;
+
+ // Take the top 26 bits as the random number
+ // (This plus the 1<<58 sampling bound give a max possible step of
+ // 5194297183973780480 bytes.)
+ // The uint32_t cast is to prevent a (hard-to-reproduce) NAN
+ // under piii debug for some binaries.
+ double q = static_cast<uint32_t>(rng >> (kPrngNumBits - 26)) + 1.0;
+ // Put the computed p-value through the CDF of a geometric.
+ double interval = bias_ + (std::log2(q) - 26) * (-std::log(2.0) * mean);
+ // Very large values of interval overflow int64_t. To avoid that, we will
+ // cheat and clamp any huge values to (int64_t max)/2. This is a potential
+ // source of bias, but the mean would need to be such a large value that it's
+ // not likely to come up. For example, with a mean of 1e18, the probability of
+ // hitting this condition is about 1/1000. For a mean of 1e17, standard
+ // calculators claim that this event won't happen.
+ if (interval > static_cast<double>(std::numeric_limits<int64_t>::max() / 2)) {
+ // Assume huge values are bias neutral, retain bias for next call.
+ return std::numeric_limits<int64_t>::max() / 2;
+ }
+ double value = std::round(interval);
+ bias_ = interval - value;
+ return value;
+}
+
+int64_t ExponentialBiased::GetStride(int64_t mean) {
+ return GetSkipCount(mean - 1) + 1;
+}
+
+void ExponentialBiased::Initialize() {
+ // We don't get well distributed numbers from `this` so we call NextRandom() a
+ // bunch to mush the bits around. We use a global_rand to handle the case
+ // where the same thread (by memory address) gets created and destroyed
+ // repeatedly.
+ ABSL_CONST_INIT static std::atomic<uint32_t> global_rand(0);
+ uint64_t r = reinterpret_cast<uint64_t>(this) +
+ global_rand.fetch_add(1, std::memory_order_relaxed);
+ for (int i = 0; i < 20; ++i) {
+ r = NextRandom(r);
+ }
+ rng_ = r;
+ initialized_ = true;
+}
+
+} // namespace base_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/base/internal/exponential_biased.h b/absl/base/internal/exponential_biased.h
new file mode 100644
index 00000000..94f79a33
--- /dev/null
+++ b/absl/base/internal/exponential_biased.h
@@ -0,0 +1,130 @@
+// 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_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
+#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
+
+#include <stdint.h>
+
+#include "absl/base/config.h"
+#include "absl/base/macros.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+
+// ExponentialBiased provides a small and fast random number generator for a
+// rounded exponential distribution. This generator manages very little state,
+// and imposes no synchronization overhead. This makes it useful in specialized
+// scenarios requiring minimum overhead, such as stride based periodic sampling.
+//
+// ExponentialBiased provides two closely related functions, GetSkipCount() and
+// GetStride(), both returning a rounded integer defining a number of events
+// required before some event with a given mean probability occurs.
+//
+// The distribution is useful to generate a random wait time or some periodic
+// event with a given mean probability. For example, if an action is supposed to
+// happen on average once every 'N' events, then we can get a random 'stride'
+// counting down how long before the event to happen. For example, if we'd want
+// to sample one in every 1000 'Frobber' calls, our code could look like this:
+//
+// Frobber::Frobber() {
+// stride_ = exponential_biased_.GetStride(1000);
+// }
+//
+// void Frobber::Frob(int arg) {
+// if (--stride == 0) {
+// SampleFrob(arg);
+// stride_ = exponential_biased_.GetStride(1000);
+// }
+// ...
+// }
+//
+// The rounding of the return value creates a bias, especially for smaller means
+// where the distribution of the fraction is not evenly distributed. We correct
+// this bias by tracking the fraction we rounded up or down on each iteration,
+// effectively tracking the distance between the cumulative value, and the
+// rounded cumulative value. For example, given a mean of 2:
+//
+// raw = 1.63076, cumulative = 1.63076, rounded = 2, bias = -0.36923
+// raw = 0.14624, cumulative = 1.77701, rounded = 2, bias = 0.14624
+// raw = 4.93194, cumulative = 6.70895, rounded = 7, bias = -0.06805
+// raw = 0.24206, cumulative = 6.95101, rounded = 7, bias = 0.24206
+// etc...
+//
+// Adjusting with rounding bias is relatively trivial:
+//
+// double value = bias_ + exponential_distribution(mean)();
+// double rounded_value = std::round(value);
+// bias_ = value - rounded_value;
+// return rounded_value;
+//
+// This class is thread-compatible.
+class ExponentialBiased {
+ public:
+ // The number of bits set by NextRandom.
+ static constexpr int kPrngNumBits = 48;
+
+ // `GetSkipCount()` returns the number of events to skip before some chosen
+ // event happens. For example, randomly tossing a coin, we will on average
+ // throw heads once before we get tails. We can simulate random coin tosses
+ // using GetSkipCount() as:
+ //
+ // ExponentialBiased eb;
+ // for (...) {
+ // int number_of_heads_before_tail = eb.GetSkipCount(1);
+ // for (int flips = 0; flips < number_of_heads_before_tail; ++flips) {
+ // printf("head...");
+ // }
+ // printf("tail\n");
+ // }
+ //
+ int64_t GetSkipCount(int64_t mean);
+
+ // GetStride() returns the number of events required for a specific event to
+ // happen. See the class comments for a usage example. `GetStride()` is
+ // equivalent to `GetSkipCount(mean - 1) + 1`. When to use `GetStride()` or
+ // `GetSkipCount()` depends mostly on what best fits the use case.
+ int64_t GetStride(int64_t mean);
+
+ // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1]
+ //
+ // This is public to enable testing.
+ static uint64_t NextRandom(uint64_t rnd);
+
+ private:
+ void Initialize();
+
+ uint64_t rng_{0};
+ double bias_{0};
+ bool initialized_{false};
+};
+
+// Returns the next prng value.
+// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48
+// This is the lrand64 generator.
+inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) {
+ const uint64_t prng_mult = uint64_t{0x5DEECE66D};
+ const uint64_t prng_add = 0xB;
+ const uint64_t prng_mod_power = 48;
+ const uint64_t prng_mod_mask =
+ ~((~static_cast<uint64_t>(0)) << prng_mod_power);
+ return (prng_mult * rnd + prng_add) & prng_mod_mask;
+}
+
+} // namespace base_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
diff --git a/absl/base/internal/exponential_biased_test.cc b/absl/base/internal/exponential_biased_test.cc
new file mode 100644
index 00000000..90a482d2
--- /dev/null
+++ b/absl/base/internal/exponential_biased_test.cc
@@ -0,0 +1,199 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/exponential_biased.h"
+
+#include <stddef.h>
+
+#include <cmath>
+#include <cstdint>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/str_cat.h"
+
+using ::testing::Ge;
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+
+MATCHER_P2(IsBetween, a, b,
+ absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a,
+ " and ", b)) {
+ return a <= arg && arg <= b;
+}
+
+// Tests of the quality of the random numbers generated
+// This uses the Anderson Darling test for uniformity.
+// See "Evaluating the Anderson-Darling Distribution" by Marsaglia
+// for details.
+
+// Short cut version of ADinf(z), z>0 (from Marsaglia)
+// This returns the p-value for Anderson Darling statistic in
+// the limit as n-> infinity. For finite n, apply the error fix below.
+double AndersonDarlingInf(double z) {
+ if (z < 2) {
+ return exp(-1.2337141 / z) / sqrt(z) *
+ (2.00012 +
+ (0.247105 -
+ (0.0649821 - (0.0347962 - (0.011672 - 0.00168691 * z) * z) * z) *
+ z) *
+ z);
+ }
+ return exp(
+ -exp(1.0776 -
+ (2.30695 -
+ (0.43424 - (0.082433 - (0.008056 - 0.0003146 * z) * z) * z) * z) *
+ z));
+}
+
+// Corrects the approximation error in AndersonDarlingInf for small values of n
+// Add this to AndersonDarlingInf to get a better approximation
+// (from Marsaglia)
+double AndersonDarlingErrFix(int n, double x) {
+ if (x > 0.8) {
+ return (-130.2137 +
+ (745.2337 -
+ (1705.091 - (1950.646 - (1116.360 - 255.7844 * x) * x) * x) * x) *
+ x) /
+ n;
+ }
+ double cutoff = 0.01265 + 0.1757 / n;
+ if (x < cutoff) {
+ double t = x / cutoff;
+ t = sqrt(t) * (1 - t) * (49 * t - 102);
+ return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n;
+ } else {
+ double t = (x - cutoff) / (0.8 - cutoff);
+ t = -0.00022633 +
+ (6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864 * t) * t) * t) * t) *
+ t;
+ return t * (0.04213 + 0.01365 / n) / n;
+ }
+}
+
+// Returns the AndersonDarling p-value given n and the value of the statistic
+double AndersonDarlingPValue(int n, double z) {
+ double ad = AndersonDarlingInf(z);
+ double errfix = AndersonDarlingErrFix(n, ad);
+ return ad + errfix;
+}
+
+double AndersonDarlingStatistic(const std::vector<double>& random_sample) {
+ int n = random_sample.size();
+ double ad_sum = 0;
+ for (int i = 0; i < n; i++) {
+ ad_sum += (2 * i + 1) *
+ std::log(random_sample[i] * (1 - random_sample[n - 1 - i]));
+ }
+ double ad_statistic = -n - 1 / static_cast<double>(n) * ad_sum;
+ return ad_statistic;
+}
+
+// Tests if the array of doubles is uniformly distributed.
+// Returns the p-value of the Anderson Darling Statistic
+// for the given set of sorted random doubles
+// See "Evaluating the Anderson-Darling Distribution" by
+// Marsaglia and Marsaglia for details.
+double AndersonDarlingTest(const std::vector<double>& random_sample) {
+ double ad_statistic = AndersonDarlingStatistic(random_sample);
+ double p = AndersonDarlingPValue(random_sample.size(), ad_statistic);
+ return p;
+}
+
+TEST(ExponentialBiasedTest, CoinTossDemoWithGetSkipCount) {
+ ExponentialBiased eb;
+ for (int runs = 0; runs < 10; ++runs) {
+ for (int flips = eb.GetSkipCount(1); flips > 0; --flips) {
+ printf("head...");
+ }
+ printf("tail\n");
+ }
+ int heads = 0;
+ for (int i = 0; i < 10000000; i += 1 + eb.GetSkipCount(1)) {
+ ++heads;
+ }
+ printf("Heads = %d (%f%%)\n", heads, 100.0 * heads / 10000000);
+}
+
+TEST(ExponentialBiasedTest, SampleDemoWithStride) {
+ ExponentialBiased eb;
+ int stride = eb.GetStride(10);
+ int samples = 0;
+ for (int i = 0; i < 10000000; ++i) {
+ if (--stride == 0) {
+ ++samples;
+ stride = eb.GetStride(10);
+ }
+ }
+ printf("Samples = %d (%f%%)\n", samples, 100.0 * samples / 10000000);
+}
+
+
+// Testing that NextRandom generates uniform random numbers. Applies the
+// Anderson-Darling test for uniformity
+TEST(ExponentialBiasedTest, TestNextRandom) {
+ for (auto n : std::vector<int>({
+ 10, // Check short-range correlation
+ 100, 1000,
+ 10000 // Make sure there's no systemic error
+ })) {
+ uint64_t x = 1;
+ // This assumes that the prng returns 48 bit numbers
+ uint64_t max_prng_value = static_cast<uint64_t>(1) << 48;
+ // Initialize.
+ for (int i = 1; i <= 20; i++) {
+ x = ExponentialBiased::NextRandom(x);
+ }
+ std::vector<uint64_t> int_random_sample(n);
+ // Collect samples
+ for (int i = 0; i < n; i++) {
+ int_random_sample[i] = x;
+ x = ExponentialBiased::NextRandom(x);
+ }
+ // First sort them...
+ std::sort(int_random_sample.begin(), int_random_sample.end());
+ std::vector<double> random_sample(n);
+ // Convert them to uniform randoms (in the range [0,1])
+ for (int i = 0; i < n; i++) {
+ random_sample[i] =
+ static_cast<double>(int_random_sample[i]) / max_prng_value;
+ }
+ // Now compute the Anderson-Darling statistic
+ double ad_pvalue = AndersonDarlingTest(random_sample);
+ EXPECT_GT(std::min(ad_pvalue, 1 - ad_pvalue), 0.0001)
+ << "prng is not uniform: n = " << n << " p = " << ad_pvalue;
+ }
+}
+
+// The generator needs to be available as a thread_local and as a static
+// variable.
+TEST(ExponentialBiasedTest, InitializationModes) {
+ ABSL_CONST_INIT static ExponentialBiased eb_static;
+ EXPECT_THAT(eb_static.GetSkipCount(2), Ge(0));
+
+#if ABSL_HAVE_THREAD_LOCAL
+ thread_local ExponentialBiased eb_thread;
+ EXPECT_THAT(eb_thread.GetSkipCount(2), Ge(0));
+#endif
+
+ ExponentialBiased eb_stack;
+ EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0));
+}
+
+} // namespace base_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/base/internal/hide_ptr.h b/absl/base/internal/hide_ptr.h
index 3720db18..1dba8090 100644
--- a/absl/base/internal/hide_ptr.h
+++ b/absl/base/internal/hide_ptr.h
@@ -17,8 +17,10 @@
#include <cstdint>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// Arbitrary value with high bits set. Xor'ing with it is unlikely
@@ -43,7 +45,7 @@ inline T* UnhidePtr(uintptr_t hidden) {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_
diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h
index b9d0f621..a3154ed7 100644
--- a/absl/base/internal/identity.h
+++ b/absl/base/internal/identity.h
@@ -16,8 +16,10 @@
#ifndef ABSL_BASE_INTERNAL_IDENTITY_H_
#define ABSL_BASE_INTERNAL_IDENTITY_H_
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace internal {
template <typename T>
@@ -29,7 +31,7 @@ template <typename T>
using identity_t = typename identity<T>::type;
} // namespace internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_IDENTITY_H_
diff --git a/absl/base/internal/inline_variable_testing.h b/absl/base/internal/inline_variable_testing.h
index 0ebdb9d8..3856b9f8 100644
--- a/absl/base/internal/inline_variable_testing.h
+++ b/absl/base/internal/inline_variable_testing.h
@@ -18,7 +18,7 @@
#include "absl/base/internal/inline_variable.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace inline_variable_testing_internal {
struct Foo {
@@ -40,7 +40,7 @@ const int& get_int_a();
const int& get_int_b();
} // namespace inline_variable_testing_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_
diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h
index 030b98df..c4eceebd 100644
--- a/absl/base/internal/invoke.h
+++ b/absl/base/internal/invoke.h
@@ -45,7 +45,7 @@
// top of this file for the API documentation.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// The five classes below each implement one of the clauses from the definition
@@ -181,7 +181,7 @@ InvokeT<F, Args...> Invoke(F&& f, Args&&... args) {
std::forward<Args>(args)...);
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_INVOKE_H_
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index 419c0e45..1bf94438 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -63,7 +63,7 @@
#endif // __APPLE__
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// A first-fit allocator with amortized logarithmic free() time.
@@ -204,32 +204,33 @@ struct LowLevelAlloc::Arena {
base_internal::SpinLock mu;
// Head of free list, sorted by address
- AllocList freelist GUARDED_BY(mu);
+ AllocList freelist ABSL_GUARDED_BY(mu);
// Count of allocated blocks
- int32_t allocation_count GUARDED_BY(mu);
+ int32_t allocation_count ABSL_GUARDED_BY(mu);
// flags passed to NewArena
const uint32_t flags;
// Result of sysconf(_SC_PAGESIZE)
const size_t pagesize;
// Lowest power of two >= max(16, sizeof(AllocList))
- const size_t roundup;
+ const size_t round_up;
// Smallest allocation block size
const size_t min_size;
// PRNG state
- uint32_t random GUARDED_BY(mu);
+ uint32_t random ABSL_GUARDED_BY(mu);
};
namespace {
-using ArenaStorage = std::aligned_storage<sizeof(LowLevelAlloc::Arena),
- alignof(LowLevelAlloc::Arena)>::type;
-
// Static storage space for the lazily-constructed, default global arena
// instances. We require this space because the whole point of LowLevelAlloc
// is to avoid relying on malloc/new.
-ArenaStorage default_arena_storage;
-ArenaStorage unhooked_arena_storage;
+alignas(LowLevelAlloc::Arena) unsigned char default_arena_storage[sizeof(
+ LowLevelAlloc::Arena)];
+alignas(LowLevelAlloc::Arena) unsigned char unhooked_arena_storage[sizeof(
+ LowLevelAlloc::Arena)];
#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
-ArenaStorage unhooked_async_sig_safe_arena_storage;
+alignas(
+ LowLevelAlloc::Arena) unsigned char unhooked_async_sig_safe_arena_storage
+ [sizeof(LowLevelAlloc::Arena)];
#endif
// We must use LowLevelCallOnce here to construct the global arenas, rather than
@@ -276,10 +277,10 @@ static const uintptr_t kMagicAllocated = 0x4c833e95U;
static const uintptr_t kMagicUnallocated = ~kMagicAllocated;
namespace {
-class SCOPED_LOCKABLE ArenaLock {
+class ABSL_SCOPED_LOCKABLE ArenaLock {
public:
explicit ArenaLock(LowLevelAlloc::Arena *arena)
- EXCLUSIVE_LOCK_FUNCTION(arena->mu)
+ ABSL_EXCLUSIVE_LOCK_FUNCTION(arena->mu)
: arena_(arena) {
#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
@@ -291,7 +292,7 @@ class SCOPED_LOCKABLE ArenaLock {
arena_->mu.Lock();
}
~ArenaLock() { ABSL_RAW_CHECK(left_, "haven't left Arena region"); }
- void Leave() UNLOCK_FUNCTION() {
+ void Leave() ABSL_UNLOCK_FUNCTION() {
arena_->mu.Unlock();
#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if (mask_valid_) {
@@ -337,11 +338,11 @@ size_t GetPageSize() {
size_t RoundedUpBlockSize() {
// Round up block sizes to a power of two close to the header size.
- size_t roundup = 16;
- while (roundup < sizeof(AllocList::Header)) {
- roundup += roundup;
+ size_t round_up = 16;
+ while (round_up < sizeof(AllocList::Header)) {
+ round_up += round_up;
}
- return roundup;
+ return round_up;
}
} // namespace
@@ -351,8 +352,8 @@ LowLevelAlloc::Arena::Arena(uint32_t flags_value)
allocation_count(0),
flags(flags_value),
pagesize(GetPageSize()),
- roundup(RoundedUpBlockSize()),
- min_size(2 * roundup),
+ round_up(RoundedUpBlockSize()),
+ min_size(2 * round_up),
random(0) {
freelist.header.size = 0;
freelist.header.magic =
@@ -448,7 +449,7 @@ static inline uintptr_t RoundUp(uintptr_t addr, uintptr_t align) {
// that the freelist is in the correct order, that it
// consists of regions marked "unallocated", and that no two regions
// are adjacent in memory (they should have been coalesced).
-// L < arena->mu
+// L >= arena->mu
static AllocList *Next(int i, AllocList *prev, LowLevelAlloc::Arena *arena) {
ABSL_RAW_CHECK(i < prev->levels, "too few levels in Next()");
AllocList *next = prev->next[i];
@@ -509,8 +510,6 @@ void LowLevelAlloc::Free(void *v) {
if (v != nullptr) {
AllocList *f = reinterpret_cast<AllocList *>(
reinterpret_cast<char *>(v) - sizeof (f->header));
- ABSL_RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header),
- "bad magic number in Free()");
LowLevelAlloc::Arena *arena = f->header.arena;
ArenaLock section(arena);
AddToFreelist(v, arena);
@@ -529,7 +528,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
ArenaLock section(arena);
// round up with header
size_t req_rnd = RoundUp(CheckedAdd(request, sizeof (s->header)),
- arena->roundup);
+ arena->round_up);
for (;;) { // loop until we find a suitable region
// find the minimum levels that a block of this size must have
int i = LLA_SkiplistLevels(req_rnd, arena->min_size, nullptr) - 1;
@@ -615,7 +614,7 @@ void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/absl/base/internal/low_level_alloc.h b/absl/base/internal/low_level_alloc.h
index 32b6ec17..db91951c 100644
--- a/absl/base/internal/low_level_alloc.h
+++ b/absl/base/internal/low_level_alloc.h
@@ -55,7 +55,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
class LowLevelAlloc {
@@ -120,7 +120,7 @@ class LowLevelAlloc {
};
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_
diff --git a/absl/base/internal/low_level_alloc_test.cc b/absl/base/internal/low_level_alloc_test.cc
index b6eb8b30..7abbbf9c 100644
--- a/absl/base/internal/low_level_alloc_test.cc
+++ b/absl/base/internal/low_level_alloc_test.cc
@@ -22,7 +22,7 @@
#include <utility>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
namespace {
@@ -150,7 +150,7 @@ static struct BeforeMain {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
int main(int argc, char *argv[]) {
diff --git a/absl/base/internal/low_level_scheduling.h b/absl/base/internal/low_level_scheduling.h
index b762279d..961cc981 100644
--- a/absl/base/internal/low_level_scheduling.h
+++ b/absl/base/internal/low_level_scheduling.h
@@ -28,7 +28,7 @@ extern "C" bool __google_disable_rescheduling(void);
extern "C" void __google_enable_rescheduling(bool disable_result);
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
class SchedulingHelper; // To allow use of SchedulingGuard.
@@ -101,7 +101,7 @@ inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_
diff --git a/absl/base/internal/periodic_sampler.cc b/absl/base/internal/periodic_sampler.cc
new file mode 100644
index 00000000..520dabba
--- /dev/null
+++ b/absl/base/internal/periodic_sampler.cc
@@ -0,0 +1,53 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/periodic_sampler.h"
+
+#include <atomic>
+
+#include "absl/base/internal/exponential_biased.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+
+int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept {
+ return rng_.GetStride(period);
+}
+
+bool PeriodicSamplerBase::SubtleConfirmSample() noexcept {
+ int current_period = period();
+
+ // Deal with period case 0 (always off) and 1 (always on)
+ if (ABSL_PREDICT_FALSE(current_period < 2)) {
+ stride_ = 0;
+ return current_period == 1;
+ }
+
+ // Check if this is the first call to Sample()
+ if (ABSL_PREDICT_FALSE(stride_ == 1)) {
+ stride_ = static_cast<uint64_t>(-GetExponentialBiased(current_period));
+ if (static_cast<int64_t>(stride_) < -1) {
+ ++stride_;
+ return false;
+ }
+ }
+
+ stride_ = static_cast<uint64_t>(-GetExponentialBiased(current_period));
+ return true;
+}
+
+} // namespace base_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/base/internal/periodic_sampler.h b/absl/base/internal/periodic_sampler.h
new file mode 100644
index 00000000..f8a86796
--- /dev/null
+++ b/absl/base/internal/periodic_sampler.h
@@ -0,0 +1,211 @@
+// 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_BASE_INTERNAL_PERIODIC_SAMPLER_H_
+#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
+
+#include <stdint.h>
+
+#include <atomic>
+
+#include "absl/base/internal/exponential_biased.h"
+#include "absl/base/optimization.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+
+// PeriodicSamplerBase provides the basic period sampler implementation.
+//
+// This is the base class for the templated PeriodicSampler class, which holds
+// a global std::atomic value identified by a user defined tag, such that
+// each specific PeriodSampler implementation holds its own global period.
+//
+// PeriodicSamplerBase is thread-compatible except where stated otherwise.
+class PeriodicSamplerBase {
+ public:
+ // PeriodicSamplerBase is trivial / copyable / movable / destructible.
+ PeriodicSamplerBase() = default;
+ PeriodicSamplerBase(PeriodicSamplerBase&&) = default;
+ PeriodicSamplerBase(const PeriodicSamplerBase&) = default;
+
+ // Returns true roughly once every `period` calls. This is established by a
+ // randomly picked `stride` that is counted down on each call to `Sample`.
+ // This stride is picked such that the probability of `Sample()` returning
+ // true is 1 in `period`.
+ inline bool Sample() noexcept;
+
+ // The below methods are intended for optimized use cases where the
+ // size of the inlined fast path code is highly important. Applications
+ // should use the `Sample()` method unless they have proof that their
+ // specific use case requires the optimizations offered by these methods.
+ //
+ // An example of such a use case is SwissTable sampling. All sampling checks
+ // are in inlined SwissTable methods, and the number of call sites is huge.
+ // In this case, the inlined code size added to each translation unit calling
+ // SwissTable methods is non-trivial.
+ //
+ // The `SubtleMaybeSample()` function spuriously returns true even if the
+ // function should not be sampled, applications MUST match each call to
+ // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call,
+ // and use the result of the latter as the sampling decision.
+ // In other words: the code should logically be equivalent to:
+ //
+ // if (SubtleMaybeSample() && SubtleConfirmSample()) {
+ // // Sample this call
+ // }
+ //
+ // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can
+ // be placed out of line, for example, the typical use case looks as follows:
+ //
+ // // --- frobber.h -----------
+ // void FrobberSampled();
+ //
+ // inline void FrobberImpl() {
+ // // ...
+ // }
+ //
+ // inline void Frobber() {
+ // if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) {
+ // FrobberSampled();
+ // } else {
+ // FrobberImpl();
+ // }
+ // }
+ //
+ // // --- frobber.cc -----------
+ // void FrobberSampled() {
+ // if (!sampler.SubtleConfirmSample())) {
+ // // Spurious false positive
+ // FrobberImpl();
+ // return;
+ // }
+ //
+ // // Sampled execution
+ // // ...
+ // }
+ inline bool SubtleMaybeSample() noexcept;
+ bool SubtleConfirmSample() noexcept;
+
+ protected:
+ // We explicitly don't use a virtual destructor as this class is never
+ // virtually destroyed, and it keeps the class trivial, which avoids TLS
+ // prologue and epilogue code for our TLS instances.
+ ~PeriodicSamplerBase() = default;
+
+ // Returns the next stride for our sampler.
+ // This function is virtual for testing purposes only.
+ virtual int64_t GetExponentialBiased(int period) noexcept;
+
+ private:
+ // Returns the current period of this sampler. Thread-safe.
+ virtual int period() const noexcept = 0;
+
+ // Keep and decrement stride_ as an unsigned integer, but compare the value
+ // to zero casted as a signed int. clang and msvc do not create optimum code
+ // if we use signed for the combined decrement and sign comparison.
+ //
+ // Below 3 alternative options, all compiles generate the best code
+ // using the unsigned increment <---> signed int comparison option.
+ //
+ // Option 1:
+ // int64_t stride_;
+ // if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... }
+ //
+ // GCC x64 (OK) : https://gcc.godbolt.org/z/R5MzzA
+ // GCC ppc (OK) : https://gcc.godbolt.org/z/z7NZAt
+ // Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd
+ // ICC x64 (OK) : https://gcc.godbolt.org/z/rE6s8W
+ // MSVC x64 (OK) : https://gcc.godbolt.org/z/ARMXqS
+ //
+ // Option 2:
+ // int64_t stride_ = 0;
+ // if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... }
+ //
+ // GCC x64 (OK) : https://gcc.godbolt.org/z/jSQxYK
+ // GCC ppc (OK) : https://gcc.godbolt.org/z/VJdYaA
+ // Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX
+ // ICC x64 (OK) : https://gcc.godbolt.org/z/4snaFd
+ // MSVC x64 (BAD): https://gcc.godbolt.org/z/BgnEKE
+ //
+ // Option 3:
+ // uint64_t stride_;
+ // if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) { ... }
+ //
+ // GCC x64 (OK) : https://gcc.godbolt.org/z/bFbfPy
+ // GCC ppc (OK) : https://gcc.godbolt.org/z/S9KkUE
+ // Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4
+ // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD
+ // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5
+ uint64_t stride_ = 0;
+ ExponentialBiased rng_;
+};
+
+inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept {
+ // See comments on `stride_` for the unsigned increment / signed compare.
+ if (ABSL_PREDICT_TRUE(static_cast<int64_t>(++stride_) < 0)) {
+ return false;
+ }
+ return true;
+}
+
+inline bool PeriodicSamplerBase::Sample() noexcept {
+ return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample()
+ : false;
+}
+
+// PeriodicSampler is a concreted periodic sampler implementation.
+// The user provided Tag identifies the implementation, and is required to
+// isolate the global state of this instance from other instances.
+//
+// Typical use case:
+//
+// struct HashTablezTag {};
+// thread_local PeriodicSampler sampler;
+//
+// void HashTableSamplingLogic(...) {
+// if (sampler.Sample()) {
+// HashTableSlowSamplePath(...);
+// }
+// }
+//
+template <typename Tag, int default_period = 0>
+class PeriodicSampler final : public PeriodicSamplerBase {
+ public:
+ ~PeriodicSampler() = default;
+
+ int period() const noexcept final {
+ return period_.load(std::memory_order_relaxed);
+ }
+
+ // Sets the global period for this sampler. Thread-safe.
+ // Setting a period of 0 disables the sampler, i.e., every call to Sample()
+ // will return false. Setting a period of 1 puts the sampler in 'always on'
+ // mode, i.e., every call to Sample() returns true.
+ static void SetGlobalPeriod(int period) {
+ period_.store(period, std::memory_order_relaxed);
+ }
+
+ private:
+ static std::atomic<int> period_;
+};
+
+template <typename Tag, int default_period>
+std::atomic<int> PeriodicSampler<Tag, default_period>::period_(default_period);
+
+} // namespace base_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_
diff --git a/absl/base/internal/periodic_sampler_benchmark.cc b/absl/base/internal/periodic_sampler_benchmark.cc
new file mode 100644
index 00000000..5ad469ce
--- /dev/null
+++ b/absl/base/internal/periodic_sampler_benchmark.cc
@@ -0,0 +1,79 @@
+// 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 "benchmark/benchmark.h"
+#include "absl/base/internal/periodic_sampler.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+namespace {
+
+template <typename Sampler>
+void BM_Sample(Sampler* sampler, benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(sampler);
+ benchmark::DoNotOptimize(sampler->Sample());
+ }
+}
+
+template <typename Sampler>
+void BM_SampleMinunumInlined(Sampler* sampler, benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(sampler);
+ if (ABSL_PREDICT_FALSE(sampler->SubtleMaybeSample())) {
+ benchmark::DoNotOptimize(sampler->SubtleConfirmSample());
+ }
+ }
+}
+
+void BM_PeriodicSampler_TinySample(benchmark::State& state) {
+ struct Tag {};
+ PeriodicSampler<Tag, 10> sampler;
+ BM_Sample(&sampler, state);
+}
+BENCHMARK(BM_PeriodicSampler_TinySample);
+
+void BM_PeriodicSampler_ShortSample(benchmark::State& state) {
+ struct Tag {};
+ PeriodicSampler<Tag, 1024> sampler;
+ BM_Sample(&sampler, state);
+}
+BENCHMARK(BM_PeriodicSampler_ShortSample);
+
+void BM_PeriodicSampler_LongSample(benchmark::State& state) {
+ struct Tag {};
+ PeriodicSampler<Tag, 1024 * 1024> sampler;
+ BM_Sample(&sampler, state);
+}
+BENCHMARK(BM_PeriodicSampler_LongSample);
+
+void BM_PeriodicSampler_LongSampleMinunumInlined(benchmark::State& state) {
+ struct Tag {};
+ PeriodicSampler<Tag, 1024 * 1024> sampler;
+ BM_SampleMinunumInlined(&sampler, state);
+}
+BENCHMARK(BM_PeriodicSampler_LongSampleMinunumInlined);
+
+void BM_PeriodicSampler_Disabled(benchmark::State& state) {
+ struct Tag {};
+ PeriodicSampler<Tag, 0> sampler;
+ BM_Sample(&sampler, state);
+}
+BENCHMARK(BM_PeriodicSampler_Disabled);
+
+} // namespace
+} // namespace base_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/base/internal/periodic_sampler_test.cc b/absl/base/internal/periodic_sampler_test.cc
new file mode 100644
index 00000000..3b301e37
--- /dev/null
+++ b/absl/base/internal/periodic_sampler_test.cc
@@ -0,0 +1,177 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/base/internal/periodic_sampler.h"
+
+#include <thread> // NOLINT(build/c++11)
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+#include "absl/base/macros.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace base_internal {
+namespace {
+
+using testing::Eq;
+using testing::Return;
+using testing::StrictMock;
+
+class MockPeriodicSampler : public PeriodicSamplerBase {
+ public:
+ virtual ~MockPeriodicSampler() = default;
+
+ MOCK_METHOD(int, period, (), (const, noexcept));
+ MOCK_METHOD(int64_t, GetExponentialBiased, (int), (noexcept));
+};
+
+TEST(PeriodicSamplerBaseTest, Sample) {
+ StrictMock<MockPeriodicSampler> sampler;
+
+ EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(16));
+ EXPECT_CALL(sampler, GetExponentialBiased(16))
+ .WillOnce(Return(2))
+ .WillOnce(Return(3))
+ .WillOnce(Return(4));
+
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_TRUE(sampler.Sample());
+
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_TRUE(sampler.Sample());
+
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+}
+
+TEST(PeriodicSamplerBaseTest, ImmediatelySample) {
+ StrictMock<MockPeriodicSampler> sampler;
+
+ EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16));
+ EXPECT_CALL(sampler, GetExponentialBiased(16))
+ .WillOnce(Return(1))
+ .WillOnce(Return(2))
+ .WillOnce(Return(3));
+
+ EXPECT_TRUE(sampler.Sample());
+
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_TRUE(sampler.Sample());
+
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+}
+
+TEST(PeriodicSamplerBaseTest, Disabled) {
+ StrictMock<MockPeriodicSampler> sampler;
+
+ EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(0));
+
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+}
+
+TEST(PeriodicSamplerBaseTest, AlwaysOn) {
+ StrictMock<MockPeriodicSampler> sampler;
+
+ EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(1));
+
+ EXPECT_TRUE(sampler.Sample());
+ EXPECT_TRUE(sampler.Sample());
+ EXPECT_TRUE(sampler.Sample());
+}
+
+TEST(PeriodicSamplerBaseTest, Disable) {
+ StrictMock<MockPeriodicSampler> sampler;
+
+ EXPECT_CALL(sampler, period()).WillOnce(Return(16));
+ EXPECT_CALL(sampler, GetExponentialBiased(16)).WillOnce(Return(3));
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+
+ EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(0));
+
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+}
+
+TEST(PeriodicSamplerBaseTest, Enable) {
+ StrictMock<MockPeriodicSampler> sampler;
+
+ EXPECT_CALL(sampler, period()).WillOnce(Return(0));
+ EXPECT_FALSE(sampler.Sample());
+
+ EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16));
+ EXPECT_CALL(sampler, GetExponentialBiased(16))
+ .Times(2)
+ .WillRepeatedly(Return(3));
+
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_TRUE(sampler.Sample());
+
+ EXPECT_FALSE(sampler.Sample());
+ EXPECT_FALSE(sampler.Sample());
+}
+
+TEST(PeriodicSamplerTest, ConstructConstInit) {
+ struct Tag {};
+ ABSL_CONST_INIT static PeriodicSampler<Tag> sampler;
+ (void)sampler;
+}
+
+TEST(PeriodicSamplerTest, DefaultPeriod0) {
+ struct Tag {};
+ PeriodicSampler<Tag> sampler;
+ EXPECT_THAT(sampler.period(), Eq(0));
+}
+
+TEST(PeriodicSamplerTest, DefaultPeriod) {
+ struct Tag {};
+ PeriodicSampler<Tag, 100> sampler;
+ EXPECT_THAT(sampler.period(), Eq(100));
+}
+
+TEST(PeriodicSamplerTest, SetGlobalPeriod) {
+ struct Tag1 {};
+ struct Tag2 {};
+ PeriodicSampler<Tag1, 25> sampler1;
+ PeriodicSampler<Tag2, 50> sampler2;
+
+ EXPECT_THAT(sampler1.period(), Eq(25));
+ EXPECT_THAT(sampler2.period(), Eq(50));
+
+ std::thread thread([] {
+ PeriodicSampler<Tag1, 25> sampler1;
+ PeriodicSampler<Tag2, 50> sampler2;
+ EXPECT_THAT(sampler1.period(), Eq(25));
+ EXPECT_THAT(sampler2.period(), Eq(50));
+ sampler1.SetGlobalPeriod(10);
+ sampler2.SetGlobalPeriod(20);
+ });
+ thread.join();
+
+ EXPECT_THAT(sampler1.period(), Eq(10));
+ EXPECT_THAT(sampler2.period(), Eq(20));
+}
+
+} // namespace
+} // namespace base_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc
index 1a36d5b6..40cea550 100644
--- a/absl/base/internal/raw_logging.cc
+++ b/absl/base/internal/raw_logging.cc
@@ -37,9 +37,9 @@
// this, consider moving both to config.h instead.
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__Fuchsia__) || defined(__native_client__) || \
- defined(__EMSCRIPTEN__)
-#include <unistd.h>
+ defined(__EMSCRIPTEN__) || defined(__ASYLO__)
+#include <unistd.h>
#define ABSL_HAVE_POSIX_WRITE 1
#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
@@ -71,10 +71,12 @@
// Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a
// whitelisted set of platforms for which we expect not to be able to raw log.
-ABSL_CONST_INIT static absl::base_internal::AtomicHook<
- absl::raw_logging_internal::LogPrefixHook> log_prefix_hook;
-ABSL_CONST_INIT static absl::base_internal::AtomicHook<
- absl::raw_logging_internal::AbortHook> abort_hook;
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
+ absl::raw_logging_internal::LogPrefixHook>
+ log_prefix_hook;
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static absl::base_internal::AtomicHook<
+ absl::raw_logging_internal::AbortHook>
+ abort_hook;
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
static const char kTruncated[] = " ... (message truncated)\n";
@@ -182,7 +184,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line,
} // namespace
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace raw_logging_internal {
void SafeWriteToStderr(const char *s, size_t len) {
#if defined(ABSL_HAVE_SYSCALL_WRITE)
@@ -225,13 +227,14 @@ bool RawLoggingFullySupported() {
#endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
}
-ABSL_CONST_INIT absl::base_internal::AtomicHook<InternalLogFunction>
- internal_log_function(DefaultInternalLog);
+ABSL_DLL ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
+ absl::base_internal::AtomicHook<InternalLogFunction>
+ internal_log_function(DefaultInternalLog);
void RegisterInternalLogFunction(InternalLogFunction func) {
internal_log_function.Store(func);
}
} // namespace raw_logging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h
index 8e5a34ef..418d6c85 100644
--- a/absl/base/internal/raw_logging.h
+++ b/absl/base/internal/raw_logging.h
@@ -22,9 +22,11 @@
#include <string>
#include "absl/base/attributes.h"
+#include "absl/base/config.h"
#include "absl/base/internal/atomic_hook.h"
#include "absl/base/log_severity.h"
#include "absl/base/macros.h"
+#include "absl/base/optimization.h"
#include "absl/base/port.h"
// This is similar to LOG(severity) << format..., but
@@ -70,14 +72,10 @@
//
// The API is a subset of the above: each macro only takes two arguments. Use
// StrCat if you need to build a richer message.
-#define ABSL_INTERNAL_LOG(severity, message) \
- do { \
- constexpr const char* absl_raw_logging_internal_basename = \
- ::absl::raw_logging_internal::Basename(__FILE__, \
- sizeof(__FILE__) - 1); \
- ::absl::raw_logging_internal::internal_log_function( \
- ABSL_RAW_LOGGING_INTERNAL_##severity, \
- absl_raw_logging_internal_basename, __LINE__, message); \
+#define ABSL_INTERNAL_LOG(severity, message) \
+ do { \
+ ::absl::raw_logging_internal::internal_log_function( \
+ ABSL_RAW_LOGGING_INTERNAL_##severity, __FILE__, __LINE__, message); \
} while (0)
#define ABSL_INTERNAL_CHECK(condition, message) \
@@ -97,7 +95,7 @@
::absl::NormalizeLogSeverity(severity)
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace raw_logging_internal {
// Helper function to implement ABSL_RAW_LOG
@@ -158,7 +156,7 @@ using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file,
//
// 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro
// was located.
-// The null-terminated logged message lives in the buffer between 'buf_start'
+// The NUL-terminated logged message lives in the buffer between 'buf_start'
// and 'buf_end'. 'prefix_end' points to the first non-prefix character of the
// buffer (as written by the LogPrefixHook.)
using AbortHook = void (*)(const char* file, int line, const char* buf_start,
@@ -172,12 +170,14 @@ using InternalLogFunction = void (*)(absl::LogSeverity severity,
const char* file, int line,
const std::string& message);
-extern base_internal::AtomicHook<InternalLogFunction> internal_log_function;
+ABSL_DLL ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES extern base_internal::AtomicHook<
+ InternalLogFunction>
+ internal_log_function;
void RegisterInternalLogFunction(InternalLogFunction func);
} // namespace raw_logging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_RAW_LOGGING_H_
diff --git a/absl/base/internal/scheduling_mode.h b/absl/base/internal/scheduling_mode.h
index 48767920..8be5ab6d 100644
--- a/absl/base/internal/scheduling_mode.h
+++ b/absl/base/internal/scheduling_mode.h
@@ -18,8 +18,10 @@
#ifndef ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_
#define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// Used to describe how a thread may be scheduled. Typically associated with
@@ -50,7 +52,7 @@ enum SchedulingMode {
};
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_
diff --git a/absl/base/internal/scoped_set_env.cc b/absl/base/internal/scoped_set_env.cc
index eb7a0116..8a934cb5 100644
--- a/absl/base/internal/scoped_set_env.cc
+++ b/absl/base/internal/scoped_set_env.cc
@@ -23,7 +23,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
namespace {
@@ -77,5 +77,5 @@ ScopedSetEnv::~ScopedSetEnv() {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/scoped_set_env.h b/absl/base/internal/scoped_set_env.h
index 709843bc..19ec7b5d 100644
--- a/absl/base/internal/scoped_set_env.h
+++ b/absl/base/internal/scoped_set_env.h
@@ -19,8 +19,10 @@
#include <string>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
class ScopedSetEnv {
@@ -37,7 +39,7 @@ class ScopedSetEnv {
};
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_
diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc
index 5f443bfa..830d4729 100644
--- a/absl/base/internal/spinlock.cc
+++ b/absl/base/internal/spinlock.cc
@@ -54,11 +54,11 @@
// holder to acquire the lock. There may be outstanding waiter(s).
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
-ABSL_CONST_INIT static base_internal::AtomicHook<void (*)(const void *lock,
- int64_t wait_cycles)>
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static base_internal::AtomicHook<void (*)(
+ const void *lock, int64_t wait_cycles)>
submit_profile_data;
void RegisterSpinLockProfiler(void (*fn)(const void *contendedlock,
@@ -229,5 +229,5 @@ uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h
index df947661..24e2e9a6 100644
--- a/absl/base/internal/spinlock.h
+++ b/absl/base/internal/spinlock.h
@@ -46,10 +46,10 @@
#include "absl/base/thread_annotations.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
-class LOCKABLE SpinLock {
+class ABSL_LOCKABLE SpinLock {
public:
SpinLock() : lockword_(kSpinLockCooperative) {
ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static);
@@ -80,7 +80,7 @@ class LOCKABLE SpinLock {
~SpinLock() { ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); }
// Acquire this SpinLock.
- inline void Lock() EXCLUSIVE_LOCK_FUNCTION() {
+ inline void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() {
ABSL_TSAN_MUTEX_PRE_LOCK(this, 0);
if (!TryLockImpl()) {
SlowLock();
@@ -92,7 +92,7 @@ class LOCKABLE SpinLock {
// acquisition was successful. If the lock was not acquired, false is
// returned. If this SpinLock is free at the time of the call, TryLock
// will return true with high probability.
- inline bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ inline bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock);
bool res = TryLockImpl();
ABSL_TSAN_MUTEX_POST_LOCK(
@@ -102,7 +102,7 @@ class LOCKABLE SpinLock {
}
// Release this SpinLock, which must be held by the calling thread.
- inline void Unlock() UNLOCK_FUNCTION() {
+ inline void Unlock() ABSL_UNLOCK_FUNCTION() {
ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0);
uint32_t lock_value = lockword_.load(std::memory_order_relaxed);
lock_value = lockword_.exchange(lock_value & kSpinLockCooperative,
@@ -180,13 +180,13 @@ class LOCKABLE SpinLock {
// Corresponding locker object that arranges to acquire a spinlock for
// the duration of a C++ scope.
-class SCOPED_LOCKABLE SpinLockHolder {
+class ABSL_SCOPED_LOCKABLE SpinLockHolder {
public:
- inline explicit SpinLockHolder(SpinLock* l) EXCLUSIVE_LOCK_FUNCTION(l)
+ inline explicit SpinLockHolder(SpinLock* l) ABSL_EXCLUSIVE_LOCK_FUNCTION(l)
: lock_(l) {
l->Lock();
}
- inline ~SpinLockHolder() UNLOCK_FUNCTION() { lock_->Unlock(); }
+ inline ~SpinLockHolder() ABSL_UNLOCK_FUNCTION() { lock_->Unlock(); }
SpinLockHolder(const SpinLockHolder&) = delete;
SpinLockHolder& operator=(const SpinLockHolder&) = delete;
@@ -226,11 +226,10 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value,
}
}
- if (lockword_.compare_exchange_strong(
+ if (!lockword_.compare_exchange_strong(
lock_value,
kSpinLockHeld | lock_value | wait_cycles | sched_disabled_bit,
std::memory_order_acquire, std::memory_order_relaxed)) {
- } else {
base_internal::SchedulingGuard::EnableRescheduling(sched_disabled_bit != 0);
}
@@ -238,7 +237,7 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value,
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_SPINLOCK_H_
diff --git a/absl/base/internal/spinlock_linux.inc b/absl/base/internal/spinlock_linux.inc
index 28e29d19..323edd62 100644
--- a/absl/base/internal/spinlock_linux.inc
+++ b/absl/base/internal/spinlock_linux.inc
@@ -19,12 +19,12 @@
#include <unistd.h>
#include <atomic>
-#include <cerrno>
#include <climits>
#include <cstdint>
#include <ctime>
#include "absl/base/attributes.h"
+#include "absl/base/internal/errno_saver.h"
// The SpinLock lockword is `std::atomic<uint32_t>`. Here we assert that
// `std::atomic<uint32_t>` is bitwise equivalent of the `int` expected
@@ -51,12 +51,11 @@ extern "C" {
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
std::atomic<uint32_t> *w, uint32_t value, int loop,
absl::base_internal::SchedulingMode) {
- int save_errno = errno;
+ absl::base_internal::ErrnoSaver errno_saver;
struct timespec tm;
tm.tv_sec = 0;
tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop);
syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm);
- errno = save_errno;
}
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w,
diff --git a/absl/base/internal/spinlock_posix.inc b/absl/base/internal/spinlock_posix.inc
index f025b5f8..fcd21b15 100644
--- a/absl/base/internal/spinlock_posix.inc
+++ b/absl/base/internal/spinlock_posix.inc
@@ -15,10 +15,11 @@
// This file is a Posix-specific part of spinlock_wait.cc
#include <sched.h>
+
#include <atomic>
#include <ctime>
-#include <cerrno>
+#include "absl/base/internal/errno_saver.h"
#include "absl/base/internal/scheduling_mode.h"
#include "absl/base/port.h"
@@ -27,7 +28,7 @@ extern "C" {
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop,
absl::base_internal::SchedulingMode /* mode */) {
- int save_errno = errno;
+ absl::base_internal::ErrnoSaver errno_saver;
if (loop == 0) {
} else if (loop == 1) {
sched_yield();
@@ -37,7 +38,6 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay(
tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop);
nanosleep(&tm, nullptr);
}
- errno = save_errno;
}
ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(
diff --git a/absl/base/internal/spinlock_wait.cc b/absl/base/internal/spinlock_wait.cc
index 60a85196..fa824be1 100644
--- a/absl/base/internal/spinlock_wait.cc
+++ b/absl/base/internal/spinlock_wait.cc
@@ -32,7 +32,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// See spinlock_wait.h for spec.
@@ -77,5 +77,5 @@ int SpinLockSuggestedDelayNS(int loop) {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h
index 7e139de0..169bc749 100644
--- a/absl/base/internal/spinlock_wait.h
+++ b/absl/base/internal/spinlock_wait.h
@@ -24,7 +24,7 @@
#include "absl/base/internal/scheduling_mode.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// SpinLockWait() waits until it can perform one of several transitions from
@@ -63,7 +63,7 @@ void SpinLockDelay(std::atomic<uint32_t> *w, uint32_t value, int loop,
int SpinLockSuggestedDelayNS(int loop);
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// In some build configurations we pass --detect-odr-violations to the
diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc
index 0d5cf821..a0930e97 100644
--- a/absl/base/internal/sysinfo.cc
+++ b/absl/base/internal/sysinfo.cc
@@ -17,7 +17,6 @@
#include "absl/base/attributes.h"
#ifdef _WIN32
-#include <shlwapi.h>
#include <windows.h>
#else
#include <fcntl.h>
@@ -56,13 +55,9 @@
#include "absl/base/internal/unscaledcycleclock.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
-static once_flag init_system_info_once;
-static int num_cpus = 0;
-static double nominal_cpu_frequency = 1.0; // 0.0 might be dangerous.
-
static int GetNumCPUs() {
#if defined(__myriad2__)
return 1;
@@ -77,14 +72,23 @@ static int GetNumCPUs() {
#if defined(_WIN32)
static double GetNominalCPUFrequency() {
- DWORD data;
- DWORD data_size = sizeof(data);
- #pragma comment(lib, "shlwapi.lib") // For SHGetValue().
- if (SUCCEEDED(
- SHGetValueA(HKEY_LOCAL_MACHINE,
- "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
- "~MHz", nullptr, &data, &data_size))) {
- return data * 1e6; // Value is MHz.
+#pragma comment(lib, "advapi32.lib") // For Reg* functions.
+ HKEY key;
+ // Use the Reg* functions rather than the SH functions because shlwapi.dll
+ // pulls in gdi32.dll which makes process destruction much more costly.
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+ "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0,
+ KEY_READ, &key) == ERROR_SUCCESS) {
+ DWORD type = 0;
+ DWORD data = 0;
+ DWORD data_size = sizeof(data);
+ auto result = RegQueryValueExA(key, "~MHz", 0, &type,
+ reinterpret_cast<LPBYTE>(&data), &data_size);
+ RegCloseKey(key);
+ if (result == ERROR_SUCCESS && type == REG_DWORD &&
+ data_size == sizeof(data)) {
+ return data * 1e6; // Value is MHz.
+ }
}
return 1.0;
}
@@ -257,28 +261,34 @@ static double GetNominalCPUFrequency() {
#endif
-// InitializeSystemInfo() may be called before main() and before
-// malloc is properly initialized, therefore this must not allocate
-// memory.
-static void InitializeSystemInfo() {
- num_cpus = GetNumCPUs();
- nominal_cpu_frequency = GetNominalCPUFrequency();
-}
+ABSL_CONST_INIT static once_flag init_num_cpus_once;
+ABSL_CONST_INIT static int num_cpus = 0;
+// NumCPUs() may be called before main() and before malloc is properly
+// initialized, therefore this must not allocate memory.
int NumCPUs() {
- base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo);
+ base_internal::LowLevelCallOnce(
+ &init_num_cpus_once, []() { num_cpus = GetNumCPUs(); });
return num_cpus;
}
+// A default frequency of 0.0 might be dangerous if it is used in division.
+ABSL_CONST_INIT static once_flag init_nominal_cpu_frequency_once;
+ABSL_CONST_INIT static double nominal_cpu_frequency = 1.0;
+
+// NominalCPUFrequency() may be called before main() and before malloc is
+// properly initialized, therefore this must not allocate memory.
double NominalCPUFrequency() {
- base_internal::LowLevelCallOnce(&init_system_info_once, InitializeSystemInfo);
+ base_internal::LowLevelCallOnce(
+ &init_nominal_cpu_frequency_once,
+ []() { nominal_cpu_frequency = GetNominalCPUFrequency(); });
return nominal_cpu_frequency;
}
#if defined(_WIN32)
pid_t GetTID() {
- return GetCurrentThreadId();
+ return pid_t{GetCurrentThreadId()};
}
#elif defined(__linux__)
@@ -402,5 +412,5 @@ pid_t GetTID() {
#endif
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/sysinfo.h b/absl/base/internal/sysinfo.h
index 22739aeb..7246d5dd 100644
--- a/absl/base/internal/sysinfo.h
+++ b/absl/base/internal/sysinfo.h
@@ -26,14 +26,14 @@
#ifndef _WIN32
#include <sys/types.h>
-#else
-#include <intsafe.h>
#endif
+#include <cstdint>
+
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// Nominal core processor cycles per second of each processor. This is _not_
@@ -52,14 +52,15 @@ int NumCPUs();
// On Linux, you may send a signal to the resulting ID with kill(). However,
// it is recommended for portability that you use pthread_kill() instead.
#ifdef _WIN32
-// On Windows, process id and thread id are of the same type according to
-// the return types of GetProcessId() and GetThreadId() are both DWORD.
-using pid_t = DWORD;
+// On Windows, process id and thread id are of the same type according to the
+// return types of GetProcessId() and GetThreadId() are both DWORD, an unsigned
+// 32-bit type.
+using pid_t = uint32_t;
#endif
pid_t GetTID();
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_SYSINFO_H_
diff --git a/absl/base/internal/sysinfo_test.cc b/absl/base/internal/sysinfo_test.cc
index c8323e52..fa8b88b1 100644
--- a/absl/base/internal/sysinfo_test.cc
+++ b/absl/base/internal/sysinfo_test.cc
@@ -28,7 +28,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
namespace {
@@ -59,8 +59,8 @@ TEST(SysinfoTest, GetTID) {
#endif
// Test that TIDs are unique to each thread.
// Uses a few loops to exercise implementations that reallocate IDs.
- for (int i = 0; i < 32; ++i) {
- constexpr int kNumThreads = 64;
+ for (int i = 0; i < 10; ++i) {
+ constexpr int kNumThreads = 10;
Barrier all_threads_done(kNumThreads);
std::vector<std::thread> threads;
@@ -96,5 +96,5 @@ TEST(SysinfoTest, LinuxGetTID) {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc
index dbec326a..d63a04ae 100644
--- a/absl/base/internal/thread_identity.cc
+++ b/absl/base/internal/thread_identity.cc
@@ -28,7 +28,7 @@
#include "absl/base/internal/spinlock.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
#if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11
@@ -56,7 +56,12 @@ void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) {
#ifdef __GNUC__
__attribute__((visibility("protected")))
#endif // __GNUC__
- ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr;
+#if ABSL_PER_THREAD_TLS
+// Prefer __thread to thread_local as benchmarks indicate it is a bit faster.
+ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr = nullptr;
+#elif defined(ABSL_HAVE_THREAD_LOCAL)
+thread_local ThreadIdentity* thread_identity_ptr = nullptr;
+#endif // ABSL_PER_THREAD_TLS
#endif // TLS or CPP11
void SetCurrentThreadIdentity(
@@ -70,8 +75,8 @@ void SetCurrentThreadIdentity(
absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
reclaimer);
-#ifdef __EMSCRIPTEN__
- // Emscripten PThread implementation does not support signals.
+#if defined(__EMSCRIPTEN__) || defined(__MINGW32__)
+ // Emscripten 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,
@@ -90,7 +95,7 @@ void SetCurrentThreadIdentity(
pthread_setspecific(thread_identity_pthread_key,
reinterpret_cast<void*>(identity));
pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr);
-#endif // !__EMSCRIPTEN__
+#endif // !__EMSCRIPTEN__ && !__MINGW32__
#elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS
// NOTE: Not async-safe. But can be open-coded.
@@ -108,6 +113,18 @@ void SetCurrentThreadIdentity(
#endif
}
+#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \
+ ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
+
+// Please see the comment on `CurrentThreadIdentityIfPresent` in
+// thread_identity.h. Because DLLs cannot expose thread_local variables in
+// headers, we opt for the correct-but-slower option of placing the definition
+// of this function only in a translation unit inside DLL.
+#if defined(ABSL_BUILD_DLL) || defined(ABSL_CONSUME_DLL)
+ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; }
+#endif
+#endif
+
void ClearCurrentThreadIdentity() {
#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \
ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
@@ -131,5 +148,5 @@ ThreadIdentity* CurrentThreadIdentityIfPresent() {
#endif
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h
index bb5aa074..ceb109b4 100644
--- a/absl/base/internal/thread_identity.h
+++ b/absl/base/internal/thread_identity.h
@@ -30,10 +30,11 @@
#include <atomic>
#include <cstdint>
+#include "absl/base/config.h"
#include "absl/base/internal/per_thread_tls.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
struct SynchLocksHeld;
struct SynchWaitParams;
@@ -209,7 +210,7 @@ void ClearCurrentThreadIdentity();
#error ABSL_THREAD_IDENTITY_MODE cannot be direcly set
#elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE)
#define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE
-#elif defined(_WIN32)
+#elif defined(_WIN32) && !defined(__MINGW32__)
#define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11
#elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \
(__GOOGLE_GRTE_VERSION__ >= 20140228L)
@@ -225,11 +226,26 @@ void ClearCurrentThreadIdentity();
#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \
ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
-extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr;
+#if ABSL_PER_THREAD_TLS
+ABSL_CONST_INIT extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity*
+ thread_identity_ptr;
+#elif defined(ABSL_HAVE_THREAD_LOCAL)
+ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr;
+#else
+#error Thread-local storage not detected on this platform
+#endif
+// thread_local variables cannot be in headers exposed by DLLs. However, it is
+// important for performance reasons in general that
+// `CurrentThreadIdentityIfPresent` be inlined. This is not possible across a
+// DLL boundary so, with DLLs, we opt to have the function not be inlined. Note
+// that `CurrentThreadIdentityIfPresent` is declared above so we can exclude
+// this entire inline definition when compiling as a DLL.
+#if !defined(ABSL_BUILD_DLL) && !defined(ABSL_CONSUME_DLL)
inline ThreadIdentity* CurrentThreadIdentityIfPresent() {
return thread_identity_ptr;
}
+#endif
#elif ABSL_THREAD_IDENTITY_MODE != \
ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
@@ -237,7 +253,7 @@ inline ThreadIdentity* CurrentThreadIdentityIfPresent() {
#endif
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_
diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc
index 8de6d9eb..3685779c 100644
--- a/absl/base/internal/thread_identity_test.cc
+++ b/absl/base/internal/thread_identity_test.cc
@@ -25,7 +25,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
namespace {
@@ -124,5 +124,5 @@ TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/throw_delegate.cc b/absl/base/internal/throw_delegate.cc
index bee404d4..c055f75d 100644
--- a/absl/base/internal/throw_delegate.cc
+++ b/absl/base/internal/throw_delegate.cc
@@ -22,7 +22,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
namespace {
@@ -104,5 +104,5 @@ void ThrowStdBadFunctionCall() { Throw(std::bad_function_call()); }
void ThrowStdBadAlloc() { Throw(std::bad_alloc()); }
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/internal/throw_delegate.h b/absl/base/internal/throw_delegate.h
index 60ed4f32..075f5272 100644
--- a/absl/base/internal/throw_delegate.h
+++ b/absl/base/internal/throw_delegate.h
@@ -19,8 +19,10 @@
#include <string>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// Helper functions that allow throwing exceptions consistently from anywhere.
@@ -67,7 +69,7 @@ namespace base_internal {
// [[noreturn]] void ThrowStdBadArrayNewLength();
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_
diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h
index a709a446..6be56c86 100644
--- a/absl/base/internal/unaligned_access.h
+++ b/absl/base/internal/unaligned_access.h
@@ -18,9 +18,11 @@
#define ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_
#include <string.h>
+
#include <cstdint>
#include "absl/base/attributes.h"
+#include "absl/base/config.h"
// unaligned APIs
@@ -56,7 +58,7 @@ void __sanitizer_unaligned_store64(void *p, uint64_t v);
} // extern "C"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
inline uint16_t UnalignedLoad16(const void *p) {
@@ -84,7 +86,7 @@ inline void UnalignedStore64(void *p, uint64_t v) {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
@@ -104,7 +106,7 @@ inline void UnalignedStore64(void *p, uint64_t v) {
#else
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
inline uint16_t UnalignedLoad16(const void *p) {
@@ -132,7 +134,7 @@ inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); }
inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \
diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc
index 29a927d3..f1e7bbef 100644
--- a/absl/base/internal/unscaledcycleclock.cc
+++ b/absl/base/internal/unscaledcycleclock.cc
@@ -21,13 +21,18 @@
#endif
#if defined(__powerpc__) || defined(__ppc__)
+#ifdef __GLIBC__
#include <sys/platform/ppc.h>
+#elif defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#endif
#endif
#include "absl/base/internal/sysinfo.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
#if defined(__i386__)
@@ -57,11 +62,43 @@ double UnscaledCycleClock::Frequency() {
#elif defined(__powerpc__) || defined(__ppc__)
int64_t UnscaledCycleClock::Now() {
+#ifdef __GLIBC__
return __ppc_get_timebase();
+#else
+#ifdef __powerpc64__
+ int64_t tbr;
+ asm volatile("mfspr %0, 268" : "=r"(tbr));
+ return tbr;
+#else
+ int32_t tbu, tbl, tmp;
+ asm volatile(
+ "0:\n"
+ "mftbu %[hi32]\n"
+ "mftb %[lo32]\n"
+ "mftbu %[tmp]\n"
+ "cmpw %[tmp],%[hi32]\n"
+ "bne 0b\n"
+ : [ hi32 ] "=r"(tbu), [ lo32 ] "=r"(tbl), [ tmp ] "=r"(tmp));
+ return (static_cast<int64_t>(tbu) << 32) | tbl;
+#endif
+#endif
}
double UnscaledCycleClock::Frequency() {
+#ifdef __GLIBC__
return __ppc_get_timebase_freq();
+#elif defined(__FreeBSD__)
+ static once_flag init_timebase_frequency_once;
+ static double timebase_frequency = 0.0;
+ base_internal::LowLevelCallOnce(&init_timebase_frequency_once, [&]() {
+ size_t length = sizeof(timebase_frequency);
+ sysctlbyname("kern.timecounter.tc.timebase.frequency", &timebase_frequency,
+ &length, nullptr, 0);
+ });
+ return timebase_frequency;
+#else
+#error Must implement UnscaledCycleClock::Frequency()
+#endif
}
#elif defined(__aarch64__)
@@ -97,7 +134,7 @@ double UnscaledCycleClock::Frequency() {
#endif
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_USE_UNSCALED_CYCLECLOCK
diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h
index e6fc9103..cdce9bf8 100644
--- a/absl/base/internal/unscaledcycleclock.h
+++ b/absl/base/internal/unscaledcycleclock.h
@@ -86,7 +86,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
class UnscaledCycleClockWrapperForGetCurrentTime;
} // namespace time_internal
@@ -116,7 +116,7 @@ class UnscaledCycleClock {
};
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_USE_UNSCALED_CYCLECLOCK
diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc
index 685501a4..6aa613c9 100644
--- a/absl/base/invoke_test.cc
+++ b/absl/base/invoke_test.cc
@@ -25,7 +25,7 @@
#include "absl/strings/str_cat.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
namespace {
@@ -219,5 +219,5 @@ TEST(InvokeTest, SfinaeFriendly) {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/log_severity.cc b/absl/base/log_severity.cc
index 8109da19..72312afd 100644
--- a/absl/base/log_severity.cc
+++ b/absl/base/log_severity.cc
@@ -17,11 +17,11 @@
#include <ostream>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
std::ostream& operator<<(std::ostream& os, absl::LogSeverity s) {
if (s == absl::NormalizeLogSeverity(s)) return os << absl::LogSeverityName(s);
return os << "absl::LogSeverity(" << static_cast<int>(s) << ")";
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h
index 45f79cca..65a3b166 100644
--- a/absl/base/log_severity.h
+++ b/absl/base/log_severity.h
@@ -19,13 +19,53 @@
#include <ostream>
#include "absl/base/attributes.h"
+#include "absl/base/config.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
-// Four severity levels are defined. Logging APIs should terminate the program
+// absl::LogSeverity
+//
+// Four severity levels are defined. Logging APIs should terminate the program
// when a message is logged at severity `kFatal`; the other levels have no
// special semantics.
+//
+// Values other than the four defined levels (e.g. produced by `static_cast`)
+// are valid, but their semantics when passed to a function, macro, or flag
+// depend on the function, macro, or flag. The usual behavior is to normalize
+// such values to a defined severity level, however in some cases values other
+// than the defined levels are useful for comparison.
+//
+// Exmaple:
+//
+// // Effectively disables all logging:
+// SetMinLogLevel(static_cast<absl::LogSeverity>(100));
+//
+// Abseil flags may be defined with type `LogSeverity`. Dependency layering
+// constraints require that the `AbslParseFlag()` overload be declared and
+// defined in the flags library itself rather than here. The `AbslUnparseFlag()`
+// overload is defined there as well for consistency.
+//
+// absl::LogSeverity Flag String Representation
+//
+// An `absl::LogSeverity` has a string representation used for parsing
+// command-line flags based on the enumerator name (e.g. `kFatal`) or
+// its unprefixed name (without the `k`) in any case-insensitive form. (E.g.
+// "FATAL", "fatal" or "Fatal" are all valid.) Unparsing such flags produces an
+// unprefixed string representation in all caps (e.g. "FATAL") or an integer.
+//
+// Additionally, the parser accepts arbitrary integers (as if the type were
+// `int`).
+//
+// Examples:
+//
+// --my_log_level=kInfo
+// --my_log_level=INFO
+// --my_log_level=info
+// --my_log_level=0
+//
+// Unparsing a flag produces the same result as `absl::LogSeverityName()` for
+// the standard levels and a base-ten integer otherwise.
enum class LogSeverity : int {
kInfo = 0,
kWarning = 1,
@@ -33,6 +73,8 @@ enum class LogSeverity : int {
kFatal = 3,
};
+// LogSeverities()
+//
// Returns an iterable of all standard `absl::LogSeverity` values, ordered from
// least to most severe.
constexpr std::array<absl::LogSeverity, 4> LogSeverities() {
@@ -40,8 +82,10 @@ constexpr std::array<absl::LogSeverity, 4> LogSeverities() {
absl::LogSeverity::kError, absl::LogSeverity::kFatal}};
}
+// LogSeverityName()
+//
// Returns the all-caps string representation (e.g. "INFO") of the specified
-// severity level if it is one of the normal levels and "UNKNOWN" otherwise.
+// 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"
@@ -52,6 +96,8 @@ constexpr const char* LogSeverityName(absl::LogSeverity s) {
: s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN";
}
+// NormalizeLogSeverity()
+//
// Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal`
// normalize to `kError` (**NOT** `kFatal`).
constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) {
@@ -60,14 +106,16 @@ constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) {
: s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s;
}
constexpr absl::LogSeverity NormalizeLogSeverity(int s) {
- return NormalizeLogSeverity(static_cast<absl::LogSeverity>(s));
+ return absl::NormalizeLogSeverity(static_cast<absl::LogSeverity>(s));
}
+// operator<<
+//
// The exact representation of a streamed `absl::LogSeverity` is deliberately
// unspecified; do not rely on it.
std::ostream& operator<<(std::ostream& os, absl::LogSeverity s);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_
diff --git a/absl/base/log_severity_test.cc b/absl/base/log_severity_test.cc
index 1de2d101..2302aa12 100644
--- a/absl/base/log_severity_test.cc
+++ b/absl/base/log_severity_test.cc
@@ -14,14 +14,26 @@
#include "absl/base/log_severity.h"
+#include <cstdint>
+#include <ios>
+#include <limits>
+#include <ostream>
#include <sstream>
#include <string>
+#include <tuple>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/flags/internal/flag.h"
+#include "absl/flags/marshalling.h"
+#include "absl/strings/str_cat.h"
namespace {
-using testing::Eq;
+using ::testing::Eq;
+using ::testing::IsFalse;
+using ::testing::IsTrue;
+using ::testing::TestWithParam;
+using ::testing::Values;
std::string StreamHelper(absl::LogSeverity value) {
std::ostringstream stream;
@@ -40,4 +52,153 @@ TEST(StreamTest, Works) {
Eq("absl::LogSeverity(4)"));
}
+static_assert(
+ absl::flags_internal::IsAtomicFlagTypeTrait<absl::LogSeverity>::value,
+ "Flags of type absl::LogSeverity ought to be lock-free.");
+
+using ParseFlagFromOutOfRangeIntegerTest = TestWithParam<int64_t>;
+INSTANTIATE_TEST_SUITE_P(
+ Instantiation, ParseFlagFromOutOfRangeIntegerTest,
+ Values(static_cast<int64_t>(std::numeric_limits<int>::min()) - 1,
+ static_cast<int64_t>(std::numeric_limits<int>::max()) + 1));
+TEST_P(ParseFlagFromOutOfRangeIntegerTest, ReturnsError) {
+ const std::string to_parse = absl::StrCat(GetParam());
+ absl::LogSeverity value;
+ std::string error;
+ EXPECT_THAT(absl::ParseFlag(to_parse, &value, &error), IsFalse()) << value;
+}
+
+using ParseFlagFromAlmostOutOfRangeIntegerTest = TestWithParam<int>;
+INSTANTIATE_TEST_SUITE_P(Instantiation,
+ ParseFlagFromAlmostOutOfRangeIntegerTest,
+ Values(std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()));
+TEST_P(ParseFlagFromAlmostOutOfRangeIntegerTest, YieldsExpectedValue) {
+ const auto expected = static_cast<absl::LogSeverity>(GetParam());
+ const std::string to_parse = absl::StrCat(GetParam());
+ absl::LogSeverity value;
+ std::string error;
+ ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error;
+ EXPECT_THAT(value, Eq(expected));
+}
+
+using ParseFlagFromIntegerMatchingEnumeratorTest =
+ TestWithParam<std::tuple<absl::string_view, absl::LogSeverity>>;
+INSTANTIATE_TEST_SUITE_P(
+ Instantiation, ParseFlagFromIntegerMatchingEnumeratorTest,
+ Values(std::make_tuple("0", absl::LogSeverity::kInfo),
+ std::make_tuple(" 0", absl::LogSeverity::kInfo),
+ std::make_tuple("-0", absl::LogSeverity::kInfo),
+ std::make_tuple("+0", absl::LogSeverity::kInfo),
+ std::make_tuple("00", absl::LogSeverity::kInfo),
+ std::make_tuple("0 ", absl::LogSeverity::kInfo),
+ std::make_tuple("0x0", absl::LogSeverity::kInfo),
+ std::make_tuple("1", absl::LogSeverity::kWarning),
+ std::make_tuple("+1", absl::LogSeverity::kWarning),
+ std::make_tuple("2", absl::LogSeverity::kError),
+ std::make_tuple("3", absl::LogSeverity::kFatal)));
+TEST_P(ParseFlagFromIntegerMatchingEnumeratorTest, YieldsExpectedValue) {
+ const absl::string_view to_parse = std::get<0>(GetParam());
+ const absl::LogSeverity expected = std::get<1>(GetParam());
+ absl::LogSeverity value;
+ std::string error;
+ ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error;
+ EXPECT_THAT(value, Eq(expected));
+}
+
+using ParseFlagFromOtherIntegerTest =
+ TestWithParam<std::tuple<absl::string_view, int>>;
+INSTANTIATE_TEST_SUITE_P(Instantiation, ParseFlagFromOtherIntegerTest,
+ Values(std::make_tuple("-1", -1),
+ std::make_tuple("4", 4),
+ std::make_tuple("010", 10),
+ std::make_tuple("0x10", 16)));
+TEST_P(ParseFlagFromOtherIntegerTest, YieldsExpectedValue) {
+ const absl::string_view to_parse = std::get<0>(GetParam());
+ const auto expected = static_cast<absl::LogSeverity>(std::get<1>(GetParam()));
+ absl::LogSeverity value;
+ std::string error;
+ ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error;
+ EXPECT_THAT(value, Eq(expected));
+}
+
+using ParseFlagFromEnumeratorTest =
+ TestWithParam<std::tuple<absl::string_view, absl::LogSeverity>>;
+INSTANTIATE_TEST_SUITE_P(
+ Instantiation, ParseFlagFromEnumeratorTest,
+ Values(std::make_tuple("INFO", absl::LogSeverity::kInfo),
+ std::make_tuple("info", absl::LogSeverity::kInfo),
+ std::make_tuple("kInfo", absl::LogSeverity::kInfo),
+ std::make_tuple("iNfO", absl::LogSeverity::kInfo),
+ std::make_tuple("kInFo", absl::LogSeverity::kInfo),
+ std::make_tuple("WARNING", absl::LogSeverity::kWarning),
+ std::make_tuple("warning", absl::LogSeverity::kWarning),
+ std::make_tuple("kWarning", absl::LogSeverity::kWarning),
+ std::make_tuple("WaRnInG", absl::LogSeverity::kWarning),
+ std::make_tuple("KwArNiNg", absl::LogSeverity::kWarning),
+ std::make_tuple("ERROR", absl::LogSeverity::kError),
+ std::make_tuple("error", absl::LogSeverity::kError),
+ std::make_tuple("kError", absl::LogSeverity::kError),
+ std::make_tuple("eRrOr", absl::LogSeverity::kError),
+ std::make_tuple("kErRoR", absl::LogSeverity::kError),
+ std::make_tuple("FATAL", absl::LogSeverity::kFatal),
+ 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)));
+TEST_P(ParseFlagFromEnumeratorTest, YieldsExpectedValue) {
+ const absl::string_view to_parse = std::get<0>(GetParam());
+ const absl::LogSeverity expected = std::get<1>(GetParam());
+ absl::LogSeverity value;
+ std::string error;
+ ASSERT_THAT(absl::ParseFlag(to_parse, &value, &error), IsTrue()) << error;
+ EXPECT_THAT(value, Eq(expected));
+}
+
+using ParseFlagFromGarbageTest = TestWithParam<absl::string_view>;
+INSTANTIATE_TEST_SUITE_P(Instantiation, ParseFlagFromGarbageTest,
+ Values("", "\0", " ", "garbage", "kkinfo", "I"));
+TEST_P(ParseFlagFromGarbageTest, ReturnsError) {
+ const absl::string_view to_parse = GetParam();
+ absl::LogSeverity value;
+ std::string error;
+ EXPECT_THAT(absl::ParseFlag(to_parse, &value, &error), IsFalse()) << value;
+}
+
+using UnparseFlagToEnumeratorTest =
+ TestWithParam<std::tuple<absl::LogSeverity, absl::string_view>>;
+INSTANTIATE_TEST_SUITE_P(
+ Instantiation, UnparseFlagToEnumeratorTest,
+ Values(std::make_tuple(absl::LogSeverity::kInfo, "INFO"),
+ std::make_tuple(absl::LogSeverity::kWarning, "WARNING"),
+ std::make_tuple(absl::LogSeverity::kError, "ERROR"),
+ std::make_tuple(absl::LogSeverity::kFatal, "FATAL")));
+TEST_P(UnparseFlagToEnumeratorTest, ReturnsExpectedValueAndRoundTrips) {
+ const absl::LogSeverity to_unparse = std::get<0>(GetParam());
+ const absl::string_view expected = std::get<1>(GetParam());
+ const std::string stringified_value = absl::UnparseFlag(to_unparse);
+ EXPECT_THAT(stringified_value, Eq(expected));
+ absl::LogSeverity reparsed_value;
+ std::string error;
+ EXPECT_THAT(absl::ParseFlag(stringified_value, &reparsed_value, &error),
+ IsTrue());
+ EXPECT_THAT(reparsed_value, Eq(to_unparse));
+}
+
+using UnparseFlagToOtherIntegerTest = TestWithParam<int>;
+INSTANTIATE_TEST_SUITE_P(Instantiation, UnparseFlagToOtherIntegerTest,
+ Values(std::numeric_limits<int>::min(), -1, 4,
+ std::numeric_limits<int>::max()));
+TEST_P(UnparseFlagToOtherIntegerTest, ReturnsExpectedValueAndRoundTrips) {
+ const absl::LogSeverity to_unparse =
+ static_cast<absl::LogSeverity>(GetParam());
+ const std::string expected = absl::StrCat(GetParam());
+ const std::string stringified_value = absl::UnparseFlag(to_unparse);
+ EXPECT_THAT(stringified_value, Eq(expected));
+ absl::LogSeverity reparsed_value;
+ std::string error;
+ EXPECT_THAT(absl::ParseFlag(stringified_value, &reparsed_value, &error),
+ IsTrue());
+ EXPECT_THAT(reparsed_value, Eq(to_unparse));
+}
} // namespace
diff --git a/absl/base/macros.h b/absl/base/macros.h
index 3121088a..547f93ba 100644
--- a/absl/base/macros.h
+++ b/absl/base/macros.h
@@ -31,6 +31,7 @@
#include <cassert>
#include <cstddef>
+#include "absl/base/attributes.h"
#include "absl/base/optimization.h"
#include "absl/base/port.h"
@@ -43,14 +44,14 @@
(sizeof(::absl::macros_internal::ArraySizeHelper(array)))
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace macros_internal {
// Note: this internal template function declaration is used by ABSL_ARRAYSIZE.
// The function doesn't need a definition, as we only use its type.
template <typename T, size_t N>
auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
} // namespace macros_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// kLinkerInitialized
@@ -74,13 +75,13 @@ auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
// // Invocation
// static MyClass my_global(absl::base_internal::kLinkerInitialized);
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
enum LinkerInitialized {
kLinkerInitialized = 0,
};
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// ABSL_FALLTHROUGH_INTENDED
@@ -111,7 +112,7 @@ enum LinkerInitialized {
// when performing switch labels fall-through diagnostic
// (`-Wimplicit-fallthrough`). See clang documentation on language extensions
// for details:
-// http://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough
+// https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough
//
// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro
// has no effect on diagnostics. In any case this macro has no effect on runtime
@@ -141,10 +142,15 @@ enum LinkerInitialized {
// declarations. The macro argument is used as a custom diagnostic message (e.g.
// suggestion of a better alternative).
//
-// Example:
+// Examples:
//
// class ABSL_DEPRECATED("Use Bar instead") Foo {...};
-// ABSL_DEPRECATED("Use Baz instead") void Bar() {...}
+//
+// ABSL_DEPRECATED("Use Baz() instead") void Bar() {...}
+//
+// template <typename T>
+// ABSL_DEPRECATED("Use DoThat() instead")
+// void DoThis();
//
// Every usage of a deprecated entity will trigger a warning when compiled with
// clang's `-Wdeprecated-declarations` option. This option is turned off by
@@ -162,7 +168,7 @@ enum LinkerInitialized {
// Used on a function overload to trap bad calls: any call that matches the
// overload will cause a compile-time error. This macro uses a clang-specific
// "enable_if" attribute, as described at
-// http://clang.llvm.org/docs/AttributeReference.html#enable-if
+// https://clang.llvm.org/docs/AttributeReference.html#enable-if
//
// Overloads which use this macro should be bracketed by
// `#ifdef ABSL_BAD_CALL_IF`.
@@ -175,12 +181,9 @@ enum LinkerInitialized {
// ABSL_BAD_CALL_IF(c <= -1 || c > 255,
// "'c' must have the value of an unsigned char or EOF");
// #endif // ABSL_BAD_CALL_IF
-
-#if defined(__clang__)
-# if __has_attribute(enable_if)
-# define ABSL_BAD_CALL_IF(expr, msg) \
- __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg)))
-# endif
+#if ABSL_HAVE_ATTRIBUTE(enable_if)
+#define ABSL_BAD_CALL_IF(expr, msg) \
+ __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg)))
#endif
// ABSL_ASSERT()
diff --git a/absl/base/optimization.h b/absl/base/optimization.h
index 0dcbef32..646523b3 100644
--- a/absl/base/optimization.h
+++ b/absl/base/optimization.h
@@ -172,7 +172,7 @@
#if ABSL_HAVE_BUILTIN(__builtin_expect) || \
(defined(__GNUC__) && !defined(__clang__))
#define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0))
-#define ABSL_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
+#define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true))
#else
#define ABSL_PREDICT_FALSE(x) (x)
#define ABSL_PREDICT_TRUE(x) (x)
diff --git a/absl/base/options.h b/absl/base/options.h
new file mode 100644
index 00000000..50f26e24
--- /dev/null
+++ b/absl/base/options.h
@@ -0,0 +1,211 @@
+#ifndef ABSL_BASE_OPTIONS_H_
+#define ABSL_BASE_OPTIONS_H_
+
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// 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: options.h
+// -----------------------------------------------------------------------------
+//
+// This file contains Abseil configuration options for setting specific
+// implementations instead of letting Abseil determine which implementation to
+// use at compile-time. Setting these options may be useful for package or build
+// managers who wish to guarantee ABI stability within binary builds (which are
+// otherwise difficult to enforce).
+//
+// *** IMPORTANT NOTICE FOR PACKAGE MANAGERS: It is important that
+// maintainers of package managers who wish to package Abseil read and
+// understand this file! ***
+//
+// Abseil contains a number of possible configuration endpoints, based on
+// parameters such as the detected platform, language version, or command-line
+// flags used to invoke the underlying binary. As is the case with all
+// libraries, binaries which contain Abseil code must ensure that separate
+// packages use the same compiled copy of Abseil to avoid a diamond dependency
+// problem, which can occur if two packages built with different Abseil
+// configuration settings are linked together. Diamond dependency problems in
+// C++ may manifest as violations to the One Definition Rule (ODR) (resulting in
+// linker errors), or undefined behavior (resulting in crashes).
+//
+// Diamond dependency problems can be avoided if all packages utilize the same
+// exact version of Abseil. Building from source code with the same compilation
+// parameters is the easiest way to avoid such dependency problems. However, for
+// package managers who cannot control such compilation parameters, we are
+// providing the file to allow you to inject ABI (Application Binary Interface)
+// stability across builds. Settings options in this file will neither change
+// API nor ABI, providing a stable copy of Abseil between packages.
+//
+// Care must be taken to keep options within these configurations isolated
+// from any other dynamic settings, such as command-line flags which could alter
+// these options. This file is provided specifically to help build and package
+// managers provide a stable copy of Abseil within their libraries and binaries;
+// other developers should not have need to alter the contents of this file.
+//
+// -----------------------------------------------------------------------------
+// Usage
+// -----------------------------------------------------------------------------
+//
+// For any particular package release, set the appropriate definitions within
+// this file to whatever value makes the most sense for your package(s). Note
+// that, by default, most of these options, at the moment, affect the
+// implementation of types; future options may affect other implementation
+// details.
+//
+// NOTE: the defaults within this file all assume that Abseil can select the
+// proper Abseil implementation at compile-time, which will not be sufficient
+// to guarantee ABI stability to package managers.
+
+// Include a standard library header to allow configuration based on the
+// standard library in use.
+#ifdef __cplusplus
+#include <ciso646>
+#endif
+
+// -----------------------------------------------------------------------------
+// Type Compatibility Options
+// -----------------------------------------------------------------------------
+//
+// ABSL_OPTION_USE_STD_ANY
+//
+// This option controls whether absl::any is implemented as an alias to
+// std::any, 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 an alias to std::any. This requires that all code
+// using Abseil is built in C++17 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if a working std::any is available. This option is
+// useful when you are building your entire program, including all of its
+// dependencies, from source. It should not be used otherwise -- for example,
+// if you are distributing Abseil in a binary package manager -- since in
+// mode 2, absl::any will name a different type, with a different mangled name
+// 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
+// absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY.
+
+#define ABSL_OPTION_USE_STD_ANY 2
+
+
+// ABSL_OPTION_USE_STD_OPTIONAL
+//
+// This option controls whether absl::optional is implemented as an alias to
+// std::optional, 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 an alias to std::optional. This requires that all
+// code using Abseil is built in C++17 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if a working std::optional is 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, absl::optional will name a different
+// type, with a different mangled name 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
+// absl::optional is a typedef of std::optional, use the feature macro
+// ABSL_USES_STD_OPTIONAL.
+
+#define ABSL_OPTION_USE_STD_OPTIONAL 2
+
+
+// ABSL_OPTION_USE_STD_STRING_VIEW
+//
+// This option controls whether absl::string_view is implemented as an alias to
+// std::string_view, 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 an alias to std::string_view. This requires that
+// all code using Abseil is built in C++17 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if a working std::string_view is 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, absl::string_view will name a
+// different type, with a different mangled name 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
+// absl::string_view is a typedef of std::string_view, use the feature macro
+// ABSL_USES_STD_STRING_VIEW.
+
+#define ABSL_OPTION_USE_STD_STRING_VIEW 2
+
+// ABSL_OPTION_USE_STD_VARIANT
+//
+// This option controls whether absl::variant is implemented as an alias to
+// std::variant, 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 an alias to std::variant. This requires that all
+// code using Abseil is built in C++17 mode or later.
+//
+// A value of 2 means to detect the C++ version being used to compile Abseil,
+// and use an alias only if a working std::variant is 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, absl::variant will name a different
+// type, with a different mangled name 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
+// absl::variant is a typedef of std::variant, use the feature macro
+// ABSL_USES_STD_VARIANT.
+
+#define ABSL_OPTION_USE_STD_VARIANT 2
+
+
+// ABSL_OPTION_USE_INLINE_NAMESPACE
+// ABSL_OPTION_INLINE_NAMESPACE_NAME
+//
+// These options controls whether all entities in the absl namespace are
+// contained within an inner inline namespace. This does not affect the
+// user-visible API of Abseil, but it changes the mangled names of all symbols.
+//
+// This can be useful as a version tag if you are distributing Abseil in
+// precompiled form. This will prevent a binary library build of Abseil with
+// one inline namespace being used with headers configured with a different
+// inline namespace name. Binary packagers are reminded that Abseil does not
+// guarantee any ABI stability in Abseil, so any update of Abseil or
+// configuration change in such a binary package should be combined with a
+// new, unique value for the inline namespace name.
+//
+// A value of 0 means not to use inline namespaces.
+//
+// A value of 1 means to use an inline namespace with the given name inside
+// namespace absl. If this is set, ABSL_OPTION_INLINE_NAMESPACE_NAME must also
+// be changed to a new, unique identifier name. In particular "head" is not
+// allowed.
+
+#define ABSL_OPTION_USE_INLINE_NAMESPACE 1
+#define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_2020_02_25
+
+#endif // ABSL_BASE_OPTIONS_H_
diff --git a/absl/base/policy_checks.h b/absl/base/policy_checks.h
index 699fb1a2..4dfa49e5 100644
--- a/absl/base/policy_checks.h
+++ b/absl/base/policy_checks.h
@@ -82,16 +82,6 @@
// Standard Library Check
// -----------------------------------------------------------------------------
-// We have chosen glibc 2.12 as the minimum as it was tagged for release
-// in May, 2010 and includes some functionality used in Google software
-// (for instance pthread_setname_np):
-// https://sourceware.org/ml/libc-alpha/2010-05/msg00000.html
-#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
-#if !__GLIBC_PREREQ(2, 12)
-#error "Minimum required version of glibc is 2.12."
-#endif
-#endif
-
#if defined(_STLPORT_VERSION)
#error "STLPort is not supported."
#endif
diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc
index 06860e71..08f61ba8 100644
--- a/absl/base/spinlock_test_common.cc
+++ b/absl/base/spinlock_test_common.cc
@@ -36,7 +36,7 @@ constexpr int32_t kNumThreads = 10;
constexpr int32_t kIters = 1000;
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// This is defined outside of anonymous namespace so that it can be
@@ -267,5 +267,5 @@ TEST(SpinLockWithThreads, DoesNotDeadlock) {
} // namespace
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h
index f3e96589..5f51c0c2 100644
--- a/absl/base/thread_annotations.h
+++ b/absl/base/thread_annotations.h
@@ -34,6 +34,7 @@
#ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_
#define ABSL_BASE_THREAD_ANNOTATIONS_H_
+#include "absl/base/config.h"
// TODO(mbonadei): Remove after the backward compatibility period.
#include "absl/base/internal/thread_annotations.h" // IWYU pragma: export
@@ -256,7 +257,7 @@
#define ABSL_TS_UNCHECKED_READ(x) absl::base_internal::ts_unchecked_read(x)
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace base_internal {
// Takes a reference to a guarded data member, and returns an unguarded
@@ -273,7 +274,7 @@ inline T& ts_unchecked_read(T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS {
}
} // namespace base_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_THREAD_ANNOTATIONS_H_
diff --git a/absl/base/throw_delegate_test.cc b/absl/base/throw_delegate_test.cc
index a74dd3cd..5ba4ce55 100644
--- a/absl/base/throw_delegate_test.cc
+++ b/absl/base/throw_delegate_test.cc
@@ -18,6 +18,7 @@
#include <new>
#include <stdexcept>
+#include "absl/base/config.h"
#include "gtest/gtest.h"
namespace {
@@ -38,31 +39,43 @@ constexpr const char* what_arg = "The quick brown fox jumps over the lazy dog";
template <typename E>
void ExpectThrowChar(void (*f)(const char*)) {
+#ifdef ABSL_HAVE_EXCEPTIONS
try {
f(what_arg);
FAIL() << "Didn't throw";
} catch (const E& e) {
EXPECT_STREQ(e.what(), what_arg);
}
+#else
+ EXPECT_DEATH_IF_SUPPORTED(f(what_arg), what_arg);
+#endif
}
template <typename E>
void ExpectThrowString(void (*f)(const std::string&)) {
+#ifdef ABSL_HAVE_EXCEPTIONS
try {
f(what_arg);
FAIL() << "Didn't throw";
} catch (const E& e) {
EXPECT_STREQ(e.what(), what_arg);
}
+#else
+ EXPECT_DEATH_IF_SUPPORTED(f(what_arg), what_arg);
+#endif
}
template <typename E>
void ExpectThrowNoWhat(void (*f)()) {
+#ifdef ABSL_HAVE_EXCEPTIONS
try {
f();
FAIL() << "Didn't throw";
} catch (const E& e) {
}
+#else
+ EXPECT_DEATH_IF_SUPPORTED(f(), "");
+#endif
}
TEST(ThrowHelper, Test) {
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel
index 9e2a5b1e..f2217140 100644
--- a/absl/container/BUILD.bazel
+++ b/absl/container/BUILD.bazel
@@ -14,12 +14,11 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
"ABSL_DEFAULT_LINKOPTS",
- "ABSL_EXCEPTIONS_FLAG",
- "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
"ABSL_TEST_COPTS",
)
@@ -71,20 +70,6 @@ cc_library(
cc_test(
name = "fixed_array_test",
srcs = ["fixed_array_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
- deps = [
- ":fixed_array",
- "//absl/base:exception_testing",
- "//absl/hash:hash_testing",
- "//absl/memory",
- "@com_google_googletest//:gtest_main",
- ],
-)
-
-cc_test(
- name = "fixed_array_test_noexceptions",
- srcs = ["fixed_array_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
@@ -99,10 +84,11 @@ cc_test(
cc_test(
name = "fixed_array_exception_safety_test",
srcs = ["fixed_array_exception_safety_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":fixed_array",
+ "//absl/base:config",
"//absl/base:exception_safety_testing",
"@com_google_googletest//:gtest_main",
],
@@ -155,39 +141,21 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"],
+ deps = ["//absl/base:config"],
)
cc_test(
name = "inlined_vector_test",
srcs = ["inlined_vector_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
- deps = [
- ":counting_allocator",
- ":inlined_vector",
- ":test_instance_tracker",
- "//absl/base",
- "//absl/base:core_headers",
- "//absl/base:exception_testing",
- "//absl/hash:hash_testing",
- "//absl/memory",
- "//absl/strings",
- "@com_google_googletest//:gtest_main",
- ],
-)
-
-cc_test(
- name = "inlined_vector_test_noexceptions",
- srcs = ["inlined_vector_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":counting_allocator",
":inlined_vector",
":test_instance_tracker",
- "//absl/base",
"//absl/base:core_headers",
"//absl/base:exception_testing",
+ "//absl/base:raw_logging_internal",
"//absl/hash:hash_testing",
"//absl/memory",
"//absl/strings",
@@ -203,8 +171,8 @@ cc_test(
tags = ["benchmark"],
deps = [
":inlined_vector",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
"@com_github_google_benchmark//:benchmark_main",
],
@@ -213,9 +181,10 @@ cc_test(
cc_test(
name = "inlined_vector_exception_safety_test",
srcs = ["inlined_vector_exception_safety_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ copts = ABSL_TEST_COPTS,
deps = [
":inlined_vector",
+ "//absl/base:config",
"//absl/base:exception_safety_testing",
"@com_google_googletest//:gtest_main",
],
@@ -447,6 +416,7 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":hash_policy_testing",
+ "//absl/memory",
"//absl/meta:type_traits",
"//absl/strings",
],
@@ -509,6 +479,9 @@ cc_library(
hdrs = ["internal/hashtable_debug_hooks.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:config",
+ ],
)
cc_library(
@@ -524,6 +497,7 @@ cc_library(
":have_sse",
"//absl/base",
"//absl/base:core_headers",
+ "//absl/base:exponential_biased",
"//absl/debugging:stacktrace",
"//absl/memory",
"//absl/synchronization",
@@ -551,6 +525,7 @@ cc_library(
hdrs = ["internal/node_hash_policy.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = ["//absl/base:config"],
)
cc_test(
@@ -635,6 +610,7 @@ cc_test(
":raw_hash_set",
"//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
@@ -678,8 +654,8 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":layout",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/types:span",
"@com_google_googletest//:gtest_main",
],
@@ -691,6 +667,9 @@ cc_library(
hdrs = ["internal/tracked.h"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:config",
+ ],
)
cc_library(
@@ -825,3 +804,99 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
+
+cc_library(
+ name = "btree",
+ srcs = [
+ "internal/btree.h",
+ "internal/btree_container.h",
+ ],
+ hdrs = [
+ "btree_map.h",
+ "btree_set.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = ["//visibility:public"],
+ deps = [
+ ":common",
+ ":compressed_tuple",
+ ":container_memory",
+ ":layout",
+ "//absl/base:core_headers",
+ "//absl/base:throw_delegate",
+ "//absl/memory",
+ "//absl/meta:type_traits",
+ "//absl/strings",
+ "//absl/types:compare",
+ "//absl/utility",
+ ],
+)
+
+cc_library(
+ name = "btree_test_common",
+ testonly = 1,
+ hdrs = ["btree_test.h"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":btree",
+ ":flat_hash_set",
+ "//absl/strings",
+ "//absl/time",
+ ],
+)
+
+cc_test(
+ name = "btree_test",
+ size = "large",
+ srcs = [
+ "btree_test.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ shard_count = 10,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":btree",
+ ":btree_test_common",
+ ":counting_allocator",
+ ":test_instance_tracker",
+ "//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
+ "//absl/flags:flag",
+ "//absl/hash:hash_testing",
+ "//absl/memory",
+ "//absl/meta:type_traits",
+ "//absl/strings",
+ "//absl/types:compare",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_binary(
+ name = "btree_benchmark",
+ testonly = 1,
+ srcs = [
+ "btree_benchmark.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":btree",
+ ":btree_test_common",
+ ":flat_hash_map",
+ ":flat_hash_set",
+ ":hashtable_debug",
+ "//absl/base:raw_logging_internal",
+ "//absl/flags:flag",
+ "//absl/hash",
+ "//absl/memory",
+ "//absl/strings:str_format",
+ "//absl/time",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt
index 7988b12f..e702ba85 100644
--- a/absl/container/CMakeLists.txt
+++ b/absl/container/CMakeLists.txt
@@ -25,6 +25,73 @@ absl_cc_library(
absl_cc_library(
NAME
+ btree
+ HDRS
+ "btree_map.h"
+ "btree_set.h"
+ "internal/btree.h"
+ "internal/btree_container.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::container_common
+ absl::compare
+ absl::compressed_tuple
+ absl::container_memory
+ absl::core_headers
+ absl::layout
+ absl::memory
+ absl::strings
+ absl::throw_delegate
+ absl::type_traits
+ absl::utility
+)
+
+absl_cc_library(
+ NAME
+ btree_test_common
+ hdrs
+ "btree_test.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::btree
+ absl::flat_hash_set
+ absl::strings
+ absl::time
+ TESTONLY
+)
+
+absl_cc_test(
+ NAME
+ btree_test
+ SRCS
+ "btree_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::btree
+ absl::btree_test_common
+ absl::compare
+ absl::core_headers
+ absl::counting_allocator
+ absl::flags
+ absl::hash_testing
+ absl::raw_logging_internal
+ absl::strings
+ absl::test_instance_tracker
+ absl::type_traits
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
compressed_tuple
HDRS
"internal/compressed_tuple.h"
@@ -76,24 +143,6 @@ absl_cc_test(
"fixed_array_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
- DEPS
- absl::fixed_array
- absl::exception_testing
- absl::hash_testing
- absl::memory
- gmock_main
-)
-
-absl_cc_test(
- NAME
- fixed_array_test_noexceptions
- SRCS
- "fixed_array_test.cc"
- COPTS
- ${ABSL_TEST_COPTS}
DEPS
absl::fixed_array
absl::exception_testing
@@ -109,11 +158,9 @@ absl_cc_test(
"fixed_array_exception_safety_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::fixed_array
+ absl::config
absl::exception_safety_testing
gmock_main
)
@@ -157,6 +204,8 @@ absl_cc_library(
"internal/counting_allocator.h"
COPTS
${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
)
absl_cc_test(
@@ -166,37 +215,15 @@ absl_cc_test(
"inlined_vector_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::counting_allocator
absl::inlined_vector
absl::test_instance_tracker
- absl::base
- absl::core_headers
- absl::exception_testing
- absl::hash_testing
- absl::memory
- absl::strings
- gmock_main
-)
-
-absl_cc_test(
- NAME
- inlined_vector_test_noexceptions
- SRCS
- "inlined_vector_test.cc"
- COPTS
- ${ABSL_TEST_COPTS}
- DEPS
- absl::inlined_vector
- absl::test_instance_tracker
- absl::base
absl::core_headers
absl::exception_testing
absl::hash_testing
absl::memory
+ absl::raw_logging_internal
absl::strings
gmock_main
)
@@ -208,11 +235,9 @@ absl_cc_test(
"inlined_vector_exception_safety_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::inlined_vector
+ absl::config
absl::exception_safety_testing
gmock_main
)
@@ -448,6 +473,7 @@ absl_cc_library(
${ABSL_TEST_COPTS}
DEPS
absl::hash_policy_testing
+ absl::memory
absl::meta
absl::strings
TESTONLY
@@ -514,6 +540,7 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::base
+ absl::exponential_biased
absl::have_sse
absl::synchronization
)
@@ -549,6 +576,8 @@ absl_cc_library(
"internal/hashtable_debug_hooks.h"
COPTS
${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
PUBLIC
)
@@ -568,6 +597,8 @@ absl_cc_library(
"internal/node_hash_policy.h"
COPTS
${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
PUBLIC
)
@@ -602,7 +633,7 @@ absl_cc_library(
NAME
container_common
HDRS
- "internal/commom.h"
+ "internal/common.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
@@ -653,6 +684,7 @@ absl_cc_test(
absl::raw_hash_set
absl::base
absl::core_headers
+ absl::raw_logging_internal
absl::strings
gmock_main
)
@@ -696,8 +728,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::layout
- absl::base
absl::core_headers
+ absl::raw_logging_internal
absl::span
gmock_main
)
@@ -709,6 +741,8 @@ absl_cc_library(
"internal/tracked.h"
COPTS
${ABSL_TEST_COPTS}
+ DEPS
+ absl::config
TESTONLY
)
diff --git a/absl/container/btree_benchmark.cc b/absl/container/btree_benchmark.cc
new file mode 100644
index 00000000..4af92f9f
--- /dev/null
+++ b/absl/container/btree_benchmark.cc
@@ -0,0 +1,707 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <functional>
+#include <map>
+#include <numeric>
+#include <random>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "absl/base/internal/raw_logging.h"
+#include "absl/container/btree_map.h"
+#include "absl/container/btree_set.h"
+#include "absl/container/btree_test.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/container/internal/hashtable_debug.h"
+#include "absl/flags/flag.h"
+#include "absl/hash/hash.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/str_format.h"
+#include "absl/time/time.h"
+#include "benchmark/benchmark.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+namespace {
+
+constexpr size_t kBenchmarkValues = 1 << 20;
+
+// How many times we add and remove sub-batches in one batch of *AddRem
+// benchmarks.
+constexpr size_t kAddRemBatchSize = 1 << 2;
+
+// Generates n values in the range [0, 4 * n].
+template <typename V>
+std::vector<V> GenerateValues(int n) {
+ constexpr int kSeed = 23;
+ return GenerateValuesWithSeed<V>(n, 4 * n, kSeed);
+}
+
+// Benchmark insertion of values into a container.
+template <typename T>
+void BM_InsertImpl(benchmark::State& state, bool sorted) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ typename KeyOfValue<typename T::key_type, V>::type key_of_value;
+
+ std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
+ if (sorted) {
+ std::sort(values.begin(), values.end());
+ }
+ T container(values.begin(), values.end());
+
+ // Remove and re-insert 10% of the keys per batch.
+ const int batch_size = (kBenchmarkValues + 9) / 10;
+ while (state.KeepRunningBatch(batch_size)) {
+ state.PauseTiming();
+ const auto i = static_cast<int>(state.iterations());
+
+ for (int j = i; j < i + batch_size; j++) {
+ int x = j % kBenchmarkValues;
+ container.erase(key_of_value(values[x]));
+ }
+
+ state.ResumeTiming();
+
+ for (int j = i; j < i + batch_size; j++) {
+ int x = j % kBenchmarkValues;
+ container.insert(values[x]);
+ }
+ }
+}
+
+template <typename T>
+void BM_Insert(benchmark::State& state) {
+ BM_InsertImpl<T>(state, false);
+}
+
+template <typename T>
+void BM_InsertSorted(benchmark::State& state) {
+ BM_InsertImpl<T>(state, true);
+}
+
+// container::insert sometimes returns a pair<iterator, bool> and sometimes
+// returns an iterator (for multi- containers).
+template <typename Iter>
+Iter GetIterFromInsert(const std::pair<Iter, bool>& pair) {
+ return pair.first;
+}
+template <typename Iter>
+Iter GetIterFromInsert(const Iter iter) {
+ return iter;
+}
+
+// Benchmark insertion of values into a container at the end.
+template <typename T>
+void BM_InsertEnd(benchmark::State& state) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ typename KeyOfValue<typename T::key_type, V>::type key_of_value;
+
+ T container;
+ const int kSize = 10000;
+ for (int i = 0; i < kSize; ++i) {
+ container.insert(Generator<V>(kSize)(i));
+ }
+ V v = Generator<V>(kSize)(kSize - 1);
+ typename T::key_type k = key_of_value(v);
+
+ auto it = container.find(k);
+ while (state.KeepRunning()) {
+ // Repeatedly removing then adding v.
+ container.erase(it);
+ it = GetIterFromInsert(container.insert(v));
+ }
+}
+
+template <typename T>
+void BM_LookupImpl(benchmark::State& state, bool sorted) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ typename KeyOfValue<typename T::key_type, V>::type key_of_value;
+
+ std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
+ if (sorted) {
+ std::sort(values.begin(), values.end());
+ }
+ T container(values.begin(), values.end());
+
+ while (state.KeepRunning()) {
+ int idx = state.iterations() % kBenchmarkValues;
+ benchmark::DoNotOptimize(container.find(key_of_value(values[idx])));
+ }
+}
+
+// Benchmark lookup of values in a container.
+template <typename T>
+void BM_Lookup(benchmark::State& state) {
+ BM_LookupImpl<T>(state, false);
+}
+
+// Benchmark lookup of values in a full container, meaning that values
+// are inserted in-order to take advantage of biased insertion, which
+// yields a full tree.
+template <typename T>
+void BM_FullLookup(benchmark::State& state) {
+ BM_LookupImpl<T>(state, true);
+}
+
+// Benchmark deletion of values from a container.
+template <typename T>
+void BM_Delete(benchmark::State& state) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ typename KeyOfValue<typename T::key_type, V>::type key_of_value;
+ std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
+ T container(values.begin(), values.end());
+
+ // Remove and re-insert 10% of the keys per batch.
+ const int batch_size = (kBenchmarkValues + 9) / 10;
+ while (state.KeepRunningBatch(batch_size)) {
+ const int i = state.iterations();
+
+ for (int j = i; j < i + batch_size; j++) {
+ int x = j % kBenchmarkValues;
+ container.erase(key_of_value(values[x]));
+ }
+
+ state.PauseTiming();
+ for (int j = i; j < i + batch_size; j++) {
+ int x = j % kBenchmarkValues;
+ container.insert(values[x]);
+ }
+ state.ResumeTiming();
+ }
+}
+
+// Benchmark deletion of multiple values from a container.
+template <typename T>
+void BM_DeleteRange(benchmark::State& state) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ typename KeyOfValue<typename T::key_type, V>::type key_of_value;
+ std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
+ T container(values.begin(), values.end());
+
+ // Remove and re-insert 10% of the keys per batch.
+ const int batch_size = (kBenchmarkValues + 9) / 10;
+ while (state.KeepRunningBatch(batch_size)) {
+ const int i = state.iterations();
+
+ const int start_index = i % kBenchmarkValues;
+
+ state.PauseTiming();
+ {
+ std::vector<V> removed;
+ removed.reserve(batch_size);
+ auto itr = container.find(key_of_value(values[start_index]));
+ auto start = itr;
+ for (int j = 0; j < batch_size; j++) {
+ if (itr == container.end()) {
+ state.ResumeTiming();
+ container.erase(start, itr);
+ state.PauseTiming();
+ itr = container.begin();
+ start = itr;
+ }
+ removed.push_back(*itr++);
+ }
+
+ state.ResumeTiming();
+ container.erase(start, itr);
+ state.PauseTiming();
+
+ container.insert(removed.begin(), removed.end());
+ }
+ state.ResumeTiming();
+ }
+}
+
+// Benchmark steady-state insert (into first half of range) and remove (from
+// second half of range), treating the container approximately like a queue with
+// log-time access for all elements. This benchmark does not test the case where
+// insertion and removal happen in the same region of the tree. This benchmark
+// counts two value constructors.
+template <typename T>
+void BM_QueueAddRem(benchmark::State& state) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ typename KeyOfValue<typename T::key_type, V>::type key_of_value;
+
+ ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance");
+
+ T container;
+
+ const size_t half = kBenchmarkValues / 2;
+ std::vector<int> remove_keys(half);
+ std::vector<int> add_keys(half);
+
+ // We want to do the exact same work repeatedly, and the benchmark can end
+ // after a different number of iterations depending on the speed of the
+ // individual run so we use a large batch size here and ensure that we do
+ // deterministic work every batch.
+ while (state.KeepRunningBatch(half * kAddRemBatchSize)) {
+ state.PauseTiming();
+
+ container.clear();
+
+ for (size_t i = 0; i < half; ++i) {
+ remove_keys[i] = i;
+ add_keys[i] = i;
+ }
+ constexpr int kSeed = 5;
+ std::mt19937_64 rand(kSeed);
+ std::shuffle(remove_keys.begin(), remove_keys.end(), rand);
+ std::shuffle(add_keys.begin(), add_keys.end(), rand);
+
+ // Note needs lazy generation of values.
+ Generator<V> g(kBenchmarkValues * kAddRemBatchSize);
+
+ for (size_t i = 0; i < half; ++i) {
+ container.insert(g(add_keys[i]));
+ container.insert(g(half + remove_keys[i]));
+ }
+
+ // There are three parts each of size "half":
+ // 1 is being deleted from [offset - half, offset)
+ // 2 is standing [offset, offset + half)
+ // 3 is being inserted into [offset + half, offset + 2 * half)
+ size_t offset = 0;
+
+ for (size_t i = 0; i < kAddRemBatchSize; ++i) {
+ std::shuffle(remove_keys.begin(), remove_keys.end(), rand);
+ std::shuffle(add_keys.begin(), add_keys.end(), rand);
+ offset += half;
+
+ state.ResumeTiming();
+ for (size_t idx = 0; idx < half; ++idx) {
+ container.erase(key_of_value(g(offset - half + remove_keys[idx])));
+ container.insert(g(offset + half + add_keys[idx]));
+ }
+ state.PauseTiming();
+ }
+ state.ResumeTiming();
+ }
+}
+
+// Mixed insertion and deletion in the same range using pre-constructed values.
+template <typename T>
+void BM_MixedAddRem(benchmark::State& state) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ typename KeyOfValue<typename T::key_type, V>::type key_of_value;
+
+ ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance");
+
+ T container;
+
+ // Create two random shuffles
+ std::vector<int> remove_keys(kBenchmarkValues);
+ std::vector<int> add_keys(kBenchmarkValues);
+
+ // We want to do the exact same work repeatedly, and the benchmark can end
+ // after a different number of iterations depending on the speed of the
+ // individual run so we use a large batch size here and ensure that we do
+ // deterministic work every batch.
+ while (state.KeepRunningBatch(kBenchmarkValues * kAddRemBatchSize)) {
+ state.PauseTiming();
+
+ container.clear();
+
+ constexpr int kSeed = 7;
+ std::mt19937_64 rand(kSeed);
+
+ std::vector<V> values = GenerateValues<V>(kBenchmarkValues * 2);
+
+ // Insert the first half of the values (already in random order)
+ container.insert(values.begin(), values.begin() + kBenchmarkValues);
+
+ // Insert the first half of the values (already in random order)
+ for (size_t i = 0; i < kBenchmarkValues; ++i) {
+ // remove_keys and add_keys will be swapped before each round,
+ // therefore fill add_keys here w/ the keys being inserted, so
+ // they'll be the first to be removed.
+ remove_keys[i] = i + kBenchmarkValues;
+ add_keys[i] = i;
+ }
+
+ for (size_t i = 0; i < kAddRemBatchSize; ++i) {
+ remove_keys.swap(add_keys);
+ std::shuffle(remove_keys.begin(), remove_keys.end(), rand);
+ std::shuffle(add_keys.begin(), add_keys.end(), rand);
+
+ state.ResumeTiming();
+ for (size_t idx = 0; idx < kBenchmarkValues; ++idx) {
+ container.erase(key_of_value(values[remove_keys[idx]]));
+ container.insert(values[add_keys[idx]]);
+ }
+ state.PauseTiming();
+ }
+ state.ResumeTiming();
+ }
+}
+
+// Insertion at end, removal from the beginning. This benchmark
+// counts two value constructors.
+// TODO(ezb): we could add a GenerateNext version of generator that could reduce
+// noise for string-like types.
+template <typename T>
+void BM_Fifo(benchmark::State& state) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+
+ T container;
+ // Need lazy generation of values as state.max_iterations is large.
+ Generator<V> g(kBenchmarkValues + state.max_iterations);
+
+ for (int i = 0; i < kBenchmarkValues; i++) {
+ container.insert(g(i));
+ }
+
+ while (state.KeepRunning()) {
+ container.erase(container.begin());
+ container.insert(container.end(), g(state.iterations() + kBenchmarkValues));
+ }
+}
+
+// Iteration (forward) through the tree
+template <typename T>
+void BM_FwdIter(benchmark::State& state) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ using R = typename T::value_type const*;
+
+ std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
+ T container(values.begin(), values.end());
+
+ auto iter = container.end();
+
+ R r = nullptr;
+
+ while (state.KeepRunning()) {
+ if (iter == container.end()) iter = container.begin();
+ r = &(*iter);
+ ++iter;
+ }
+
+ benchmark::DoNotOptimize(r);
+}
+
+// Benchmark random range-construction of a container.
+template <typename T>
+void BM_RangeConstructionImpl(benchmark::State& state, bool sorted) {
+ using V = typename remove_pair_const<typename T::value_type>::type;
+
+ std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
+ if (sorted) {
+ std::sort(values.begin(), values.end());
+ }
+ {
+ T container(values.begin(), values.end());
+ }
+
+ while (state.KeepRunning()) {
+ T container(values.begin(), values.end());
+ benchmark::DoNotOptimize(container);
+ }
+}
+
+template <typename T>
+void BM_InsertRangeRandom(benchmark::State& state) {
+ BM_RangeConstructionImpl<T>(state, false);
+}
+
+template <typename T>
+void BM_InsertRangeSorted(benchmark::State& state) {
+ BM_RangeConstructionImpl<T>(state, true);
+}
+
+#define STL_ORDERED_TYPES(value) \
+ using stl_set_##value = std::set<value>; \
+ using stl_map_##value = std::map<value, intptr_t>; \
+ using stl_multiset_##value = std::multiset<value>; \
+ using stl_multimap_##value = std::multimap<value, intptr_t>
+
+using StdString = std::string;
+STL_ORDERED_TYPES(int32_t);
+STL_ORDERED_TYPES(int64_t);
+STL_ORDERED_TYPES(StdString);
+STL_ORDERED_TYPES(Time);
+
+#define STL_UNORDERED_TYPES(value) \
+ using stl_unordered_set_##value = std::unordered_set<value>; \
+ using stl_unordered_map_##value = std::unordered_map<value, intptr_t>; \
+ using flat_hash_set_##value = flat_hash_set<value>; \
+ using flat_hash_map_##value = flat_hash_map<value, intptr_t>; \
+ using stl_unordered_multiset_##value = std::unordered_multiset<value>; \
+ using stl_unordered_multimap_##value = \
+ std::unordered_multimap<value, intptr_t>
+
+#define STL_UNORDERED_TYPES_CUSTOM_HASH(value, hash) \
+ using stl_unordered_set_##value = std::unordered_set<value, hash>; \
+ using stl_unordered_map_##value = std::unordered_map<value, intptr_t, hash>; \
+ using flat_hash_set_##value = flat_hash_set<value, hash>; \
+ using flat_hash_map_##value = flat_hash_map<value, intptr_t, hash>; \
+ using stl_unordered_multiset_##value = std::unordered_multiset<value, hash>; \
+ using stl_unordered_multimap_##value = \
+ std::unordered_multimap<value, intptr_t, hash>
+
+STL_UNORDERED_TYPES(int32_t);
+STL_UNORDERED_TYPES(int64_t);
+STL_UNORDERED_TYPES(StdString);
+STL_UNORDERED_TYPES_CUSTOM_HASH(Time, absl::Hash<absl::Time>);
+
+#define BTREE_TYPES(value) \
+ using btree_256_set_##value = \
+ btree_set<value, std::less<value>, std::allocator<value>>; \
+ using btree_256_map_##value = \
+ btree_map<value, intptr_t, std::less<value>, \
+ std::allocator<std::pair<const value, intptr_t>>>; \
+ using btree_256_multiset_##value = \
+ btree_multiset<value, std::less<value>, std::allocator<value>>; \
+ using btree_256_multimap_##value = \
+ btree_multimap<value, intptr_t, std::less<value>, \
+ std::allocator<std::pair<const value, intptr_t>>>
+
+BTREE_TYPES(int32_t);
+BTREE_TYPES(int64_t);
+BTREE_TYPES(StdString);
+BTREE_TYPES(Time);
+
+#define MY_BENCHMARK4(type, func) \
+ void BM_##type##_##func(benchmark::State& state) { BM_##func<type>(state); } \
+ BENCHMARK(BM_##type##_##func)
+
+#define MY_BENCHMARK3(type) \
+ MY_BENCHMARK4(type, Insert); \
+ MY_BENCHMARK4(type, InsertSorted); \
+ MY_BENCHMARK4(type, InsertEnd); \
+ MY_BENCHMARK4(type, Lookup); \
+ MY_BENCHMARK4(type, FullLookup); \
+ MY_BENCHMARK4(type, Delete); \
+ MY_BENCHMARK4(type, DeleteRange); \
+ MY_BENCHMARK4(type, QueueAddRem); \
+ MY_BENCHMARK4(type, MixedAddRem); \
+ MY_BENCHMARK4(type, Fifo); \
+ MY_BENCHMARK4(type, FwdIter); \
+ MY_BENCHMARK4(type, InsertRangeRandom); \
+ MY_BENCHMARK4(type, InsertRangeSorted)
+
+#define MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type) \
+ MY_BENCHMARK3(stl_##type); \
+ MY_BENCHMARK3(stl_unordered_##type); \
+ MY_BENCHMARK3(btree_256_##type)
+
+#define MY_BENCHMARK2(type) \
+ MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type); \
+ MY_BENCHMARK3(flat_hash_##type)
+
+// Define MULTI_TESTING to see benchmarks for multi-containers also.
+//
+// You can use --copt=-DMULTI_TESTING.
+#ifdef MULTI_TESTING
+#define MY_BENCHMARK(type) \
+ MY_BENCHMARK2(set_##type); \
+ MY_BENCHMARK2(map_##type); \
+ MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multiset_##type); \
+ MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multimap_##type)
+#else
+#define MY_BENCHMARK(type) \
+ MY_BENCHMARK2(set_##type); \
+ MY_BENCHMARK2(map_##type)
+#endif
+
+MY_BENCHMARK(int32_t);
+MY_BENCHMARK(int64_t);
+MY_BENCHMARK(StdString);
+MY_BENCHMARK(Time);
+
+// Define a type whose size and cost of moving are independently customizable.
+// When sizeof(value_type) increases, we expect btree to no longer have as much
+// cache-locality advantage over STL. When cost of moving increases, we expect
+// btree to actually do more work than STL because it has to move values around
+// and STL doesn't have to.
+template <int Size, int Copies>
+struct BigType {
+ BigType() : BigType(0) {}
+ explicit BigType(int x) { std::iota(values.begin(), values.end(), x); }
+
+ void Copy(const BigType& x) {
+ for (int i = 0; i < Size && i < Copies; ++i) values[i] = x.values[i];
+ // If Copies > Size, do extra copies.
+ for (int i = Size, idx = 0; i < Copies; ++i) {
+ int64_t tmp = x.values[idx];
+ benchmark::DoNotOptimize(tmp);
+ idx = idx + 1 == Size ? 0 : idx + 1;
+ }
+ }
+
+ BigType(const BigType& x) { Copy(x); }
+ BigType& operator=(const BigType& x) {
+ Copy(x);
+ return *this;
+ }
+
+ // Compare only the first Copies elements if Copies is less than Size.
+ bool operator<(const BigType& other) const {
+ return std::lexicographical_compare(
+ values.begin(), values.begin() + std::min(Size, Copies),
+ other.values.begin(), other.values.begin() + std::min(Size, Copies));
+ }
+ bool operator==(const BigType& other) const {
+ return std::equal(values.begin(), values.begin() + std::min(Size, Copies),
+ other.values.begin());
+ }
+
+ // Support absl::Hash.
+ template <typename State>
+ friend State AbslHashValue(State h, const BigType& b) {
+ for (int i = 0; i < Size && i < Copies; ++i)
+ h = State::combine(std::move(h), b.values[i]);
+ return h;
+ }
+
+ std::array<int64_t, Size> values;
+};
+
+#define BIG_TYPE_BENCHMARKS(SIZE, COPIES) \
+ using stl_set_size##SIZE##copies##COPIES = std::set<BigType<SIZE, COPIES>>; \
+ using stl_map_size##SIZE##copies##COPIES = \
+ std::map<BigType<SIZE, COPIES>, intptr_t>; \
+ using stl_multiset_size##SIZE##copies##COPIES = \
+ std::multiset<BigType<SIZE, COPIES>>; \
+ using stl_multimap_size##SIZE##copies##COPIES = \
+ std::multimap<BigType<SIZE, COPIES>, intptr_t>; \
+ using stl_unordered_set_size##SIZE##copies##COPIES = \
+ std::unordered_set<BigType<SIZE, COPIES>, \
+ absl::Hash<BigType<SIZE, COPIES>>>; \
+ using stl_unordered_map_size##SIZE##copies##COPIES = \
+ std::unordered_map<BigType<SIZE, COPIES>, intptr_t, \
+ absl::Hash<BigType<SIZE, COPIES>>>; \
+ using flat_hash_set_size##SIZE##copies##COPIES = \
+ flat_hash_set<BigType<SIZE, COPIES>>; \
+ using flat_hash_map_size##SIZE##copies##COPIES = \
+ flat_hash_map<BigType<SIZE, COPIES>, intptr_t>; \
+ using stl_unordered_multiset_size##SIZE##copies##COPIES = \
+ std::unordered_multiset<BigType<SIZE, COPIES>, \
+ absl::Hash<BigType<SIZE, COPIES>>>; \
+ using stl_unordered_multimap_size##SIZE##copies##COPIES = \
+ std::unordered_multimap<BigType<SIZE, COPIES>, intptr_t, \
+ absl::Hash<BigType<SIZE, COPIES>>>; \
+ using btree_256_set_size##SIZE##copies##COPIES = \
+ btree_set<BigType<SIZE, COPIES>>; \
+ using btree_256_map_size##SIZE##copies##COPIES = \
+ btree_map<BigType<SIZE, COPIES>, intptr_t>; \
+ using btree_256_multiset_size##SIZE##copies##COPIES = \
+ btree_multiset<BigType<SIZE, COPIES>>; \
+ using btree_256_multimap_size##SIZE##copies##COPIES = \
+ btree_multimap<BigType<SIZE, COPIES>, intptr_t>; \
+ MY_BENCHMARK(size##SIZE##copies##COPIES)
+
+// Define BIG_TYPE_TESTING to see benchmarks for more big types.
+//
+// You can use --copt=-DBIG_TYPE_TESTING.
+#ifndef NODESIZE_TESTING
+#ifdef BIG_TYPE_TESTING
+BIG_TYPE_BENCHMARKS(1, 4);
+BIG_TYPE_BENCHMARKS(4, 1);
+BIG_TYPE_BENCHMARKS(4, 4);
+BIG_TYPE_BENCHMARKS(1, 8);
+BIG_TYPE_BENCHMARKS(8, 1);
+BIG_TYPE_BENCHMARKS(8, 8);
+BIG_TYPE_BENCHMARKS(1, 16);
+BIG_TYPE_BENCHMARKS(16, 1);
+BIG_TYPE_BENCHMARKS(16, 16);
+BIG_TYPE_BENCHMARKS(1, 32);
+BIG_TYPE_BENCHMARKS(32, 1);
+BIG_TYPE_BENCHMARKS(32, 32);
+#else
+BIG_TYPE_BENCHMARKS(32, 32);
+#endif
+#endif
+
+// Benchmark using unique_ptrs to large value types. In order to be able to use
+// the same benchmark code as the other types, use a type that holds a
+// unique_ptr and has a copy constructor.
+template <int Size>
+struct BigTypePtr {
+ BigTypePtr() : BigTypePtr(0) {}
+ explicit BigTypePtr(int x) {
+ ptr = absl::make_unique<BigType<Size, Size>>(x);
+ }
+ BigTypePtr(const BigTypePtr& x) {
+ ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr);
+ }
+ BigTypePtr(BigTypePtr&& x) noexcept = default;
+ BigTypePtr& operator=(const BigTypePtr& x) {
+ ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr);
+ }
+ BigTypePtr& operator=(BigTypePtr&& x) noexcept = default;
+
+ bool operator<(const BigTypePtr& other) const { return *ptr < *other.ptr; }
+ bool operator==(const BigTypePtr& other) const { return *ptr == *other.ptr; }
+
+ std::unique_ptr<BigType<Size, Size>> ptr;
+};
+
+template <int Size>
+double ContainerInfo(const btree_set<BigTypePtr<Size>>& b) {
+ const double bytes_used =
+ b.bytes_used() + b.size() * sizeof(BigType<Size, Size>);
+ const double bytes_per_value = bytes_used / b.size();
+ BtreeContainerInfoLog(b, bytes_used, bytes_per_value);
+ return bytes_per_value;
+}
+template <int Size>
+double ContainerInfo(const btree_map<int, BigTypePtr<Size>>& b) {
+ const double bytes_used =
+ b.bytes_used() + b.size() * sizeof(BigType<Size, Size>);
+ const double bytes_per_value = bytes_used / b.size();
+ BtreeContainerInfoLog(b, bytes_used, bytes_per_value);
+ return bytes_per_value;
+}
+
+#define BIG_TYPE_PTR_BENCHMARKS(SIZE) \
+ using stl_set_size##SIZE##copies##SIZE##ptr = std::set<BigType<SIZE, SIZE>>; \
+ using stl_map_size##SIZE##copies##SIZE##ptr = \
+ std::map<int, BigType<SIZE, SIZE>>; \
+ using stl_unordered_set_size##SIZE##copies##SIZE##ptr = \
+ std::unordered_set<BigType<SIZE, SIZE>, \
+ absl::Hash<BigType<SIZE, SIZE>>>; \
+ using stl_unordered_map_size##SIZE##copies##SIZE##ptr = \
+ std::unordered_map<int, BigType<SIZE, SIZE>>; \
+ using flat_hash_set_size##SIZE##copies##SIZE##ptr = \
+ flat_hash_set<BigType<SIZE, SIZE>>; \
+ using flat_hash_map_size##SIZE##copies##SIZE##ptr = \
+ flat_hash_map<int, BigTypePtr<SIZE>>; \
+ using btree_256_set_size##SIZE##copies##SIZE##ptr = \
+ btree_set<BigTypePtr<SIZE>>; \
+ using btree_256_map_size##SIZE##copies##SIZE##ptr = \
+ btree_map<int, BigTypePtr<SIZE>>; \
+ MY_BENCHMARK3(stl_set_size##SIZE##copies##SIZE##ptr); \
+ MY_BENCHMARK3(stl_unordered_set_size##SIZE##copies##SIZE##ptr); \
+ MY_BENCHMARK3(flat_hash_set_size##SIZE##copies##SIZE##ptr); \
+ MY_BENCHMARK3(btree_256_set_size##SIZE##copies##SIZE##ptr); \
+ MY_BENCHMARK3(stl_map_size##SIZE##copies##SIZE##ptr); \
+ MY_BENCHMARK3(stl_unordered_map_size##SIZE##copies##SIZE##ptr); \
+ MY_BENCHMARK3(flat_hash_map_size##SIZE##copies##SIZE##ptr); \
+ MY_BENCHMARK3(btree_256_map_size##SIZE##copies##SIZE##ptr)
+
+BIG_TYPE_PTR_BENCHMARKS(32);
+
+} // namespace
+} // namespace container_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/container/btree_map.h b/absl/container/btree_map.h
new file mode 100644
index 00000000..d23f4ee5
--- /dev/null
+++ b/absl/container/btree_map.h
@@ -0,0 +1,759 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: btree_map.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines B-tree maps: sorted associative containers mapping
+// keys to values.
+//
+// * `absl::btree_map<>`
+// * `absl::btree_multimap<>`
+//
+// These B-tree types are similar to the corresponding types in the STL
+// (`std::map` and `std::multimap`) and generally conform to the STL interfaces
+// of those types. However, because they are implemented using B-trees, they
+// are more efficient in most situations.
+//
+// Unlike `std::map` and `std::multimap`, which are commonly implemented using
+// red-black tree nodes, B-tree maps use more generic B-tree nodes able to hold
+// multiple values per node. Holding multiple values per node often makes
+// B-tree maps perform better than their `std::map` counterparts, because
+// multiple entries can be checked within the same cache hit.
+//
+// However, these types should not be considered drop-in replacements for
+// `std::map` and `std::multimap` as there are some API differences, which are
+// noted in this header file.
+//
+// Importantly, insertions and deletions may invalidate outstanding iterators,
+// pointers, and references to elements. Such invalidations are typically only
+// an issue if insertion and deletion operations are interleaved with the use of
+// more than one iterator, pointer, or reference simultaneously. For this
+// reason, `insert()` and `erase()` return a valid iterator at the current
+// position.
+
+#ifndef ABSL_CONTAINER_BTREE_MAP_H_
+#define ABSL_CONTAINER_BTREE_MAP_H_
+
+#include "absl/container/internal/btree.h" // IWYU pragma: export
+#include "absl/container/internal/btree_container.h" // IWYU pragma: export
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// absl::btree_map<>
+//
+// An `absl::btree_map<K, V>` is an ordered associative container of
+// unique keys and associated values designed to be a more efficient replacement
+// for `std::map` (in most cases).
+//
+// Keys are sorted using an (optional) comparison function, which defaults to
+// `std::less<K>`.
+//
+// An `absl::btree_map<K, V>` uses a default allocator of
+// `std::allocator<std::pair<const K, V>>` to allocate (and deallocate)
+// nodes, and construct and destruct values within those nodes. You may
+// instead specify a custom allocator `A` (which in turn requires specifying a
+// custom comparator `C`) as in `absl::btree_map<K, V, C, A>`.
+//
+template <typename Key, typename Value, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<std::pair<const Key, Value>>>
+class btree_map
+ : public container_internal::btree_map_container<
+ container_internal::btree<container_internal::map_params<
+ Key, Value, Compare, Alloc, /*TargetNodeSize=*/256,
+ /*Multi=*/false>>> {
+ using Base = typename btree_map::btree_map_container;
+
+ public:
+ // Constructors and Assignment Operators
+ //
+ // A `btree_map` supports the same overload set as `std::map`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // absl::btree_map<int, std::string> map1;
+ //
+ // * Initializer List constructor
+ //
+ // absl::btree_map<int, std::string> map2 =
+ // {{1, "huey"}, {2, "dewey"}, {3, "louie"},};
+ //
+ // * Copy constructor
+ //
+ // absl::btree_map<int, std::string> map3(map2);
+ //
+ // * Copy assignment operator
+ //
+ // absl::btree_map<int, std::string> map4;
+ // map4 = map3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // absl::btree_map<int, std::string> map5(std::move(map4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // absl::btree_map<int, std::string> map6;
+ // map6 = std::move(map5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}};
+ // absl::btree_map<int, std::string> map7(v.begin(), v.end());
+ btree_map() {}
+ using Base::Base;
+
+ // btree_map::begin()
+ //
+ // Returns an iterator to the beginning of the `btree_map`.
+ using Base::begin;
+
+ // btree_map::cbegin()
+ //
+ // Returns a const iterator to the beginning of the `btree_map`.
+ using Base::cbegin;
+
+ // btree_map::end()
+ //
+ // Returns an iterator to the end of the `btree_map`.
+ using Base::end;
+
+ // btree_map::cend()
+ //
+ // Returns a const iterator to the end of the `btree_map`.
+ using Base::cend;
+
+ // btree_map::empty()
+ //
+ // Returns whether or not the `btree_map` is empty.
+ using Base::empty;
+
+ // btree_map::max_size()
+ //
+ // Returns the largest theoretical possible number of elements within a
+ // `btree_map` under current memory constraints. This value can be thought
+ // of as the largest value of `std::distance(begin(), end())` for a
+ // `btree_map<Key, T>`.
+ using Base::max_size;
+
+ // btree_map::size()
+ //
+ // Returns the number of elements currently within the `btree_map`.
+ using Base::size;
+
+ // btree_map::clear()
+ //
+ // Removes all elements from the `btree_map`. Invalidates any references,
+ // pointers, or iterators referring to contained elements.
+ using Base::clear;
+
+ // btree_map::erase()
+ //
+ // Erases elements within the `btree_map`. If an erase occurs, any references,
+ // pointers, or iterators are invalidated.
+ // Overloads are listed below.
+ //
+ // iterator erase(iterator position):
+ // iterator erase(const_iterator position):
+ //
+ // Erases the element at `position` of the `btree_map`, returning
+ // the iterator pointing to the element after the one that was erased
+ // (or end() if none exists).
+ //
+ // iterator erase(const_iterator first, const_iterator last):
+ //
+ // Erases the elements in the open interval [`first`, `last`), returning
+ // the iterator pointing to the element after the interval that was erased
+ // (or end() if none exists).
+ //
+ // template <typename K> size_type erase(const K& key):
+ //
+ // Erases the element with the matching key, if it exists, returning the
+ // number of elements erased.
+ using Base::erase;
+
+ // btree_map::insert()
+ //
+ // Inserts an element of the specified value into the `btree_map`,
+ // returning an iterator pointing to the newly inserted element, provided that
+ // an element with the given key does not already exist. If an insertion
+ // occurs, any references, pointers, or iterators are invalidated.
+ // Overloads are listed below.
+ //
+ // std::pair<iterator,bool> insert(const value_type& value):
+ //
+ // Inserts a value into the `btree_map`. Returns a pair consisting of an
+ // iterator to the inserted element (or to the element that prevented the
+ // insertion) and a bool denoting whether the insertion took place.
+ //
+ // std::pair<iterator,bool> insert(value_type&& value):
+ //
+ // Inserts a moveable value into the `btree_map`. Returns a pair
+ // consisting of an iterator to the inserted element (or to the element that
+ // prevented the insertion) and a bool denoting whether the insertion took
+ // place.
+ //
+ // iterator insert(const_iterator hint, const value_type& value):
+ // iterator insert(const_iterator hint, value_type&& value):
+ //
+ // Inserts a value, using the position of `hint` as a non-binding suggestion
+ // for where to begin the insertion search. Returns an iterator to the
+ // inserted element, or to the existing element that prevented the
+ // insertion.
+ //
+ // void insert(InputIterator first, InputIterator last):
+ //
+ // Inserts a range of values [`first`, `last`).
+ //
+ // void insert(std::initializer_list<init_type> ilist):
+ //
+ // Inserts the elements within the initializer list `ilist`.
+ using Base::insert;
+
+ // btree_map::insert_or_assign()
+ //
+ // Inserts an element of the specified value into the `btree_map` provided
+ // that a value with the given key does not already exist, or replaces the
+ // corresponding mapped type with the forwarded `obj` argument if a key for
+ // that value already exists, returning an iterator pointing to the newly
+ // inserted element. Overloads are listed below.
+ //
+ // pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj):
+ // pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj):
+ //
+ // Inserts/Assigns (or moves) the element of the specified key into the
+ // `btree_map`. If the returned bool is true, insertion took place, and if
+ // it's false, assignment took place.
+ //
+ // iterator insert_or_assign(const_iterator hint,
+ // const key_type& k, M&& obj):
+ // iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj):
+ //
+ // Inserts/Assigns (or moves) the element of the specified key into the
+ // `btree_map` using the position of `hint` as a non-binding suggestion
+ // for where to begin the insertion search.
+ using Base::insert_or_assign;
+
+ // btree_map::emplace()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_map`, provided that no element with the given key
+ // already exists.
+ //
+ // The element may be constructed even if there already is an element with the
+ // key in the container, in which case the newly constructed element will be
+ // destroyed immediately. Prefer `try_emplace()` unless your key is not
+ // copyable or moveable.
+ //
+ // If an insertion occurs, any references, pointers, or iterators are
+ // invalidated.
+ using Base::emplace;
+
+ // btree_map::emplace_hint()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_map`, using the position of `hint` as a non-binding
+ // suggestion for where to begin the insertion search, and only inserts
+ // provided that no element with the given key already exists.
+ //
+ // The element may be constructed even if there already is an element with the
+ // key in the container, in which case the newly constructed element will be
+ // destroyed immediately. Prefer `try_emplace()` unless your key is not
+ // copyable or moveable.
+ //
+ // If an insertion occurs, any references, pointers, or iterators are
+ // invalidated.
+ using Base::emplace_hint;
+
+ // btree_map::try_emplace()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_map`, provided that no element with the given key
+ // already exists. Unlike `emplace()`, if an element with the given key
+ // already exists, we guarantee that no element is constructed.
+ //
+ // If an insertion occurs, any references, pointers, or iterators are
+ // invalidated.
+ //
+ // Overloads are listed below.
+ //
+ // std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args):
+ // std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args):
+ //
+ // Inserts (via copy or move) the element of the specified key into the
+ // `btree_map`.
+ //
+ // iterator try_emplace(const_iterator hint,
+ // const key_type& k, Args&&... args):
+ // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args):
+ //
+ // Inserts (via copy or move) the element of the specified key into the
+ // `btree_map` using the position of `hint` as a non-binding suggestion
+ // for where to begin the insertion search.
+ using Base::try_emplace;
+
+ // btree_map::extract()
+ //
+ // Extracts the indicated element, erasing it in the process, and returns it
+ // as a C++17-compatible node handle. Overloads are listed below.
+ //
+ // node_type extract(const_iterator position):
+ //
+ // Extracts the element at the indicated position and returns a node handle
+ // owning that extracted data.
+ //
+ // template <typename K> node_type extract(const K& x):
+ //
+ // Extracts the element with the key matching the passed key value and
+ // returns a node handle owning that extracted data. If the `btree_map`
+ // does not contain an element with a matching key, this function returns an
+ // empty node handle.
+ //
+ // NOTE: In this context, `node_type` refers to the C++17 concept of a
+ // move-only type that owns and provides access to the elements in associative
+ // containers (https://en.cppreference.com/w/cpp/container/node_handle).
+ // It does NOT refer to the data layout of the underlying btree.
+ using Base::extract;
+
+ // btree_map::merge()
+ //
+ // Extracts elements from a given `source` btree_map into this
+ // `btree_map`. If the destination `btree_map` already contains an
+ // element with an equivalent key, that element is not extracted.
+ using Base::merge;
+
+ // btree_map::swap(btree_map& other)
+ //
+ // Exchanges the contents of this `btree_map` with those of the `other`
+ // btree_map, avoiding invocation of any move, copy, or swap operations on
+ // individual elements.
+ //
+ // All iterators and references on the `btree_map` remain valid, excepting
+ // for the past-the-end iterator, which is invalidated.
+ using Base::swap;
+
+ // btree_map::at()
+ //
+ // Returns a reference to the mapped value of the element with key equivalent
+ // to the passed key.
+ using Base::at;
+
+ // btree_map::contains()
+ //
+ // template <typename K> bool contains(const K& key) const:
+ //
+ // Determines whether an element comparing equal to the given `key` exists
+ // within the `btree_map`, returning `true` if so or `false` otherwise.
+ //
+ // Supports heterogeneous lookup, provided that the map is provided a
+ // compatible heterogeneous comparator.
+ using Base::contains;
+
+ // btree_map::count()
+ //
+ // template <typename K> size_type count(const K& key) const:
+ //
+ // Returns the number of elements comparing equal to the given `key` within
+ // the `btree_map`. Note that this function will return either `1` or `0`
+ // since duplicate elements are not allowed within a `btree_map`.
+ //
+ // Supports heterogeneous lookup, provided that the map is provided a
+ // compatible heterogeneous comparator.
+ using Base::count;
+
+ // btree_map::equal_range()
+ //
+ // Returns a closed range [first, last], defined by a `std::pair` of two
+ // iterators, containing all elements with the passed key in the
+ // `btree_map`.
+ using Base::equal_range;
+
+ // btree_map::find()
+ //
+ // template <typename K> iterator find(const K& key):
+ // template <typename K> const_iterator find(const K& key) const:
+ //
+ // Finds an element with the passed `key` within the `btree_map`.
+ //
+ // Supports heterogeneous lookup, provided that the map is provided a
+ // compatible heterogeneous comparator.
+ using Base::find;
+
+ // btree_map::operator[]()
+ //
+ // Returns a reference to the value mapped to the passed key within the
+ // `btree_map`, performing an `insert()` if the key does not already
+ // exist.
+ //
+ // If an insertion occurs, any references, pointers, or iterators are
+ // invalidated. Otherwise iterators are not affected and references are not
+ // invalidated. Overloads are listed below.
+ //
+ // T& operator[](key_type&& key):
+ // T& operator[](const key_type& key):
+ //
+ // Inserts a value_type object constructed in-place if the element with the
+ // given key does not exist.
+ using Base::operator[];
+
+ // btree_map::get_allocator()
+ //
+ // Returns the allocator function associated with this `btree_map`.
+ using Base::get_allocator;
+
+ // btree_map::key_comp();
+ //
+ // Returns the key comparator associated with this `btree_map`.
+ using Base::key_comp;
+
+ // btree_map::value_comp();
+ //
+ // Returns the value comparator associated with this `btree_map`.
+ using Base::value_comp;
+};
+
+// absl::swap(absl::btree_map<>, absl::btree_map<>)
+//
+// Swaps the contents of two `absl::btree_map` containers.
+template <typename K, typename V, typename C, typename A>
+void swap(btree_map<K, V, C, A> &x, btree_map<K, V, C, A> &y) {
+ return x.swap(y);
+}
+
+// absl::erase_if(absl::btree_map<>, Pred)
+//
+// Erases all elements that satisfy the predicate pred from the container.
+template <typename K, typename V, typename C, typename A, typename Pred>
+void erase_if(btree_map<K, V, C, A> &map, Pred pred) {
+ for (auto it = map.begin(); it != map.end();) {
+ if (pred(*it)) {
+ it = map.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+// absl::btree_multimap
+//
+// An `absl::btree_multimap<K, V>` is an ordered associative container of
+// keys and associated values designed to be a more efficient replacement for
+// `std::multimap` (in most cases). Unlike `absl::btree_map`, a B-tree multimap
+// allows multiple elements with equivalent keys.
+//
+// Keys are sorted using an (optional) comparison function, which defaults to
+// `std::less<K>`.
+//
+// An `absl::btree_multimap<K, V>` uses a default allocator of
+// `std::allocator<std::pair<const K, V>>` to allocate (and deallocate)
+// nodes, and construct and destruct values within those nodes. You may
+// instead specify a custom allocator `A` (which in turn requires specifying a
+// custom comparator `C`) as in `absl::btree_multimap<K, V, C, A>`.
+//
+template <typename Key, typename Value, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<std::pair<const Key, Value>>>
+class btree_multimap
+ : public container_internal::btree_multimap_container<
+ container_internal::btree<container_internal::map_params<
+ Key, Value, Compare, Alloc, /*TargetNodeSize=*/256,
+ /*Multi=*/true>>> {
+ using Base = typename btree_multimap::btree_multimap_container;
+
+ public:
+ // Constructors and Assignment Operators
+ //
+ // A `btree_multimap` supports the same overload set as `std::multimap`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // absl::btree_multimap<int, std::string> map1;
+ //
+ // * Initializer List constructor
+ //
+ // absl::btree_multimap<int, std::string> map2 =
+ // {{1, "huey"}, {2, "dewey"}, {3, "louie"},};
+ //
+ // * Copy constructor
+ //
+ // absl::btree_multimap<int, std::string> map3(map2);
+ //
+ // * Copy assignment operator
+ //
+ // absl::btree_multimap<int, std::string> map4;
+ // map4 = map3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // absl::btree_multimap<int, std::string> map5(std::move(map4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // absl::btree_multimap<int, std::string> map6;
+ // map6 = std::move(map5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}};
+ // absl::btree_multimap<int, std::string> map7(v.begin(), v.end());
+ btree_multimap() {}
+ using Base::Base;
+
+ // btree_multimap::begin()
+ //
+ // Returns an iterator to the beginning of the `btree_multimap`.
+ using Base::begin;
+
+ // btree_multimap::cbegin()
+ //
+ // Returns a const iterator to the beginning of the `btree_multimap`.
+ using Base::cbegin;
+
+ // btree_multimap::end()
+ //
+ // Returns an iterator to the end of the `btree_multimap`.
+ using Base::end;
+
+ // btree_multimap::cend()
+ //
+ // Returns a const iterator to the end of the `btree_multimap`.
+ using Base::cend;
+
+ // btree_multimap::empty()
+ //
+ // Returns whether or not the `btree_multimap` is empty.
+ using Base::empty;
+
+ // btree_multimap::max_size()
+ //
+ // Returns the largest theoretical possible number of elements within a
+ // `btree_multimap` under current memory constraints. This value can be
+ // thought of as the largest value of `std::distance(begin(), end())` for a
+ // `btree_multimap<Key, T>`.
+ using Base::max_size;
+
+ // btree_multimap::size()
+ //
+ // Returns the number of elements currently within the `btree_multimap`.
+ using Base::size;
+
+ // btree_multimap::clear()
+ //
+ // Removes all elements from the `btree_multimap`. Invalidates any references,
+ // pointers, or iterators referring to contained elements.
+ using Base::clear;
+
+ // btree_multimap::erase()
+ //
+ // Erases elements within the `btree_multimap`. If an erase occurs, any
+ // references, pointers, or iterators are invalidated.
+ // Overloads are listed below.
+ //
+ // iterator erase(iterator position):
+ // iterator erase(const_iterator position):
+ //
+ // Erases the element at `position` of the `btree_multimap`, returning
+ // the iterator pointing to the element after the one that was erased
+ // (or end() if none exists).
+ //
+ // iterator erase(const_iterator first, const_iterator last):
+ //
+ // Erases the elements in the open interval [`first`, `last`), returning
+ // the iterator pointing to the element after the interval that was erased
+ // (or end() if none exists).
+ //
+ // template <typename K> size_type erase(const K& key):
+ //
+ // Erases the elements matching the key, if any exist, returning the
+ // number of elements erased.
+ using Base::erase;
+
+ // btree_multimap::insert()
+ //
+ // Inserts an element of the specified value into the `btree_multimap`,
+ // returning an iterator pointing to the newly inserted element.
+ // Any references, pointers, or iterators are invalidated. Overloads are
+ // listed below.
+ //
+ // iterator insert(const value_type& value):
+ //
+ // Inserts a value into the `btree_multimap`, returning an iterator to the
+ // inserted element.
+ //
+ // iterator insert(value_type&& value):
+ //
+ // Inserts a moveable value into the `btree_multimap`, returning an iterator
+ // to the inserted element.
+ //
+ // iterator insert(const_iterator hint, const value_type& value):
+ // iterator insert(const_iterator hint, value_type&& value):
+ //
+ // Inserts a value, using the position of `hint` as a non-binding suggestion
+ // for where to begin the insertion search. Returns an iterator to the
+ // inserted element.
+ //
+ // void insert(InputIterator first, InputIterator last):
+ //
+ // Inserts a range of values [`first`, `last`).
+ //
+ // void insert(std::initializer_list<init_type> ilist):
+ //
+ // Inserts the elements within the initializer list `ilist`.
+ using Base::insert;
+
+ // btree_multimap::emplace()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_multimap`. Any references, pointers, or iterators are
+ // invalidated.
+ using Base::emplace;
+
+ // btree_multimap::emplace_hint()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_multimap`, using the position of `hint` as a non-binding
+ // suggestion for where to begin the insertion search.
+ //
+ // Any references, pointers, or iterators are invalidated.
+ using Base::emplace_hint;
+
+ // btree_multimap::extract()
+ //
+ // Extracts the indicated element, erasing it in the process, and returns it
+ // as a C++17-compatible node handle. Overloads are listed below.
+ //
+ // node_type extract(const_iterator position):
+ //
+ // Extracts the element at the indicated position and returns a node handle
+ // owning that extracted data.
+ //
+ // template <typename K> node_type extract(const K& x):
+ //
+ // Extracts the element with the key matching the passed key value and
+ // returns a node handle owning that extracted data. If the `btree_multimap`
+ // does not contain an element with a matching key, this function returns an
+ // empty node handle.
+ //
+ // NOTE: In this context, `node_type` refers to the C++17 concept of a
+ // move-only type that owns and provides access to the elements in associative
+ // containers (https://en.cppreference.com/w/cpp/container/node_handle).
+ // It does NOT refer to the data layout of the underlying btree.
+ using Base::extract;
+
+ // btree_multimap::merge()
+ //
+ // Extracts elements from a given `source` btree_multimap into this
+ // `btree_multimap`. If the destination `btree_multimap` already contains an
+ // element with an equivalent key, that element is not extracted.
+ using Base::merge;
+
+ // btree_multimap::swap(btree_multimap& other)
+ //
+ // Exchanges the contents of this `btree_multimap` with those of the `other`
+ // btree_multimap, avoiding invocation of any move, copy, or swap operations
+ // on individual elements.
+ //
+ // All iterators and references on the `btree_multimap` remain valid,
+ // excepting for the past-the-end iterator, which is invalidated.
+ using Base::swap;
+
+ // btree_multimap::contains()
+ //
+ // template <typename K> bool contains(const K& key) const:
+ //
+ // Determines whether an element comparing equal to the given `key` exists
+ // within the `btree_multimap`, returning `true` if so or `false` otherwise.
+ //
+ // Supports heterogeneous lookup, provided that the map is provided a
+ // compatible heterogeneous comparator.
+ using Base::contains;
+
+ // btree_multimap::count()
+ //
+ // template <typename K> size_type count(const K& key) const:
+ //
+ // Returns the number of elements comparing equal to the given `key` within
+ // the `btree_multimap`.
+ //
+ // Supports heterogeneous lookup, provided that the map is provided a
+ // compatible heterogeneous comparator.
+ using Base::count;
+
+ // btree_multimap::equal_range()
+ //
+ // Returns a closed range [first, last], defined by a `std::pair` of two
+ // iterators, containing all elements with the passed key in the
+ // `btree_multimap`.
+ using Base::equal_range;
+
+ // btree_multimap::find()
+ //
+ // template <typename K> iterator find(const K& key):
+ // template <typename K> const_iterator find(const K& key) const:
+ //
+ // Finds an element with the passed `key` within the `btree_multimap`.
+ //
+ // Supports heterogeneous lookup, provided that the map is provided a
+ // compatible heterogeneous comparator.
+ using Base::find;
+
+ // btree_multimap::get_allocator()
+ //
+ // Returns the allocator function associated with this `btree_multimap`.
+ using Base::get_allocator;
+
+ // btree_multimap::key_comp();
+ //
+ // Returns the key comparator associated with this `btree_multimap`.
+ using Base::key_comp;
+
+ // btree_multimap::value_comp();
+ //
+ // Returns the value comparator associated with this `btree_multimap`.
+ using Base::value_comp;
+};
+
+// absl::swap(absl::btree_multimap<>, absl::btree_multimap<>)
+//
+// Swaps the contents of two `absl::btree_multimap` containers.
+template <typename K, typename V, typename C, typename A>
+void swap(btree_multimap<K, V, C, A> &x, btree_multimap<K, V, C, A> &y) {
+ return x.swap(y);
+}
+
+// absl::erase_if(absl::btree_multimap<>, Pred)
+//
+// Erases all elements that satisfy the predicate pred from the container.
+template <typename K, typename V, typename C, typename A, typename Pred>
+void erase_if(btree_multimap<K, V, C, A> &map, Pred pred) {
+ for (auto it = map.begin(); it != map.end();) {
+ if (pred(*it)) {
+ it = map.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_CONTAINER_BTREE_MAP_H_
diff --git a/absl/container/btree_set.h b/absl/container/btree_set.h
new file mode 100644
index 00000000..127fb940
--- /dev/null
+++ b/absl/container/btree_set.h
@@ -0,0 +1,683 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: btree_set.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines B-tree sets: sorted associative containers of
+// values.
+//
+// * `absl::btree_set<>`
+// * `absl::btree_multiset<>`
+//
+// These B-tree types are similar to the corresponding types in the STL
+// (`std::set` and `std::multiset`) and generally conform to the STL interfaces
+// of those types. However, because they are implemented using B-trees, they
+// are more efficient in most situations.
+//
+// Unlike `std::set` and `std::multiset`, which are commonly implemented using
+// red-black tree nodes, B-tree sets use more generic B-tree nodes able to hold
+// multiple values per node. Holding multiple values per node often makes
+// B-tree sets perform better than their `std::set` counterparts, because
+// multiple entries can be checked within the same cache hit.
+//
+// However, these types should not be considered drop-in replacements for
+// `std::set` and `std::multiset` as there are some API differences, which are
+// noted in this header file.
+//
+// Importantly, insertions and deletions may invalidate outstanding iterators,
+// pointers, and references to elements. Such invalidations are typically only
+// an issue if insertion and deletion operations are interleaved with the use of
+// more than one iterator, pointer, or reference simultaneously. For this
+// reason, `insert()` and `erase()` return a valid iterator at the current
+// position.
+
+#ifndef ABSL_CONTAINER_BTREE_SET_H_
+#define ABSL_CONTAINER_BTREE_SET_H_
+
+#include "absl/container/internal/btree.h" // IWYU pragma: export
+#include "absl/container/internal/btree_container.h" // IWYU pragma: export
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// absl::btree_set<>
+//
+// An `absl::btree_set<K>` is an ordered associative container of unique key
+// values designed to be a more efficient replacement for `std::set` (in most
+// cases).
+//
+// Keys are sorted using an (optional) comparison function, which defaults to
+// `std::less<K>`.
+//
+// An `absl::btree_set<K>` uses a default allocator of `std::allocator<K>` to
+// allocate (and deallocate) nodes, and construct and destruct values within
+// those nodes. You may instead specify a custom allocator `A` (which in turn
+// requires specifying a custom comparator `C`) as in
+// `absl::btree_set<K, C, A>`.
+//
+template <typename Key, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<Key>>
+class btree_set
+ : public container_internal::btree_set_container<
+ container_internal::btree<container_internal::set_params<
+ Key, Compare, Alloc, /*TargetNodeSize=*/256,
+ /*Multi=*/false>>> {
+ using Base = typename btree_set::btree_set_container;
+
+ public:
+ // Constructors and Assignment Operators
+ //
+ // A `btree_set` supports the same overload set as `std::set`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // absl::btree_set<std::string> set1;
+ //
+ // * Initializer List constructor
+ //
+ // absl::btree_set<std::string> set2 =
+ // {{"huey"}, {"dewey"}, {"louie"},};
+ //
+ // * Copy constructor
+ //
+ // absl::btree_set<std::string> set3(set2);
+ //
+ // * Copy assignment operator
+ //
+ // absl::btree_set<std::string> set4;
+ // set4 = set3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // absl::btree_set<std::string> set5(std::move(set4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // absl::btree_set<std::string> set6;
+ // set6 = std::move(set5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::string> v = {"a", "b"};
+ // absl::btree_set<std::string> set7(v.begin(), v.end());
+ btree_set() {}
+ using Base::Base;
+
+ // btree_set::begin()
+ //
+ // Returns an iterator to the beginning of the `btree_set`.
+ using Base::begin;
+
+ // btree_set::cbegin()
+ //
+ // Returns a const iterator to the beginning of the `btree_set`.
+ using Base::cbegin;
+
+ // btree_set::end()
+ //
+ // Returns an iterator to the end of the `btree_set`.
+ using Base::end;
+
+ // btree_set::cend()
+ //
+ // Returns a const iterator to the end of the `btree_set`.
+ using Base::cend;
+
+ // btree_set::empty()
+ //
+ // Returns whether or not the `btree_set` is empty.
+ using Base::empty;
+
+ // btree_set::max_size()
+ //
+ // Returns the largest theoretical possible number of elements within a
+ // `btree_set` under current memory constraints. This value can be thought
+ // of as the largest value of `std::distance(begin(), end())` for a
+ // `btree_set<Key>`.
+ using Base::max_size;
+
+ // btree_set::size()
+ //
+ // Returns the number of elements currently within the `btree_set`.
+ using Base::size;
+
+ // btree_set::clear()
+ //
+ // Removes all elements from the `btree_set`. Invalidates any references,
+ // pointers, or iterators referring to contained elements.
+ using Base::clear;
+
+ // btree_set::erase()
+ //
+ // Erases elements within the `btree_set`. Overloads are listed below.
+ //
+ // iterator erase(iterator position):
+ // iterator erase(const_iterator position):
+ //
+ // Erases the element at `position` of the `btree_set`, returning
+ // the iterator pointing to the element after the one that was erased
+ // (or end() if none exists).
+ //
+ // iterator erase(const_iterator first, const_iterator last):
+ //
+ // Erases the elements in the open interval [`first`, `last`), returning
+ // the iterator pointing to the element after the interval that was erased
+ // (or end() if none exists).
+ //
+ // template <typename K> size_type erase(const K& key):
+ //
+ // Erases the element with the matching key, if it exists, returning the
+ // number of elements erased.
+ using Base::erase;
+
+ // btree_set::insert()
+ //
+ // Inserts an element of the specified value into the `btree_set`,
+ // returning an iterator pointing to the newly inserted element, provided that
+ // an element with the given key does not already exist. If an insertion
+ // occurs, any references, pointers, or iterators are invalidated.
+ // Overloads are listed below.
+ //
+ // std::pair<iterator,bool> insert(const value_type& value):
+ //
+ // Inserts a value into the `btree_set`. Returns a pair consisting of an
+ // iterator to the inserted element (or to the element that prevented the
+ // insertion) and a bool denoting whether the insertion took place.
+ //
+ // std::pair<iterator,bool> insert(value_type&& value):
+ //
+ // Inserts a moveable value into the `btree_set`. Returns a pair
+ // consisting of an iterator to the inserted element (or to the element that
+ // prevented the insertion) and a bool denoting whether the insertion took
+ // place.
+ //
+ // iterator insert(const_iterator hint, const value_type& value):
+ // iterator insert(const_iterator hint, value_type&& value):
+ //
+ // Inserts a value, using the position of `hint` as a non-binding suggestion
+ // for where to begin the insertion search. Returns an iterator to the
+ // inserted element, or to the existing element that prevented the
+ // insertion.
+ //
+ // void insert(InputIterator first, InputIterator last):
+ //
+ // Inserts a range of values [`first`, `last`).
+ //
+ // void insert(std::initializer_list<init_type> ilist):
+ //
+ // Inserts the elements within the initializer list `ilist`.
+ using Base::insert;
+
+ // btree_set::emplace()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_set`, provided that no element with the given key
+ // already exists.
+ //
+ // The element may be constructed even if there already is an element with the
+ // key in the container, in which case the newly constructed element will be
+ // destroyed immediately.
+ //
+ // If an insertion occurs, any references, pointers, or iterators are
+ // invalidated.
+ using Base::emplace;
+
+ // btree_set::emplace_hint()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_set`, using the position of `hint` as a non-binding
+ // suggestion for where to begin the insertion search, and only inserts
+ // provided that no element with the given key already exists.
+ //
+ // The element may be constructed even if there already is an element with the
+ // key in the container, in which case the newly constructed element will be
+ // destroyed immediately.
+ //
+ // If an insertion occurs, any references, pointers, or iterators are
+ // invalidated.
+ using Base::emplace_hint;
+
+ // btree_set::extract()
+ //
+ // Extracts the indicated element, erasing it in the process, and returns it
+ // as a C++17-compatible node handle. Overloads are listed below.
+ //
+ // node_type extract(const_iterator position):
+ //
+ // Extracts the element at the indicated position and returns a node handle
+ // owning that extracted data.
+ //
+ // template <typename K> node_type extract(const K& x):
+ //
+ // Extracts the element with the key matching the passed key value and
+ // returns a node handle owning that extracted data. If the `btree_set`
+ // does not contain an element with a matching key, this function returns an
+ // empty node handle.
+ //
+ // NOTE: In this context, `node_type` refers to the C++17 concept of a
+ // move-only type that owns and provides access to the elements in associative
+ // containers (https://en.cppreference.com/w/cpp/container/node_handle).
+ // It does NOT refer to the data layout of the underlying btree.
+ using Base::extract;
+
+ // btree_set::merge()
+ //
+ // Extracts elements from a given `source` btree_set into this
+ // `btree_set`. If the destination `btree_set` already contains an
+ // element with an equivalent key, that element is not extracted.
+ using Base::merge;
+
+ // btree_set::swap(btree_set& other)
+ //
+ // Exchanges the contents of this `btree_set` with those of the `other`
+ // btree_set, avoiding invocation of any move, copy, or swap operations on
+ // individual elements.
+ //
+ // All iterators and references on the `btree_set` remain valid, excepting
+ // for the past-the-end iterator, which is invalidated.
+ using Base::swap;
+
+ // btree_set::contains()
+ //
+ // template <typename K> bool contains(const K& key) const:
+ //
+ // Determines whether an element comparing equal to the given `key` exists
+ // within the `btree_set`, returning `true` if so or `false` otherwise.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::contains;
+
+ // btree_set::count()
+ //
+ // template <typename K> size_type count(const K& key) const:
+ //
+ // Returns the number of elements comparing equal to the given `key` within
+ // the `btree_set`. Note that this function will return either `1` or `0`
+ // since duplicate elements are not allowed within a `btree_set`.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::count;
+
+ // btree_set::equal_range()
+ //
+ // Returns a closed range [first, last], defined by a `std::pair` of two
+ // iterators, containing all elements with the passed key in the
+ // `btree_set`.
+ using Base::equal_range;
+
+ // btree_set::find()
+ //
+ // template <typename K> iterator find(const K& key):
+ // template <typename K> const_iterator find(const K& key) const:
+ //
+ // Finds an element with the passed `key` within the `btree_set`.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::find;
+
+ // btree_set::get_allocator()
+ //
+ // Returns the allocator function associated with this `btree_set`.
+ using Base::get_allocator;
+
+ // btree_set::key_comp();
+ //
+ // Returns the key comparator associated with this `btree_set`.
+ using Base::key_comp;
+
+ // btree_set::value_comp();
+ //
+ // Returns the value comparator associated with this `btree_set`. The keys to
+ // sort the elements are the values themselves, therefore `value_comp` and its
+ // sibling member function `key_comp` are equivalent.
+ using Base::value_comp;
+};
+
+// absl::swap(absl::btree_set<>, absl::btree_set<>)
+//
+// Swaps the contents of two `absl::btree_set` containers.
+template <typename K, typename C, typename A>
+void swap(btree_set<K, C, A> &x, btree_set<K, C, A> &y) {
+ return x.swap(y);
+}
+
+// absl::erase_if(absl::btree_set<>, Pred)
+//
+// Erases all elements that satisfy the predicate pred from the container.
+template <typename K, typename C, typename A, typename Pred>
+void erase_if(btree_set<K, C, A> &set, Pred pred) {
+ for (auto it = set.begin(); it != set.end();) {
+ if (pred(*it)) {
+ it = set.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+// absl::btree_multiset<>
+//
+// An `absl::btree_multiset<K>` is an ordered associative container of
+// keys and associated values designed to be a more efficient replacement
+// for `std::multiset` (in most cases). Unlike `absl::btree_set`, a B-tree
+// multiset allows equivalent elements.
+//
+// Keys are sorted using an (optional) comparison function, which defaults to
+// `std::less<K>`.
+//
+// An `absl::btree_multiset<K>` uses a default allocator of `std::allocator<K>`
+// to allocate (and deallocate) nodes, and construct and destruct values within
+// those nodes. You may instead specify a custom allocator `A` (which in turn
+// requires specifying a custom comparator `C`) as in
+// `absl::btree_multiset<K, C, A>`.
+//
+template <typename Key, typename Compare = std::less<Key>,
+ typename Alloc = std::allocator<Key>>
+class btree_multiset
+ : public container_internal::btree_multiset_container<
+ container_internal::btree<container_internal::set_params<
+ Key, Compare, Alloc, /*TargetNodeSize=*/256,
+ /*Multi=*/true>>> {
+ using Base = typename btree_multiset::btree_multiset_container;
+
+ public:
+ // Constructors and Assignment Operators
+ //
+ // A `btree_multiset` supports the same overload set as `std::set`
+ // for construction and assignment:
+ //
+ // * Default constructor
+ //
+ // absl::btree_multiset<std::string> set1;
+ //
+ // * Initializer List constructor
+ //
+ // absl::btree_multiset<std::string> set2 =
+ // {{"huey"}, {"dewey"}, {"louie"},};
+ //
+ // * Copy constructor
+ //
+ // absl::btree_multiset<std::string> set3(set2);
+ //
+ // * Copy assignment operator
+ //
+ // absl::btree_multiset<std::string> set4;
+ // set4 = set3;
+ //
+ // * Move constructor
+ //
+ // // Move is guaranteed efficient
+ // absl::btree_multiset<std::string> set5(std::move(set4));
+ //
+ // * Move assignment operator
+ //
+ // // May be efficient if allocators are compatible
+ // absl::btree_multiset<std::string> set6;
+ // set6 = std::move(set5);
+ //
+ // * Range constructor
+ //
+ // std::vector<std::string> v = {"a", "b"};
+ // absl::btree_multiset<std::string> set7(v.begin(), v.end());
+ btree_multiset() {}
+ using Base::Base;
+
+ // btree_multiset::begin()
+ //
+ // Returns an iterator to the beginning of the `btree_multiset`.
+ using Base::begin;
+
+ // btree_multiset::cbegin()
+ //
+ // Returns a const iterator to the beginning of the `btree_multiset`.
+ using Base::cbegin;
+
+ // btree_multiset::end()
+ //
+ // Returns an iterator to the end of the `btree_multiset`.
+ using Base::end;
+
+ // btree_multiset::cend()
+ //
+ // Returns a const iterator to the end of the `btree_multiset`.
+ using Base::cend;
+
+ // btree_multiset::empty()
+ //
+ // Returns whether or not the `btree_multiset` is empty.
+ using Base::empty;
+
+ // btree_multiset::max_size()
+ //
+ // Returns the largest theoretical possible number of elements within a
+ // `btree_multiset` under current memory constraints. This value can be
+ // thought of as the largest value of `std::distance(begin(), end())` for a
+ // `btree_multiset<Key>`.
+ using Base::max_size;
+
+ // btree_multiset::size()
+ //
+ // Returns the number of elements currently within the `btree_multiset`.
+ using Base::size;
+
+ // btree_multiset::clear()
+ //
+ // Removes all elements from the `btree_multiset`. Invalidates any references,
+ // pointers, or iterators referring to contained elements.
+ using Base::clear;
+
+ // btree_multiset::erase()
+ //
+ // Erases elements within the `btree_multiset`. Overloads are listed below.
+ //
+ // iterator erase(iterator position):
+ // iterator erase(const_iterator position):
+ //
+ // Erases the element at `position` of the `btree_multiset`, returning
+ // the iterator pointing to the element after the one that was erased
+ // (or end() if none exists).
+ //
+ // iterator erase(const_iterator first, const_iterator last):
+ //
+ // Erases the elements in the open interval [`first`, `last`), returning
+ // the iterator pointing to the element after the interval that was erased
+ // (or end() if none exists).
+ //
+ // template <typename K> size_type erase(const K& key):
+ //
+ // Erases the elements matching the key, if any exist, returning the
+ // number of elements erased.
+ using Base::erase;
+
+ // btree_multiset::insert()
+ //
+ // Inserts an element of the specified value into the `btree_multiset`,
+ // returning an iterator pointing to the newly inserted element.
+ // Any references, pointers, or iterators are invalidated. Overloads are
+ // listed below.
+ //
+ // iterator insert(const value_type& value):
+ //
+ // Inserts a value into the `btree_multiset`, returning an iterator to the
+ // inserted element.
+ //
+ // iterator insert(value_type&& value):
+ //
+ // Inserts a moveable value into the `btree_multiset`, returning an iterator
+ // to the inserted element.
+ //
+ // iterator insert(const_iterator hint, const value_type& value):
+ // iterator insert(const_iterator hint, value_type&& value):
+ //
+ // Inserts a value, using the position of `hint` as a non-binding suggestion
+ // for where to begin the insertion search. Returns an iterator to the
+ // inserted element.
+ //
+ // void insert(InputIterator first, InputIterator last):
+ //
+ // Inserts a range of values [`first`, `last`).
+ //
+ // void insert(std::initializer_list<init_type> ilist):
+ //
+ // Inserts the elements within the initializer list `ilist`.
+ using Base::insert;
+
+ // btree_multiset::emplace()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_multiset`. Any references, pointers, or iterators are
+ // invalidated.
+ using Base::emplace;
+
+ // btree_multiset::emplace_hint()
+ //
+ // Inserts an element of the specified value by constructing it in-place
+ // within the `btree_multiset`, using the position of `hint` as a non-binding
+ // suggestion for where to begin the insertion search.
+ //
+ // Any references, pointers, or iterators are invalidated.
+ using Base::emplace_hint;
+
+ // btree_multiset::extract()
+ //
+ // Extracts the indicated element, erasing it in the process, and returns it
+ // as a C++17-compatible node handle. Overloads are listed below.
+ //
+ // node_type extract(const_iterator position):
+ //
+ // Extracts the element at the indicated position and returns a node handle
+ // owning that extracted data.
+ //
+ // template <typename K> node_type extract(const K& x):
+ //
+ // Extracts the element with the key matching the passed key value and
+ // returns a node handle owning that extracted data. If the `btree_multiset`
+ // does not contain an element with a matching key, this function returns an
+ // empty node handle.
+ //
+ // NOTE: In this context, `node_type` refers to the C++17 concept of a
+ // move-only type that owns and provides access to the elements in associative
+ // containers (https://en.cppreference.com/w/cpp/container/node_handle).
+ // It does NOT refer to the data layout of the underlying btree.
+ using Base::extract;
+
+ // btree_multiset::merge()
+ //
+ // Extracts elements from a given `source` btree_multiset into this
+ // `btree_multiset`. If the destination `btree_multiset` already contains an
+ // element with an equivalent key, that element is not extracted.
+ using Base::merge;
+
+ // btree_multiset::swap(btree_multiset& other)
+ //
+ // Exchanges the contents of this `btree_multiset` with those of the `other`
+ // btree_multiset, avoiding invocation of any move, copy, or swap operations
+ // on individual elements.
+ //
+ // All iterators and references on the `btree_multiset` remain valid,
+ // excepting for the past-the-end iterator, which is invalidated.
+ using Base::swap;
+
+ // btree_multiset::contains()
+ //
+ // template <typename K> bool contains(const K& key) const:
+ //
+ // Determines whether an element comparing equal to the given `key` exists
+ // within the `btree_multiset`, returning `true` if so or `false` otherwise.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::contains;
+
+ // btree_multiset::count()
+ //
+ // template <typename K> size_type count(const K& key) const:
+ //
+ // Returns the number of elements comparing equal to the given `key` within
+ // the `btree_multiset`.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::count;
+
+ // btree_multiset::equal_range()
+ //
+ // Returns a closed range [first, last], defined by a `std::pair` of two
+ // iterators, containing all elements with the passed key in the
+ // `btree_multiset`.
+ using Base::equal_range;
+
+ // btree_multiset::find()
+ //
+ // template <typename K> iterator find(const K& key):
+ // template <typename K> const_iterator find(const K& key) const:
+ //
+ // Finds an element with the passed `key` within the `btree_multiset`.
+ //
+ // Supports heterogeneous lookup, provided that the set is provided a
+ // compatible heterogeneous comparator.
+ using Base::find;
+
+ // btree_multiset::get_allocator()
+ //
+ // Returns the allocator function associated with this `btree_multiset`.
+ using Base::get_allocator;
+
+ // btree_multiset::key_comp();
+ //
+ // Returns the key comparator associated with this `btree_multiset`.
+ using Base::key_comp;
+
+ // btree_multiset::value_comp();
+ //
+ // Returns the value comparator associated with this `btree_multiset`. The
+ // keys to sort the elements are the values themselves, therefore `value_comp`
+ // and its sibling member function `key_comp` are equivalent.
+ using Base::value_comp;
+};
+
+// absl::swap(absl::btree_multiset<>, absl::btree_multiset<>)
+//
+// Swaps the contents of two `absl::btree_multiset` containers.
+template <typename K, typename C, typename A>
+void swap(btree_multiset<K, C, A> &x, btree_multiset<K, C, A> &y) {
+ return x.swap(y);
+}
+
+// absl::erase_if(absl::btree_multiset<>, Pred)
+//
+// Erases all elements that satisfy the predicate pred from the container.
+template <typename K, typename C, typename A, typename Pred>
+void erase_if(btree_multiset<K, C, A> &set, Pred pred) {
+ for (auto it = set.begin(); it != set.end();) {
+ if (pred(*it)) {
+ it = set.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_CONTAINER_BTREE_SET_H_
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc
new file mode 100644
index 00000000..9edf38f9
--- /dev/null
+++ b/absl/container/btree_test.cc
@@ -0,0 +1,2404 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/container/btree_test.h"
+
+#include <cstdint>
+#include <map>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/internal/raw_logging.h"
+#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_instance_tracker.h"
+#include "absl/flags/flag.h"
+#include "absl/hash/hash_testing.h"
+#include "absl/memory/memory.h"
+#include "absl/meta/type_traits.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/compare.h"
+
+ABSL_FLAG(int, test_values, 10000, "The number of values to use for tests");
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+namespace {
+
+using ::absl::test_internal::CopyableMovableInstance;
+using ::absl::test_internal::InstanceTracker;
+using ::absl::test_internal::MovableOnlyInstance;
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::IsEmpty;
+using ::testing::Pair;
+
+template <typename T, typename U>
+void CheckPairEquals(const T &x, const U &y) {
+ ABSL_INTERNAL_CHECK(x == y, "Values are unequal.");
+}
+
+template <typename T, typename U, typename V, typename W>
+void CheckPairEquals(const std::pair<T, U> &x, const std::pair<V, W> &y) {
+ CheckPairEquals(x.first, y.first);
+ CheckPairEquals(x.second, y.second);
+}
+} // namespace
+
+// The base class for a sorted associative container checker. TreeType is the
+// container type to check and CheckerType is the container type to check
+// against. TreeType is expected to be btree_{set,map,multiset,multimap} and
+// CheckerType is expected to be {set,map,multiset,multimap}.
+template <typename TreeType, typename CheckerType>
+class base_checker {
+ public:
+ using key_type = typename TreeType::key_type;
+ using value_type = typename TreeType::value_type;
+ using key_compare = typename TreeType::key_compare;
+ using pointer = typename TreeType::pointer;
+ using const_pointer = typename TreeType::const_pointer;
+ using reference = typename TreeType::reference;
+ using const_reference = typename TreeType::const_reference;
+ using size_type = typename TreeType::size_type;
+ using difference_type = typename TreeType::difference_type;
+ using iterator = typename TreeType::iterator;
+ using const_iterator = typename TreeType::const_iterator;
+ using reverse_iterator = typename TreeType::reverse_iterator;
+ using const_reverse_iterator = typename TreeType::const_reverse_iterator;
+
+ public:
+ base_checker() : const_tree_(tree_) {}
+ base_checker(const base_checker &x)
+ : tree_(x.tree_), const_tree_(tree_), checker_(x.checker_) {}
+ template <typename InputIterator>
+ base_checker(InputIterator b, InputIterator e)
+ : tree_(b, e), const_tree_(tree_), checker_(b, e) {}
+
+ iterator begin() { return tree_.begin(); }
+ const_iterator begin() const { return tree_.begin(); }
+ iterator end() { return tree_.end(); }
+ const_iterator end() const { return tree_.end(); }
+ reverse_iterator rbegin() { return tree_.rbegin(); }
+ const_reverse_iterator rbegin() const { return tree_.rbegin(); }
+ reverse_iterator rend() { return tree_.rend(); }
+ const_reverse_iterator rend() const { return tree_.rend(); }
+
+ template <typename IterType, typename CheckerIterType>
+ IterType iter_check(IterType tree_iter, CheckerIterType checker_iter) const {
+ if (tree_iter == tree_.end()) {
+ ABSL_INTERNAL_CHECK(checker_iter == checker_.end(),
+ "Checker iterator not at end.");
+ } else {
+ CheckPairEquals(*tree_iter, *checker_iter);
+ }
+ return tree_iter;
+ }
+ template <typename IterType, typename CheckerIterType>
+ IterType riter_check(IterType tree_iter, CheckerIterType checker_iter) const {
+ if (tree_iter == tree_.rend()) {
+ ABSL_INTERNAL_CHECK(checker_iter == checker_.rend(),
+ "Checker iterator not at rend.");
+ } else {
+ CheckPairEquals(*tree_iter, *checker_iter);
+ }
+ return tree_iter;
+ }
+ void value_check(const value_type &x) {
+ typename KeyOfValue<typename TreeType::key_type,
+ typename TreeType::value_type>::type key_of_value;
+ const key_type &key = key_of_value(x);
+ CheckPairEquals(*find(key), x);
+ lower_bound(key);
+ upper_bound(key);
+ equal_range(key);
+ contains(key);
+ count(key);
+ }
+ void erase_check(const key_type &key) {
+ EXPECT_FALSE(tree_.contains(key));
+ EXPECT_EQ(tree_.find(key), const_tree_.end());
+ EXPECT_FALSE(const_tree_.contains(key));
+ EXPECT_EQ(const_tree_.find(key), tree_.end());
+ EXPECT_EQ(tree_.equal_range(key).first,
+ const_tree_.equal_range(key).second);
+ }
+
+ iterator lower_bound(const key_type &key) {
+ return iter_check(tree_.lower_bound(key), checker_.lower_bound(key));
+ }
+ const_iterator lower_bound(const key_type &key) const {
+ return iter_check(tree_.lower_bound(key), checker_.lower_bound(key));
+ }
+ iterator upper_bound(const key_type &key) {
+ return iter_check(tree_.upper_bound(key), checker_.upper_bound(key));
+ }
+ const_iterator upper_bound(const key_type &key) const {
+ return iter_check(tree_.upper_bound(key), checker_.upper_bound(key));
+ }
+ std::pair<iterator, iterator> equal_range(const key_type &key) {
+ std::pair<typename CheckerType::iterator, typename CheckerType::iterator>
+ checker_res = checker_.equal_range(key);
+ std::pair<iterator, iterator> tree_res = tree_.equal_range(key);
+ iter_check(tree_res.first, checker_res.first);
+ iter_check(tree_res.second, checker_res.second);
+ return tree_res;
+ }
+ std::pair<const_iterator, const_iterator> equal_range(
+ const key_type &key) const {
+ std::pair<typename CheckerType::const_iterator,
+ typename CheckerType::const_iterator>
+ checker_res = checker_.equal_range(key);
+ std::pair<const_iterator, const_iterator> tree_res = tree_.equal_range(key);
+ iter_check(tree_res.first, checker_res.first);
+ iter_check(tree_res.second, checker_res.second);
+ return tree_res;
+ }
+ iterator find(const key_type &key) {
+ return iter_check(tree_.find(key), checker_.find(key));
+ }
+ const_iterator find(const key_type &key) const {
+ return iter_check(tree_.find(key), checker_.find(key));
+ }
+ bool contains(const key_type &key) const { return find(key) != end(); }
+ size_type count(const key_type &key) const {
+ size_type res = checker_.count(key);
+ EXPECT_EQ(res, tree_.count(key));
+ return res;
+ }
+
+ base_checker &operator=(const base_checker &x) {
+ tree_ = x.tree_;
+ checker_ = x.checker_;
+ return *this;
+ }
+
+ int erase(const key_type &key) {
+ int size = tree_.size();
+ int res = checker_.erase(key);
+ EXPECT_EQ(res, tree_.count(key));
+ EXPECT_EQ(res, tree_.erase(key));
+ EXPECT_EQ(tree_.count(key), 0);
+ EXPECT_EQ(tree_.size(), size - res);
+ erase_check(key);
+ return res;
+ }
+ iterator erase(iterator iter) {
+ key_type key = iter.key();
+ int size = tree_.size();
+ int count = tree_.count(key);
+ auto checker_iter = checker_.lower_bound(key);
+ for (iterator tmp(tree_.lower_bound(key)); tmp != iter; ++tmp) {
+ ++checker_iter;
+ }
+ auto checker_next = checker_iter;
+ ++checker_next;
+ checker_.erase(checker_iter);
+ iter = tree_.erase(iter);
+ EXPECT_EQ(tree_.size(), checker_.size());
+ EXPECT_EQ(tree_.size(), size - 1);
+ EXPECT_EQ(tree_.count(key), count - 1);
+ if (count == 1) {
+ erase_check(key);
+ }
+ return iter_check(iter, checker_next);
+ }
+
+ void erase(iterator begin, iterator end) {
+ int size = tree_.size();
+ int count = std::distance(begin, end);
+ auto checker_begin = checker_.lower_bound(begin.key());
+ for (iterator tmp(tree_.lower_bound(begin.key())); tmp != begin; ++tmp) {
+ ++checker_begin;
+ }
+ auto checker_end =
+ end == tree_.end() ? checker_.end() : checker_.lower_bound(end.key());
+ if (end != tree_.end()) {
+ for (iterator tmp(tree_.lower_bound(end.key())); tmp != end; ++tmp) {
+ ++checker_end;
+ }
+ }
+ const auto checker_ret = checker_.erase(checker_begin, checker_end);
+ const auto tree_ret = tree_.erase(begin, end);
+ EXPECT_EQ(std::distance(checker_.begin(), checker_ret),
+ std::distance(tree_.begin(), tree_ret));
+ EXPECT_EQ(tree_.size(), checker_.size());
+ EXPECT_EQ(tree_.size(), size - count);
+ }
+
+ void clear() {
+ tree_.clear();
+ checker_.clear();
+ }
+ void swap(base_checker &x) {
+ tree_.swap(x.tree_);
+ checker_.swap(x.checker_);
+ }
+
+ void verify() const {
+ tree_.verify();
+ EXPECT_EQ(tree_.size(), checker_.size());
+
+ // Move through the forward iterators using increment.
+ auto checker_iter = checker_.begin();
+ const_iterator tree_iter(tree_.begin());
+ for (; tree_iter != tree_.end(); ++tree_iter, ++checker_iter) {
+ CheckPairEquals(*tree_iter, *checker_iter);
+ }
+
+ // Move through the forward iterators using decrement.
+ for (int n = tree_.size() - 1; n >= 0; --n) {
+ iter_check(tree_iter, checker_iter);
+ --tree_iter;
+ --checker_iter;
+ }
+ EXPECT_EQ(tree_iter, tree_.begin());
+ EXPECT_EQ(checker_iter, checker_.begin());
+
+ // Move through the reverse iterators using increment.
+ auto checker_riter = checker_.rbegin();
+ const_reverse_iterator tree_riter(tree_.rbegin());
+ for (; tree_riter != tree_.rend(); ++tree_riter, ++checker_riter) {
+ CheckPairEquals(*tree_riter, *checker_riter);
+ }
+
+ // Move through the reverse iterators using decrement.
+ for (int n = tree_.size() - 1; n >= 0; --n) {
+ riter_check(tree_riter, checker_riter);
+ --tree_riter;
+ --checker_riter;
+ }
+ EXPECT_EQ(tree_riter, tree_.rbegin());
+ EXPECT_EQ(checker_riter, checker_.rbegin());
+ }
+
+ const TreeType &tree() const { return tree_; }
+
+ size_type size() const {
+ EXPECT_EQ(tree_.size(), checker_.size());
+ return tree_.size();
+ }
+ size_type max_size() const { return tree_.max_size(); }
+ bool empty() const {
+ EXPECT_EQ(tree_.empty(), checker_.empty());
+ return tree_.empty();
+ }
+
+ protected:
+ TreeType tree_;
+ const TreeType &const_tree_;
+ CheckerType checker_;
+};
+
+namespace {
+// A checker for unique sorted associative containers. TreeType is expected to
+// be btree_{set,map} and CheckerType is expected to be {set,map}.
+template <typename TreeType, typename CheckerType>
+class unique_checker : public base_checker<TreeType, CheckerType> {
+ using super_type = base_checker<TreeType, CheckerType>;
+
+ public:
+ using iterator = typename super_type::iterator;
+ using value_type = typename super_type::value_type;
+
+ public:
+ unique_checker() : super_type() {}
+ unique_checker(const unique_checker &x) : super_type(x) {}
+ template <class InputIterator>
+ unique_checker(InputIterator b, InputIterator e) : super_type(b, e) {}
+ unique_checker &operator=(const unique_checker &) = default;
+
+ // Insertion routines.
+ std::pair<iterator, bool> insert(const value_type &x) {
+ int size = this->tree_.size();
+ std::pair<typename CheckerType::iterator, bool> checker_res =
+ this->checker_.insert(x);
+ std::pair<iterator, bool> tree_res = this->tree_.insert(x);
+ CheckPairEquals(*tree_res.first, *checker_res.first);
+ EXPECT_EQ(tree_res.second, checker_res.second);
+ EXPECT_EQ(this->tree_.size(), this->checker_.size());
+ EXPECT_EQ(this->tree_.size(), size + tree_res.second);
+ return tree_res;
+ }
+ iterator insert(iterator position, const value_type &x) {
+ int size = this->tree_.size();
+ std::pair<typename CheckerType::iterator, bool> checker_res =
+ this->checker_.insert(x);
+ iterator tree_res = this->tree_.insert(position, x);
+ CheckPairEquals(*tree_res, *checker_res.first);
+ EXPECT_EQ(this->tree_.size(), this->checker_.size());
+ EXPECT_EQ(this->tree_.size(), size + checker_res.second);
+ return tree_res;
+ }
+ template <typename InputIterator>
+ void insert(InputIterator b, InputIterator e) {
+ for (; b != e; ++b) {
+ insert(*b);
+ }
+ }
+};
+
+// A checker for multiple sorted associative containers. TreeType is expected
+// to be btree_{multiset,multimap} and CheckerType is expected to be
+// {multiset,multimap}.
+template <typename TreeType, typename CheckerType>
+class multi_checker : public base_checker<TreeType, CheckerType> {
+ using super_type = base_checker<TreeType, CheckerType>;
+
+ public:
+ using iterator = typename super_type::iterator;
+ using value_type = typename super_type::value_type;
+
+ public:
+ multi_checker() : super_type() {}
+ multi_checker(const multi_checker &x) : super_type(x) {}
+ template <class InputIterator>
+ multi_checker(InputIterator b, InputIterator e) : super_type(b, e) {}
+ multi_checker &operator=(const multi_checker &) = default;
+
+ // Insertion routines.
+ iterator insert(const value_type &x) {
+ int size = this->tree_.size();
+ auto checker_res = this->checker_.insert(x);
+ iterator tree_res = this->tree_.insert(x);
+ CheckPairEquals(*tree_res, *checker_res);
+ EXPECT_EQ(this->tree_.size(), this->checker_.size());
+ EXPECT_EQ(this->tree_.size(), size + 1);
+ return tree_res;
+ }
+ iterator insert(iterator position, const value_type &x) {
+ int size = this->tree_.size();
+ auto checker_res = this->checker_.insert(x);
+ iterator tree_res = this->tree_.insert(position, x);
+ CheckPairEquals(*tree_res, *checker_res);
+ EXPECT_EQ(this->tree_.size(), this->checker_.size());
+ EXPECT_EQ(this->tree_.size(), size + 1);
+ return tree_res;
+ }
+ template <typename InputIterator>
+ void insert(InputIterator b, InputIterator e) {
+ for (; b != e; ++b) {
+ insert(*b);
+ }
+ }
+};
+
+template <typename T, typename V>
+void DoTest(const char *name, T *b, const std::vector<V> &values) {
+ typename KeyOfValue<typename T::key_type, V>::type key_of_value;
+
+ T &mutable_b = *b;
+ const T &const_b = *b;
+
+ // Test insert.
+ for (int i = 0; i < values.size(); ++i) {
+ mutable_b.insert(values[i]);
+ mutable_b.value_check(values[i]);
+ }
+ ASSERT_EQ(mutable_b.size(), values.size());
+
+ const_b.verify();
+
+ // Test copy constructor.
+ T b_copy(const_b);
+ EXPECT_EQ(b_copy.size(), const_b.size());
+ for (int i = 0; i < values.size(); ++i) {
+ CheckPairEquals(*b_copy.find(key_of_value(values[i])), values[i]);
+ }
+
+ // Test range constructor.
+ T b_range(const_b.begin(), const_b.end());
+ EXPECT_EQ(b_range.size(), const_b.size());
+ for (int i = 0; i < values.size(); ++i) {
+ CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]);
+ }
+
+ // Test range insertion for values that already exist.
+ b_range.insert(b_copy.begin(), b_copy.end());
+ b_range.verify();
+
+ // Test range insertion for new values.
+ b_range.clear();
+ b_range.insert(b_copy.begin(), b_copy.end());
+ EXPECT_EQ(b_range.size(), b_copy.size());
+ for (int i = 0; i < values.size(); ++i) {
+ CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]);
+ }
+
+ // Test assignment to self. Nothing should change.
+ b_range.operator=(b_range);
+ EXPECT_EQ(b_range.size(), b_copy.size());
+
+ // Test assignment of new values.
+ b_range.clear();
+ b_range = b_copy;
+ EXPECT_EQ(b_range.size(), b_copy.size());
+
+ // Test swap.
+ b_range.clear();
+ b_range.swap(b_copy);
+ EXPECT_EQ(b_copy.size(), 0);
+ EXPECT_EQ(b_range.size(), const_b.size());
+ for (int i = 0; i < values.size(); ++i) {
+ CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]);
+ }
+ b_range.swap(b_copy);
+
+ // Test non-member function swap.
+ swap(b_range, b_copy);
+ EXPECT_EQ(b_copy.size(), 0);
+ EXPECT_EQ(b_range.size(), const_b.size());
+ for (int i = 0; i < values.size(); ++i) {
+ CheckPairEquals(*b_range.find(key_of_value(values[i])), values[i]);
+ }
+ swap(b_range, b_copy);
+
+ // Test erase via values.
+ for (int i = 0; i < values.size(); ++i) {
+ mutable_b.erase(key_of_value(values[i]));
+ // Erasing a non-existent key should have no effect.
+ ASSERT_EQ(mutable_b.erase(key_of_value(values[i])), 0);
+ }
+
+ const_b.verify();
+ EXPECT_EQ(const_b.size(), 0);
+
+ // Test erase via iterators.
+ mutable_b = b_copy;
+ for (int i = 0; i < values.size(); ++i) {
+ mutable_b.erase(mutable_b.find(key_of_value(values[i])));
+ }
+
+ const_b.verify();
+ EXPECT_EQ(const_b.size(), 0);
+
+ // Test insert with hint.
+ for (int i = 0; i < values.size(); i++) {
+ mutable_b.insert(mutable_b.upper_bound(key_of_value(values[i])), values[i]);
+ }
+
+ const_b.verify();
+
+ // Test range erase.
+ mutable_b.erase(mutable_b.begin(), mutable_b.end());
+ EXPECT_EQ(mutable_b.size(), 0);
+ const_b.verify();
+
+ // First half.
+ mutable_b = b_copy;
+ typename T::iterator mutable_iter_end = mutable_b.begin();
+ for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_end;
+ mutable_b.erase(mutable_b.begin(), mutable_iter_end);
+ EXPECT_EQ(mutable_b.size(), values.size() - values.size() / 2);
+ const_b.verify();
+
+ // Second half.
+ mutable_b = b_copy;
+ typename T::iterator mutable_iter_begin = mutable_b.begin();
+ for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_begin;
+ mutable_b.erase(mutable_iter_begin, mutable_b.end());
+ EXPECT_EQ(mutable_b.size(), values.size() / 2);
+ const_b.verify();
+
+ // Second quarter.
+ mutable_b = b_copy;
+ mutable_iter_begin = mutable_b.begin();
+ for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_begin;
+ mutable_iter_end = mutable_iter_begin;
+ for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_end;
+ mutable_b.erase(mutable_iter_begin, mutable_iter_end);
+ EXPECT_EQ(mutable_b.size(), values.size() - values.size() / 4);
+ const_b.verify();
+
+ mutable_b.clear();
+}
+
+template <typename T>
+void ConstTest() {
+ using value_type = typename T::value_type;
+ typename KeyOfValue<typename T::key_type, value_type>::type key_of_value;
+
+ T mutable_b;
+ const T &const_b = mutable_b;
+
+ // Insert a single value into the container and test looking it up.
+ value_type value = Generator<value_type>(2)(2);
+ mutable_b.insert(value);
+ EXPECT_TRUE(mutable_b.contains(key_of_value(value)));
+ EXPECT_NE(mutable_b.find(key_of_value(value)), const_b.end());
+ EXPECT_TRUE(const_b.contains(key_of_value(value)));
+ EXPECT_NE(const_b.find(key_of_value(value)), mutable_b.end());
+ EXPECT_EQ(*const_b.lower_bound(key_of_value(value)), value);
+ EXPECT_EQ(const_b.upper_bound(key_of_value(value)), const_b.end());
+ EXPECT_EQ(*const_b.equal_range(key_of_value(value)).first, value);
+
+ // We can only create a non-const iterator from a non-const container.
+ typename T::iterator mutable_iter(mutable_b.begin());
+ EXPECT_EQ(mutable_iter, const_b.begin());
+ EXPECT_NE(mutable_iter, const_b.end());
+ EXPECT_EQ(const_b.begin(), mutable_iter);
+ EXPECT_NE(const_b.end(), mutable_iter);
+ typename T::reverse_iterator mutable_riter(mutable_b.rbegin());
+ EXPECT_EQ(mutable_riter, const_b.rbegin());
+ EXPECT_NE(mutable_riter, const_b.rend());
+ EXPECT_EQ(const_b.rbegin(), mutable_riter);
+ EXPECT_NE(const_b.rend(), mutable_riter);
+
+ // We can create a const iterator from a non-const iterator.
+ typename T::const_iterator const_iter(mutable_iter);
+ EXPECT_EQ(const_iter, mutable_b.begin());
+ EXPECT_NE(const_iter, mutable_b.end());
+ EXPECT_EQ(mutable_b.begin(), const_iter);
+ EXPECT_NE(mutable_b.end(), const_iter);
+ typename T::const_reverse_iterator const_riter(mutable_riter);
+ EXPECT_EQ(const_riter, mutable_b.rbegin());
+ EXPECT_NE(const_riter, mutable_b.rend());
+ EXPECT_EQ(mutable_b.rbegin(), const_riter);
+ EXPECT_NE(mutable_b.rend(), const_riter);
+
+ // Make sure various methods can be invoked on a const container.
+ const_b.verify();
+ ASSERT_TRUE(!const_b.empty());
+ EXPECT_EQ(const_b.size(), 1);
+ EXPECT_GT(const_b.max_size(), 0);
+ EXPECT_TRUE(const_b.contains(key_of_value(value)));
+ EXPECT_EQ(const_b.count(key_of_value(value)), 1);
+}
+
+template <typename T, typename C>
+void BtreeTest() {
+ ConstTest<T>();
+
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ const std::vector<V> random_values = GenerateValuesWithSeed<V>(
+ absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values),
+ testing::GTEST_FLAG(random_seed));
+
+ unique_checker<T, C> container;
+
+ // Test key insertion/deletion in sorted order.
+ std::vector<V> sorted_values(random_values);
+ std::sort(sorted_values.begin(), sorted_values.end());
+ DoTest("sorted: ", &container, sorted_values);
+
+ // Test key insertion/deletion in reverse sorted order.
+ std::reverse(sorted_values.begin(), sorted_values.end());
+ DoTest("rsorted: ", &container, sorted_values);
+
+ // Test key insertion/deletion in random order.
+ DoTest("random: ", &container, random_values);
+}
+
+template <typename T, typename C>
+void BtreeMultiTest() {
+ ConstTest<T>();
+
+ using V = typename remove_pair_const<typename T::value_type>::type;
+ const std::vector<V> random_values = GenerateValuesWithSeed<V>(
+ absl::GetFlag(FLAGS_test_values), 4 * absl::GetFlag(FLAGS_test_values),
+ testing::GTEST_FLAG(random_seed));
+
+ multi_checker<T, C> container;
+
+ // Test keys in sorted order.
+ std::vector<V> sorted_values(random_values);
+ std::sort(sorted_values.begin(), sorted_values.end());
+ DoTest("sorted: ", &container, sorted_values);
+
+ // Test keys in reverse sorted order.
+ std::reverse(sorted_values.begin(), sorted_values.end());
+ DoTest("rsorted: ", &container, sorted_values);
+
+ // Test keys in random order.
+ DoTest("random: ", &container, random_values);
+
+ // Test keys in random order w/ duplicates.
+ std::vector<V> duplicate_values(random_values);
+ duplicate_values.insert(duplicate_values.end(), random_values.begin(),
+ random_values.end());
+ DoTest("duplicates:", &container, duplicate_values);
+
+ // Test all identical keys.
+ std::vector<V> identical_values(100);
+ std::fill(identical_values.begin(), identical_values.end(),
+ Generator<V>(2)(2));
+ DoTest("identical: ", &container, identical_values);
+}
+
+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;
+
+ mapped_type m = Generator<mapped_type>(0)(0);
+ (void)m;
+
+ T b;
+
+ // Verify we can insert using operator[].
+ for (int i = 0; i < 1000; i++) {
+ value_type v = Generator<value_type>(1000)(i);
+ b[v.first] = v.second;
+ }
+ EXPECT_EQ(b.size(), 1000);
+
+ // Test whether we can use the "->" operator on iterators and
+ // reverse_iterators. This stresses the btree_map_params::pair_pointer
+ // mechanism.
+ EXPECT_EQ(b.begin()->first, Generator<value_type>(1000)(0).first);
+ EXPECT_EQ(b.begin()->second, Generator<value_type>(1000)(0).second);
+ EXPECT_EQ(b.rbegin()->first, Generator<value_type>(1000)(999).first);
+ EXPECT_EQ(b.rbegin()->second, Generator<value_type>(1000)(999).second);
+}
+
+template <typename T>
+void BtreeMultiMapTest() {
+ using mapped_type = typename T::mapped_type;
+ mapped_type m = Generator<mapped_type>(0)(0);
+ (void)m;
+}
+
+template <typename K, int N = 256>
+void SetTest() {
+ EXPECT_EQ(
+ 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>
+void MapTest() {
+ EXPECT_EQ(
+ 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_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_pair) { MapTest<std::pair<int, int>>(); }
+
+template <typename K, int N = 256>
+void MultiSetTest() {
+ EXPECT_EQ(
+ 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>
+void MultiMapTest() {
+ EXPECT_EQ(sizeof(absl::btree_multimap<K, K>),
+ 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_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_pair) { MultiMapTest<std::pair<int, int>>(); }
+
+struct CompareIntToString {
+ bool operator()(const std::string &a, const std::string &b) const {
+ return a < b;
+ }
+ bool operator()(const std::string &a, int b) const {
+ return a < absl::StrCat(b);
+ }
+ bool operator()(int a, const std::string &b) const {
+ return absl::StrCat(a) < b;
+ }
+ using is_transparent = void;
+};
+
+struct NonTransparentCompare {
+ template <typename T, typename U>
+ bool operator()(const T &t, const U &u) const {
+ // Treating all comparators as transparent can cause inefficiencies (see
+ // N3657 C++ proposal). Test that for comparators without 'is_transparent'
+ // alias (like this one), we do not attempt heterogeneous lookup.
+ EXPECT_TRUE((std::is_same<T, U>()));
+ return t < u;
+ }
+};
+
+template <typename T>
+bool CanEraseWithEmptyBrace(T t, decltype(t.erase({})) *) {
+ return true;
+}
+
+template <typename T>
+bool CanEraseWithEmptyBrace(T, ...) {
+ return false;
+}
+
+template <typename T>
+void TestHeterogeneous(T table) {
+ auto lb = table.lower_bound("3");
+ EXPECT_EQ(lb, table.lower_bound(3));
+ EXPECT_NE(lb, table.lower_bound(4));
+ EXPECT_EQ(lb, table.lower_bound({"3"}));
+ EXPECT_NE(lb, table.lower_bound({}));
+
+ auto ub = table.upper_bound("3");
+ EXPECT_EQ(ub, table.upper_bound(3));
+ EXPECT_NE(ub, table.upper_bound(5));
+ EXPECT_EQ(ub, table.upper_bound({"3"}));
+ EXPECT_NE(ub, table.upper_bound({}));
+
+ auto er = table.equal_range("3");
+ EXPECT_EQ(er, table.equal_range(3));
+ EXPECT_NE(er, table.equal_range(4));
+ EXPECT_EQ(er, table.equal_range({"3"}));
+ EXPECT_NE(er, table.equal_range({}));
+
+ auto it = table.find("3");
+ EXPECT_EQ(it, table.find(3));
+ EXPECT_NE(it, table.find(4));
+ EXPECT_EQ(it, table.find({"3"}));
+ EXPECT_NE(it, table.find({}));
+
+ EXPECT_TRUE(table.contains(3));
+ EXPECT_FALSE(table.contains(4));
+ EXPECT_TRUE(table.count({"3"}));
+ EXPECT_FALSE(table.contains({}));
+
+ EXPECT_EQ(1, table.count(3));
+ EXPECT_EQ(0, table.count(4));
+ EXPECT_EQ(1, table.count({"3"}));
+ EXPECT_EQ(0, table.count({}));
+
+ auto copy = table;
+ copy.erase(3);
+ EXPECT_EQ(table.size() - 1, copy.size());
+ copy.erase(4);
+ EXPECT_EQ(table.size() - 1, copy.size());
+ copy.erase({"5"});
+ EXPECT_EQ(table.size() - 2, copy.size());
+ EXPECT_FALSE(CanEraseWithEmptyBrace(table, nullptr));
+
+ // Also run it with const T&.
+ if (std::is_class<T>()) TestHeterogeneous<const T &>(table);
+}
+
+TEST(Btree, HeterogeneousLookup) {
+ TestHeterogeneous(btree_set<std::string, CompareIntToString>{"1", "3", "5"});
+ TestHeterogeneous(btree_map<std::string, int, CompareIntToString>{
+ {"1", 1}, {"3", 3}, {"5", 5}});
+ TestHeterogeneous(
+ btree_multiset<std::string, CompareIntToString>{"1", "3", "5"});
+ TestHeterogeneous(btree_multimap<std::string, int, CompareIntToString>{
+ {"1", 1}, {"3", 3}, {"5", 5}});
+
+ // Only maps have .at()
+ btree_map<std::string, int, CompareIntToString> map{
+ {"", -1}, {"1", 1}, {"3", 3}, {"5", 5}};
+ EXPECT_EQ(1, map.at(1));
+ EXPECT_EQ(3, map.at({"3"}));
+ EXPECT_EQ(-1, map.at({}));
+ const auto &cmap = map;
+ EXPECT_EQ(1, cmap.at(1));
+ EXPECT_EQ(3, cmap.at({"3"}));
+ EXPECT_EQ(-1, cmap.at({}));
+}
+
+TEST(Btree, NoHeterogeneousLookupWithoutAlias) {
+ using StringSet = absl::btree_set<std::string, NonTransparentCompare>;
+ StringSet s;
+ ASSERT_TRUE(s.insert("hello").second);
+ ASSERT_TRUE(s.insert("world").second);
+ EXPECT_TRUE(s.end() == s.find("blah"));
+ EXPECT_TRUE(s.begin() == s.lower_bound("hello"));
+ EXPECT_EQ(1, s.count("world"));
+ EXPECT_TRUE(s.contains("hello"));
+ EXPECT_TRUE(s.contains("world"));
+ EXPECT_FALSE(s.contains("blah"));
+
+ using StringMultiSet =
+ absl::btree_multiset<std::string, NonTransparentCompare>;
+ StringMultiSet ms;
+ ms.insert("hello");
+ ms.insert("world");
+ ms.insert("world");
+ EXPECT_TRUE(ms.end() == ms.find("blah"));
+ EXPECT_TRUE(ms.begin() == ms.lower_bound("hello"));
+ EXPECT_EQ(2, ms.count("world"));
+ EXPECT_TRUE(ms.contains("hello"));
+ EXPECT_TRUE(ms.contains("world"));
+ EXPECT_FALSE(ms.contains("blah"));
+}
+
+TEST(Btree, DefaultTransparent) {
+ {
+ // `int` does not have a default transparent comparator.
+ // The input value is converted to key_type.
+ btree_set<int> s = {1};
+ double d = 1.1;
+ EXPECT_EQ(s.begin(), s.find(d));
+ EXPECT_TRUE(s.contains(d));
+ }
+
+ {
+ // `std::string` has heterogeneous support.
+ btree_set<std::string> s = {"A"};
+ EXPECT_EQ(s.begin(), s.find(absl::string_view("A")));
+ EXPECT_TRUE(s.contains(absl::string_view("A")));
+ }
+}
+
+class StringLike {
+ public:
+ StringLike() = default;
+
+ StringLike(const char *s) : s_(s) { // NOLINT
+ ++constructor_calls_;
+ }
+
+ bool operator<(const StringLike &a) const { return s_ < a.s_; }
+
+ static void clear_constructor_call_count() { constructor_calls_ = 0; }
+
+ static int constructor_calls() { return constructor_calls_; }
+
+ private:
+ static int constructor_calls_;
+ std::string s_;
+};
+
+int StringLike::constructor_calls_ = 0;
+
+TEST(Btree, HeterogeneousLookupDoesntDegradePerformance) {
+ using StringSet = absl::btree_set<StringLike>;
+ StringSet s;
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_TRUE(s.insert(absl::StrCat(i).c_str()).second);
+ }
+ StringLike::clear_constructor_call_count();
+ s.find("50");
+ ASSERT_EQ(1, StringLike::constructor_calls());
+
+ StringLike::clear_constructor_call_count();
+ s.contains("50");
+ ASSERT_EQ(1, StringLike::constructor_calls());
+
+ StringLike::clear_constructor_call_count();
+ s.count("50");
+ ASSERT_EQ(1, StringLike::constructor_calls());
+
+ StringLike::clear_constructor_call_count();
+ s.lower_bound("50");
+ ASSERT_EQ(1, StringLike::constructor_calls());
+
+ StringLike::clear_constructor_call_count();
+ s.upper_bound("50");
+ ASSERT_EQ(1, StringLike::constructor_calls());
+
+ StringLike::clear_constructor_call_count();
+ s.equal_range("50");
+ ASSERT_EQ(1, StringLike::constructor_calls());
+
+ StringLike::clear_constructor_call_count();
+ s.erase("50");
+ ASSERT_EQ(1, StringLike::constructor_calls());
+}
+
+// Verify that swapping btrees swaps the key comparison functors and that we can
+// use non-default constructible comparators.
+struct SubstringLess {
+ SubstringLess() = delete;
+ explicit SubstringLess(int length) : n(length) {}
+ bool operator()(const std::string &a, const std::string &b) const {
+ return absl::string_view(a).substr(0, n) <
+ absl::string_view(b).substr(0, n);
+ }
+ int n;
+};
+
+TEST(Btree, SwapKeyCompare) {
+ using SubstringSet = absl::btree_set<std::string, SubstringLess>;
+ SubstringSet s1(SubstringLess(1), SubstringSet::allocator_type());
+ SubstringSet s2(SubstringLess(2), SubstringSet::allocator_type());
+
+ ASSERT_TRUE(s1.insert("a").second);
+ ASSERT_FALSE(s1.insert("aa").second);
+
+ ASSERT_TRUE(s2.insert("a").second);
+ ASSERT_TRUE(s2.insert("aa").second);
+ ASSERT_FALSE(s2.insert("aaa").second);
+
+ swap(s1, s2);
+
+ ASSERT_TRUE(s1.insert("b").second);
+ ASSERT_TRUE(s1.insert("bb").second);
+ ASSERT_FALSE(s1.insert("bbb").second);
+
+ ASSERT_TRUE(s2.insert("b").second);
+ ASSERT_FALSE(s2.insert("bb").second);
+}
+
+TEST(Btree, UpperBoundRegression) {
+ // Regress a bug where upper_bound would default-construct a new key_compare
+ // instead of copying the existing one.
+ using SubstringSet = absl::btree_set<std::string, SubstringLess>;
+ SubstringSet my_set(SubstringLess(3));
+ my_set.insert("aab");
+ my_set.insert("abb");
+ // We call upper_bound("aaa"). If this correctly uses the length 3
+ // comparator, aaa < aab < abb, so we should get aab as the result.
+ // If it instead uses the default-constructed length 2 comparator,
+ // aa == aa < ab, so we'll get abb as our result.
+ SubstringSet::iterator it = my_set.upper_bound("aaa");
+ ASSERT_TRUE(it != my_set.end());
+ EXPECT_EQ("aab", *it);
+}
+
+TEST(Btree, Comparison) {
+ const int kSetSize = 1201;
+ absl::btree_set<int64_t> my_set;
+ for (int i = 0; i < kSetSize; ++i) {
+ my_set.insert(i);
+ }
+ absl::btree_set<int64_t> my_set_copy(my_set);
+ EXPECT_TRUE(my_set_copy == my_set);
+ EXPECT_TRUE(my_set == my_set_copy);
+ EXPECT_FALSE(my_set_copy != my_set);
+ EXPECT_FALSE(my_set != my_set_copy);
+
+ my_set.insert(kSetSize);
+ EXPECT_FALSE(my_set_copy == my_set);
+ EXPECT_FALSE(my_set == my_set_copy);
+ EXPECT_TRUE(my_set_copy != my_set);
+ EXPECT_TRUE(my_set != my_set_copy);
+
+ my_set.erase(kSetSize - 1);
+ EXPECT_FALSE(my_set_copy == my_set);
+ EXPECT_FALSE(my_set == my_set_copy);
+ EXPECT_TRUE(my_set_copy != my_set);
+ EXPECT_TRUE(my_set != my_set_copy);
+
+ absl::btree_map<std::string, int64_t> my_map;
+ for (int i = 0; i < kSetSize; ++i) {
+ my_map[std::string(i, 'a')] = i;
+ }
+ absl::btree_map<std::string, int64_t> my_map_copy(my_map);
+ EXPECT_TRUE(my_map_copy == my_map);
+ EXPECT_TRUE(my_map == my_map_copy);
+ EXPECT_FALSE(my_map_copy != my_map);
+ EXPECT_FALSE(my_map != my_map_copy);
+
+ ++my_map_copy[std::string(7, 'a')];
+ EXPECT_FALSE(my_map_copy == my_map);
+ EXPECT_FALSE(my_map == my_map_copy);
+ EXPECT_TRUE(my_map_copy != my_map);
+ EXPECT_TRUE(my_map != my_map_copy);
+
+ my_map_copy = my_map;
+ my_map["hello"] = kSetSize;
+ EXPECT_FALSE(my_map_copy == my_map);
+ EXPECT_FALSE(my_map == my_map_copy);
+ EXPECT_TRUE(my_map_copy != my_map);
+ EXPECT_TRUE(my_map != my_map_copy);
+
+ my_map.erase(std::string(kSetSize - 1, 'a'));
+ EXPECT_FALSE(my_map_copy == my_map);
+ EXPECT_FALSE(my_map == my_map_copy);
+ EXPECT_TRUE(my_map_copy != my_map);
+ EXPECT_TRUE(my_map != my_map_copy);
+}
+
+TEST(Btree, RangeCtorSanity) {
+ std::vector<int> ivec;
+ ivec.push_back(1);
+ std::map<int, int> imap;
+ imap.insert(std::make_pair(1, 2));
+ absl::btree_multiset<int> tmset(ivec.begin(), ivec.end());
+ absl::btree_multimap<int, int> tmmap(imap.begin(), imap.end());
+ absl::btree_set<int> tset(ivec.begin(), ivec.end());
+ absl::btree_map<int, int> tmap(imap.begin(), imap.end());
+ EXPECT_EQ(1, tmset.size());
+ EXPECT_EQ(1, tmmap.size());
+ EXPECT_EQ(1, tset.size());
+ EXPECT_EQ(1, tmap.size());
+}
+
+TEST(Btree, BtreeMapCanHoldMoveOnlyTypes) {
+ absl::btree_map<std::string, std::unique_ptr<std::string>> m;
+
+ std::unique_ptr<std::string> &v = m["A"];
+ EXPECT_TRUE(v == nullptr);
+ v.reset(new std::string("X"));
+
+ auto iter = m.find("A");
+ EXPECT_EQ("X", *iter->second);
+}
+
+TEST(Btree, InitializerListConstructor) {
+ absl::btree_set<std::string> set({"a", "b"});
+ EXPECT_EQ(set.count("a"), 1);
+ EXPECT_EQ(set.count("b"), 1);
+
+ absl::btree_multiset<int> mset({1, 1, 4});
+ EXPECT_EQ(mset.count(1), 2);
+ EXPECT_EQ(mset.count(4), 1);
+
+ absl::btree_map<int, int> map({{1, 5}, {2, 10}});
+ EXPECT_EQ(map[1], 5);
+ EXPECT_EQ(map[2], 10);
+
+ absl::btree_multimap<int, int> mmap({{1, 5}, {1, 10}});
+ auto range = mmap.equal_range(1);
+ auto it = range.first;
+ ASSERT_NE(it, range.second);
+ EXPECT_EQ(it->second, 5);
+ ASSERT_NE(++it, range.second);
+ EXPECT_EQ(it->second, 10);
+ EXPECT_EQ(++it, range.second);
+}
+
+TEST(Btree, InitializerListInsert) {
+ absl::btree_set<std::string> set;
+ set.insert({"a", "b"});
+ EXPECT_EQ(set.count("a"), 1);
+ EXPECT_EQ(set.count("b"), 1);
+
+ absl::btree_multiset<int> mset;
+ mset.insert({1, 1, 4});
+ EXPECT_EQ(mset.count(1), 2);
+ EXPECT_EQ(mset.count(4), 1);
+
+ absl::btree_map<int, int> map;
+ map.insert({{1, 5}, {2, 10}});
+ // Test that inserting one element using an initializer list also works.
+ map.insert({3, 15});
+ EXPECT_EQ(map[1], 5);
+ EXPECT_EQ(map[2], 10);
+ EXPECT_EQ(map[3], 15);
+
+ absl::btree_multimap<int, int> mmap;
+ mmap.insert({{1, 5}, {1, 10}});
+ auto range = mmap.equal_range(1);
+ auto it = range.first;
+ ASSERT_NE(it, range.second);
+ EXPECT_EQ(it->second, 5);
+ ASSERT_NE(++it, range.second);
+ EXPECT_EQ(it->second, 10);
+ EXPECT_EQ(++it, range.second);
+}
+
+template <typename Compare, typename K>
+void AssertKeyCompareToAdapted() {
+ using Adapted = typename key_compare_to_adapter<Compare>::type;
+ static_assert(!std::is_same<Adapted, Compare>::value,
+ "key_compare_to_adapter should have adapted this comparator.");
+ static_assert(
+ std::is_same<absl::weak_ordering,
+ absl::result_of_t<Adapted(const K &, const K &)>>::value,
+ "Adapted comparator should be a key-compare-to comparator.");
+}
+template <typename Compare, typename K>
+void AssertKeyCompareToNotAdapted() {
+ using Unadapted = typename key_compare_to_adapter<Compare>::type;
+ static_assert(
+ std::is_same<Unadapted, Compare>::value,
+ "key_compare_to_adapter shouldn't have adapted this comparator.");
+ static_assert(
+ std::is_same<bool,
+ absl::result_of_t<Unadapted(const K &, const K &)>>::value,
+ "Un-adapted comparator should return bool.");
+}
+
+TEST(Btree, KeyCompareToAdapter) {
+ AssertKeyCompareToAdapted<std::less<std::string>, std::string>();
+ AssertKeyCompareToAdapted<std::greater<std::string>, std::string>();
+ AssertKeyCompareToAdapted<std::less<absl::string_view>, absl::string_view>();
+ AssertKeyCompareToAdapted<std::greater<absl::string_view>,
+ absl::string_view>();
+ AssertKeyCompareToNotAdapted<std::less<int>, int>();
+ AssertKeyCompareToNotAdapted<std::greater<int>, int>();
+}
+
+TEST(Btree, RValueInsert) {
+ InstanceTracker tracker;
+
+ absl::btree_set<MovableOnlyInstance> set;
+ set.insert(MovableOnlyInstance(1));
+ set.insert(MovableOnlyInstance(3));
+ MovableOnlyInstance two(2);
+ set.insert(set.find(MovableOnlyInstance(3)), std::move(two));
+ auto it = set.find(MovableOnlyInstance(2));
+ ASSERT_NE(it, set.end());
+ ASSERT_NE(++it, set.end());
+ EXPECT_EQ(it->value(), 3);
+
+ absl::btree_multiset<MovableOnlyInstance> mset;
+ MovableOnlyInstance zero(0);
+ MovableOnlyInstance zero2(0);
+ mset.insert(std::move(zero));
+ mset.insert(mset.find(MovableOnlyInstance(0)), std::move(zero2));
+ EXPECT_EQ(mset.count(MovableOnlyInstance(0)), 2);
+
+ absl::btree_map<int, MovableOnlyInstance> map;
+ std::pair<const int, MovableOnlyInstance> p1 = {1, MovableOnlyInstance(5)};
+ std::pair<const int, MovableOnlyInstance> p2 = {2, MovableOnlyInstance(10)};
+ std::pair<const int, MovableOnlyInstance> p3 = {3, MovableOnlyInstance(15)};
+ map.insert(std::move(p1));
+ map.insert(std::move(p3));
+ map.insert(map.find(3), std::move(p2));
+ ASSERT_NE(map.find(2), map.end());
+ EXPECT_EQ(map.find(2)->second.value(), 10);
+
+ absl::btree_multimap<int, MovableOnlyInstance> mmap;
+ std::pair<const int, MovableOnlyInstance> p4 = {1, MovableOnlyInstance(5)};
+ std::pair<const int, MovableOnlyInstance> p5 = {1, MovableOnlyInstance(10)};
+ mmap.insert(std::move(p4));
+ mmap.insert(mmap.find(1), std::move(p5));
+ auto range = mmap.equal_range(1);
+ auto it1 = range.first;
+ ASSERT_NE(it1, range.second);
+ EXPECT_EQ(it1->second.value(), 10);
+ ASSERT_NE(++it1, range.second);
+ EXPECT_EQ(it1->second.value(), 5);
+ EXPECT_EQ(++it1, range.second);
+
+ EXPECT_EQ(tracker.copies(), 0);
+ EXPECT_EQ(tracker.swaps(), 0);
+}
+
+} // namespace
+
+class BtreeNodePeer {
+ public:
+ // Yields the size of a leaf node with a specific number of values.
+ template <typename ValueType>
+ constexpr static size_t GetTargetNodeSize(size_t target_values_per_node) {
+ return btree_node<
+ set_params<ValueType, std::less<ValueType>, std::allocator<ValueType>,
+ /*TargetNodeSize=*/256, // This parameter isn't used here.
+ /*Multi=*/false>>::SizeWithNValues(target_values_per_node);
+ }
+
+ // Yields the number of values in a (non-root) leaf node for this set.
+ template <typename Set>
+ constexpr static size_t GetNumValuesPerNode() {
+ return btree_node<typename Set::params_type>::kNodeValues;
+ }
+};
+
+namespace {
+
+// A btree set with a specific number of values per node.
+template <typename Key, int TargetValuesPerNode, typename Cmp = std::less<Key>>
+class SizedBtreeSet
+ : public btree_set_container<btree<
+ set_params<Key, Cmp, std::allocator<Key>,
+ BtreeNodePeer::GetTargetNodeSize<Key>(TargetValuesPerNode),
+ /*Multi=*/false>>> {
+ using Base = typename SizedBtreeSet::btree_set_container;
+
+ public:
+ SizedBtreeSet() {}
+ using Base::Base;
+};
+
+template <typename Set>
+void ExpectOperationCounts(const int expected_moves,
+ const int expected_comparisons,
+ const std::vector<int> &values,
+ InstanceTracker *tracker, Set *set) {
+ for (const int v : values) set->insert(MovableOnlyInstance(v));
+ set->clear();
+ EXPECT_EQ(tracker->moves(), expected_moves);
+ EXPECT_EQ(tracker->comparisons(), expected_comparisons);
+ EXPECT_EQ(tracker->copies(), 0);
+ EXPECT_EQ(tracker->swaps(), 0);
+ tracker->ResetCopiesMovesSwaps();
+}
+
+// Note: when the values in this test change, it is expected to have an impact
+// on performance.
+TEST(Btree, MovesComparisonsCopiesSwapsTracking) {
+ InstanceTracker tracker;
+ // Note: this is minimum number of values per node.
+ SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3> set3;
+ // Note: this is the default number of values per node for a set of int32s
+ // (with 64-bit pointers).
+ SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61> set61;
+ SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/100> set100;
+
+ // Don't depend on flags for random values because then the expectations will
+ // fail if the flags change.
+ std::vector<int> values =
+ GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23);
+
+ EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3);
+ EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61);
+ EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100);
+ if (sizeof(void *) == 8) {
+ EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(),
+ BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>());
+ }
+
+ // Test key insertion/deletion in random order.
+ ExpectOperationCounts(45281, 132551, values, &tracker, &set3);
+ ExpectOperationCounts(386718, 129807, values, &tracker, &set61);
+ ExpectOperationCounts(586761, 130310, values, &tracker, &set100);
+
+ // Test key insertion/deletion in sorted order.
+ std::sort(values.begin(), values.end());
+ ExpectOperationCounts(26638, 92134, values, &tracker, &set3);
+ ExpectOperationCounts(20208, 87757, values, &tracker, &set61);
+ ExpectOperationCounts(20124, 96583, values, &tracker, &set100);
+
+ // Test key insertion/deletion in reverse sorted order.
+ std::reverse(values.begin(), values.end());
+ ExpectOperationCounts(49951, 119325, values, &tracker, &set3);
+ ExpectOperationCounts(338813, 118266, values, &tracker, &set61);
+ ExpectOperationCounts(534529, 125279, values, &tracker, &set100);
+}
+
+struct MovableOnlyInstanceThreeWayCompare {
+ absl::weak_ordering operator()(const MovableOnlyInstance &a,
+ const MovableOnlyInstance &b) const {
+ return a.compare(b);
+ }
+};
+
+// Note: when the values in this test change, it is expected to have an impact
+// on performance.
+TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) {
+ InstanceTracker tracker;
+ // Note: this is minimum number of values per node.
+ SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3,
+ MovableOnlyInstanceThreeWayCompare>
+ set3;
+ // Note: this is the default number of values per node for a set of int32s
+ // (with 64-bit pointers).
+ SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61,
+ MovableOnlyInstanceThreeWayCompare>
+ set61;
+ SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/100,
+ MovableOnlyInstanceThreeWayCompare>
+ set100;
+
+ // Don't depend on flags for random values because then the expectations will
+ // fail if the flags change.
+ std::vector<int> values =
+ GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23);
+
+ EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3);
+ EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61);
+ EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100);
+ if (sizeof(void *) == 8) {
+ EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(),
+ BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>());
+ }
+
+ // Test key insertion/deletion in random order.
+ ExpectOperationCounts(45281, 122560, values, &tracker, &set3);
+ ExpectOperationCounts(386718, 119816, values, &tracker, &set61);
+ ExpectOperationCounts(586761, 120319, values, &tracker, &set100);
+
+ // Test key insertion/deletion in sorted order.
+ std::sort(values.begin(), values.end());
+ ExpectOperationCounts(26638, 92134, values, &tracker, &set3);
+ ExpectOperationCounts(20208, 87757, values, &tracker, &set61);
+ ExpectOperationCounts(20124, 96583, values, &tracker, &set100);
+
+ // Test key insertion/deletion in reverse sorted order.
+ std::reverse(values.begin(), values.end());
+ ExpectOperationCounts(49951, 109326, values, &tracker, &set3);
+ ExpectOperationCounts(338813, 108267, values, &tracker, &set61);
+ ExpectOperationCounts(534529, 115280, values, &tracker, &set100);
+}
+
+struct NoDefaultCtor {
+ int num;
+ explicit NoDefaultCtor(int i) : num(i) {}
+
+ friend bool operator<(const NoDefaultCtor &a, const NoDefaultCtor &b) {
+ return a.num < b.num;
+ }
+};
+
+TEST(Btree, BtreeMapCanHoldNoDefaultCtorTypes) {
+ absl::btree_map<NoDefaultCtor, NoDefaultCtor> m;
+
+ for (int i = 1; i <= 99; ++i) {
+ SCOPED_TRACE(i);
+ EXPECT_TRUE(m.emplace(NoDefaultCtor(i), NoDefaultCtor(100 - i)).second);
+ }
+ EXPECT_FALSE(m.emplace(NoDefaultCtor(78), NoDefaultCtor(0)).second);
+
+ auto iter99 = m.find(NoDefaultCtor(99));
+ ASSERT_NE(iter99, m.end());
+ EXPECT_EQ(iter99->second.num, 1);
+
+ auto iter1 = m.find(NoDefaultCtor(1));
+ ASSERT_NE(iter1, m.end());
+ EXPECT_EQ(iter1->second.num, 99);
+
+ auto iter50 = m.find(NoDefaultCtor(50));
+ ASSERT_NE(iter50, m.end());
+ EXPECT_EQ(iter50->second.num, 50);
+
+ auto iter25 = m.find(NoDefaultCtor(25));
+ ASSERT_NE(iter25, m.end());
+ EXPECT_EQ(iter25->second.num, 75);
+}
+
+TEST(Btree, BtreeMultimapCanHoldNoDefaultCtorTypes) {
+ absl::btree_multimap<NoDefaultCtor, NoDefaultCtor> m;
+
+ for (int i = 1; i <= 99; ++i) {
+ SCOPED_TRACE(i);
+ m.emplace(NoDefaultCtor(i), NoDefaultCtor(100 - i));
+ }
+
+ auto iter99 = m.find(NoDefaultCtor(99));
+ ASSERT_NE(iter99, m.end());
+ EXPECT_EQ(iter99->second.num, 1);
+
+ auto iter1 = m.find(NoDefaultCtor(1));
+ ASSERT_NE(iter1, m.end());
+ EXPECT_EQ(iter1->second.num, 99);
+
+ auto iter50 = m.find(NoDefaultCtor(50));
+ ASSERT_NE(iter50, m.end());
+ EXPECT_EQ(iter50->second.num, 50);
+
+ auto iter25 = m.find(NoDefaultCtor(25));
+ ASSERT_NE(iter25, m.end());
+ EXPECT_EQ(iter25->second.num, 75);
+}
+
+TEST(Btree, MapAt) {
+ absl::btree_map<int, int> map = {{1, 2}, {2, 4}};
+ EXPECT_EQ(map.at(1), 2);
+ EXPECT_EQ(map.at(2), 4);
+ map.at(2) = 8;
+ const absl::btree_map<int, int> &const_map = map;
+ EXPECT_EQ(const_map.at(1), 2);
+ EXPECT_EQ(const_map.at(2), 8);
+#ifdef ABSL_HAVE_EXCEPTIONS
+ EXPECT_THROW(map.at(3), std::out_of_range);
+#else
+ EXPECT_DEATH(map.at(3), "absl::btree_map::at");
+#endif
+}
+
+TEST(Btree, BtreeMultisetEmplace) {
+ const int value_to_insert = 123456;
+ absl::btree_multiset<int> s;
+ auto iter = s.emplace(value_to_insert);
+ ASSERT_NE(iter, s.end());
+ EXPECT_EQ(*iter, value_to_insert);
+ auto iter2 = s.emplace(value_to_insert);
+ EXPECT_NE(iter2, iter);
+ ASSERT_NE(iter2, s.end());
+ EXPECT_EQ(*iter2, value_to_insert);
+ auto result = s.equal_range(value_to_insert);
+ EXPECT_EQ(std::distance(result.first, result.second), 2);
+}
+
+TEST(Btree, BtreeMultisetEmplaceHint) {
+ const int value_to_insert = 123456;
+ absl::btree_multiset<int> s;
+ auto iter = s.emplace(value_to_insert);
+ ASSERT_NE(iter, s.end());
+ EXPECT_EQ(*iter, value_to_insert);
+ auto emplace_iter = s.emplace_hint(iter, value_to_insert);
+ EXPECT_NE(emplace_iter, iter);
+ ASSERT_NE(emplace_iter, s.end());
+ EXPECT_EQ(*emplace_iter, value_to_insert);
+}
+
+TEST(Btree, BtreeMultimapEmplace) {
+ const int key_to_insert = 123456;
+ const char value0[] = "a";
+ absl::btree_multimap<int, std::string> s;
+ auto iter = s.emplace(key_to_insert, value0);
+ ASSERT_NE(iter, s.end());
+ EXPECT_EQ(iter->first, key_to_insert);
+ EXPECT_EQ(iter->second, value0);
+ const char value1[] = "b";
+ auto iter2 = s.emplace(key_to_insert, value1);
+ EXPECT_NE(iter2, iter);
+ ASSERT_NE(iter2, s.end());
+ EXPECT_EQ(iter2->first, key_to_insert);
+ EXPECT_EQ(iter2->second, value1);
+ auto result = s.equal_range(key_to_insert);
+ EXPECT_EQ(std::distance(result.first, result.second), 2);
+}
+
+TEST(Btree, BtreeMultimapEmplaceHint) {
+ const int key_to_insert = 123456;
+ const char value0[] = "a";
+ absl::btree_multimap<int, std::string> s;
+ auto iter = s.emplace(key_to_insert, value0);
+ ASSERT_NE(iter, s.end());
+ EXPECT_EQ(iter->first, key_to_insert);
+ EXPECT_EQ(iter->second, value0);
+ const char value1[] = "b";
+ auto emplace_iter = s.emplace_hint(iter, key_to_insert, value1);
+ EXPECT_NE(emplace_iter, iter);
+ ASSERT_NE(emplace_iter, s.end());
+ EXPECT_EQ(emplace_iter->first, key_to_insert);
+ EXPECT_EQ(emplace_iter->second, value1);
+}
+
+TEST(Btree, ConstIteratorAccessors) {
+ absl::btree_set<int> set;
+ for (int i = 0; i < 100; ++i) {
+ set.insert(i);
+ }
+
+ auto it = set.cbegin();
+ auto r_it = set.crbegin();
+ for (int i = 0; i < 100; ++i, ++it, ++r_it) {
+ ASSERT_EQ(*it, i);
+ ASSERT_EQ(*r_it, 99 - i);
+ }
+ EXPECT_EQ(it, set.cend());
+ EXPECT_EQ(r_it, set.crend());
+}
+
+TEST(Btree, StrSplitCompatible) {
+ const absl::btree_set<std::string> split_set = absl::StrSplit("a,b,c", ',');
+ const absl::btree_set<std::string> expected_set = {"a", "b", "c"};
+
+ EXPECT_EQ(split_set, expected_set);
+}
+
+// We can't use EXPECT_EQ/etc. to compare absl::weak_ordering because they
+// convert literal 0 to int and absl::weak_ordering can only be compared with
+// literal 0. Defining this function allows for avoiding ClangTidy warnings.
+bool Identity(const bool b) { return b; }
+
+TEST(Btree, ValueComp) {
+ absl::btree_set<int> s;
+ EXPECT_TRUE(s.value_comp()(1, 2));
+ EXPECT_FALSE(s.value_comp()(2, 2));
+ EXPECT_FALSE(s.value_comp()(2, 1));
+
+ absl::btree_map<int, int> m1;
+ EXPECT_TRUE(m1.value_comp()(std::make_pair(1, 0), std::make_pair(2, 0)));
+ EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(2, 0)));
+ EXPECT_FALSE(m1.value_comp()(std::make_pair(2, 0), std::make_pair(1, 0)));
+
+ absl::btree_map<std::string, int> m2;
+ EXPECT_TRUE(Identity(
+ m2.value_comp()(std::make_pair("a", 0), std::make_pair("b", 0)) < 0));
+ EXPECT_TRUE(Identity(
+ m2.value_comp()(std::make_pair("b", 0), std::make_pair("b", 0)) == 0));
+ EXPECT_TRUE(Identity(
+ m2.value_comp()(std::make_pair("b", 0), std::make_pair("a", 0)) > 0));
+}
+
+TEST(Btree, DefaultConstruction) {
+ absl::btree_set<int> s;
+ absl::btree_map<int, int> m;
+ absl::btree_multiset<int> ms;
+ absl::btree_multimap<int, int> mm;
+
+ EXPECT_TRUE(s.empty());
+ EXPECT_TRUE(m.empty());
+ EXPECT_TRUE(ms.empty());
+ EXPECT_TRUE(mm.empty());
+}
+
+TEST(Btree, SwissTableHashable) {
+ static constexpr int kValues = 10000;
+ std::vector<int> values(kValues);
+ std::iota(values.begin(), values.end(), 0);
+ std::vector<std::pair<int, int>> map_values;
+ for (int v : values) map_values.emplace_back(v, -v);
+
+ using set = absl::btree_set<int>;
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
+ set{},
+ set{1},
+ set{2},
+ set{1, 2},
+ set{2, 1},
+ set(values.begin(), values.end()),
+ set(values.rbegin(), values.rend()),
+ }));
+
+ using mset = absl::btree_multiset<int>;
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
+ mset{},
+ mset{1},
+ mset{1, 1},
+ mset{2},
+ mset{2, 2},
+ mset{1, 2},
+ mset{1, 1, 2},
+ mset{1, 2, 2},
+ mset{1, 1, 2, 2},
+ mset(values.begin(), values.end()),
+ mset(values.rbegin(), values.rend()),
+ }));
+
+ using map = absl::btree_map<int, int>;
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
+ map{},
+ map{{1, 0}},
+ map{{1, 1}},
+ map{{2, 0}},
+ map{{2, 2}},
+ map{{1, 0}, {2, 1}},
+ map(map_values.begin(), map_values.end()),
+ map(map_values.rbegin(), map_values.rend()),
+ }));
+
+ using mmap = absl::btree_multimap<int, int>;
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
+ mmap{},
+ mmap{{1, 0}},
+ mmap{{1, 1}},
+ mmap{{1, 0}, {1, 1}},
+ mmap{{1, 1}, {1, 0}},
+ mmap{{2, 0}},
+ mmap{{2, 2}},
+ mmap{{1, 0}, {2, 1}},
+ mmap(map_values.begin(), map_values.end()),
+ mmap(map_values.rbegin(), map_values.rend()),
+ }));
+}
+
+TEST(Btree, ComparableSet) {
+ absl::btree_set<int> s1 = {1, 2};
+ absl::btree_set<int> s2 = {2, 3};
+ EXPECT_LT(s1, s2);
+ EXPECT_LE(s1, s2);
+ EXPECT_LE(s1, s1);
+ EXPECT_GT(s2, s1);
+ EXPECT_GE(s2, s1);
+ EXPECT_GE(s1, s1);
+}
+
+TEST(Btree, ComparableSetsDifferentLength) {
+ absl::btree_set<int> s1 = {1, 2};
+ absl::btree_set<int> s2 = {1, 2, 3};
+ EXPECT_LT(s1, s2);
+ EXPECT_LE(s1, s2);
+ EXPECT_GT(s2, s1);
+ EXPECT_GE(s2, s1);
+}
+
+TEST(Btree, ComparableMultiset) {
+ absl::btree_multiset<int> s1 = {1, 2};
+ absl::btree_multiset<int> s2 = {2, 3};
+ EXPECT_LT(s1, s2);
+ EXPECT_LE(s1, s2);
+ EXPECT_LE(s1, s1);
+ EXPECT_GT(s2, s1);
+ EXPECT_GE(s2, s1);
+ EXPECT_GE(s1, s1);
+}
+
+TEST(Btree, ComparableMap) {
+ absl::btree_map<int, int> s1 = {{1, 2}};
+ absl::btree_map<int, int> s2 = {{2, 3}};
+ EXPECT_LT(s1, s2);
+ EXPECT_LE(s1, s2);
+ EXPECT_LE(s1, s1);
+ EXPECT_GT(s2, s1);
+ EXPECT_GE(s2, s1);
+ EXPECT_GE(s1, s1);
+}
+
+TEST(Btree, ComparableMultimap) {
+ absl::btree_multimap<int, int> s1 = {{1, 2}};
+ absl::btree_multimap<int, int> s2 = {{2, 3}};
+ EXPECT_LT(s1, s2);
+ EXPECT_LE(s1, s2);
+ EXPECT_LE(s1, s1);
+ EXPECT_GT(s2, s1);
+ EXPECT_GE(s2, s1);
+ EXPECT_GE(s1, s1);
+}
+
+TEST(Btree, ComparableSetWithCustomComparator) {
+ // As specified by
+ // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf section
+ // [container.requirements.general].12, ordering associative containers always
+ // uses default '<' operator
+ // - even if otherwise the container uses custom functor.
+ absl::btree_set<int, std::greater<int>> s1 = {1, 2};
+ absl::btree_set<int, std::greater<int>> s2 = {2, 3};
+ EXPECT_LT(s1, s2);
+ EXPECT_LE(s1, s2);
+ EXPECT_LE(s1, s1);
+ EXPECT_GT(s2, s1);
+ EXPECT_GE(s2, s1);
+ EXPECT_GE(s1, s1);
+}
+
+TEST(Btree, EraseReturnsIterator) {
+ absl::btree_set<int> set = {1, 2, 3, 4, 5};
+ auto result_it = set.erase(set.begin(), set.find(3));
+ EXPECT_EQ(result_it, set.find(3));
+ result_it = set.erase(set.find(5));
+ EXPECT_EQ(result_it, set.end());
+}
+
+TEST(Btree, ExtractAndInsertNodeHandleSet) {
+ absl::btree_set<int> src1 = {1, 2, 3, 4, 5};
+ auto nh = src1.extract(src1.find(3));
+ EXPECT_THAT(src1, ElementsAre(1, 2, 4, 5));
+ absl::btree_set<int> other;
+ absl::btree_set<int>::insert_return_type res = other.insert(std::move(nh));
+ EXPECT_THAT(other, ElementsAre(3));
+ EXPECT_EQ(res.position, other.find(3));
+ EXPECT_TRUE(res.inserted);
+ EXPECT_TRUE(res.node.empty());
+
+ absl::btree_set<int> src2 = {3, 4};
+ nh = src2.extract(src2.find(3));
+ EXPECT_THAT(src2, ElementsAre(4));
+ res = other.insert(std::move(nh));
+ EXPECT_THAT(other, ElementsAre(3));
+ EXPECT_EQ(res.position, other.find(3));
+ EXPECT_FALSE(res.inserted);
+ ASSERT_FALSE(res.node.empty());
+ EXPECT_EQ(res.node.value(), 3);
+}
+
+template <typename Set>
+void TestExtractWithTrackingForSet() {
+ InstanceTracker tracker;
+ {
+ Set s;
+ // Add enough elements to make sure we test internal nodes too.
+ const size_t kSize = 1000;
+ while (s.size() < kSize) {
+ s.insert(MovableOnlyInstance(s.size()));
+ }
+ for (int i = 0; i < kSize; ++i) {
+ // Extract with key
+ auto nh = s.extract(MovableOnlyInstance(i));
+ EXPECT_EQ(s.size(), kSize - 1);
+ EXPECT_EQ(nh.value().value(), i);
+ // Insert with node
+ s.insert(std::move(nh));
+ EXPECT_EQ(s.size(), kSize);
+
+ // Extract with iterator
+ auto it = s.find(MovableOnlyInstance(i));
+ nh = s.extract(it);
+ EXPECT_EQ(s.size(), kSize - 1);
+ EXPECT_EQ(nh.value().value(), i);
+ // Insert with node and hint
+ s.insert(s.begin(), std::move(nh));
+ EXPECT_EQ(s.size(), kSize);
+ }
+ }
+ EXPECT_EQ(0, tracker.instances());
+}
+
+template <typename Map>
+void TestExtractWithTrackingForMap() {
+ InstanceTracker tracker;
+ {
+ Map m;
+ // Add enough elements to make sure we test internal nodes too.
+ const size_t kSize = 1000;
+ while (m.size() < kSize) {
+ m.insert(
+ {CopyableMovableInstance(m.size()), MovableOnlyInstance(m.size())});
+ }
+ for (int i = 0; i < kSize; ++i) {
+ // Extract with key
+ auto nh = m.extract(CopyableMovableInstance(i));
+ EXPECT_EQ(m.size(), kSize - 1);
+ EXPECT_EQ(nh.key().value(), i);
+ EXPECT_EQ(nh.mapped().value(), i);
+ // Insert with node
+ m.insert(std::move(nh));
+ EXPECT_EQ(m.size(), kSize);
+
+ // Extract with iterator
+ auto it = m.find(CopyableMovableInstance(i));
+ nh = m.extract(it);
+ EXPECT_EQ(m.size(), kSize - 1);
+ EXPECT_EQ(nh.key().value(), i);
+ EXPECT_EQ(nh.mapped().value(), i);
+ // Insert with node and hint
+ m.insert(m.begin(), std::move(nh));
+ EXPECT_EQ(m.size(), kSize);
+ }
+ }
+ EXPECT_EQ(0, tracker.instances());
+}
+
+TEST(Btree, ExtractTracking) {
+ TestExtractWithTrackingForSet<absl::btree_set<MovableOnlyInstance>>();
+ TestExtractWithTrackingForSet<absl::btree_multiset<MovableOnlyInstance>>();
+ TestExtractWithTrackingForMap<
+ absl::btree_map<CopyableMovableInstance, MovableOnlyInstance>>();
+ TestExtractWithTrackingForMap<
+ absl::btree_multimap<CopyableMovableInstance, MovableOnlyInstance>>();
+}
+
+TEST(Btree, ExtractAndInsertNodeHandleMultiSet) {
+ absl::btree_multiset<int> src1 = {1, 2, 3, 3, 4, 5};
+ auto nh = src1.extract(src1.find(3));
+ EXPECT_THAT(src1, ElementsAre(1, 2, 3, 4, 5));
+ absl::btree_multiset<int> other;
+ auto res = other.insert(std::move(nh));
+ EXPECT_THAT(other, ElementsAre(3));
+ EXPECT_EQ(res, other.find(3));
+
+ absl::btree_multiset<int> src2 = {3, 4};
+ nh = src2.extract(src2.find(3));
+ EXPECT_THAT(src2, ElementsAre(4));
+ res = other.insert(std::move(nh));
+ EXPECT_THAT(other, ElementsAre(3, 3));
+ EXPECT_EQ(res, ++other.find(3));
+}
+
+TEST(Btree, ExtractAndInsertNodeHandleMap) {
+ absl::btree_map<int, int> src1 = {{1, 2}, {3, 4}, {5, 6}};
+ auto nh = src1.extract(src1.find(3));
+ EXPECT_THAT(src1, ElementsAre(Pair(1, 2), Pair(5, 6)));
+ absl::btree_map<int, int> other;
+ absl::btree_map<int, int>::insert_return_type res =
+ other.insert(std::move(nh));
+ EXPECT_THAT(other, ElementsAre(Pair(3, 4)));
+ EXPECT_EQ(res.position, other.find(3));
+ EXPECT_TRUE(res.inserted);
+ EXPECT_TRUE(res.node.empty());
+
+ absl::btree_map<int, int> src2 = {{3, 6}};
+ nh = src2.extract(src2.find(3));
+ EXPECT_TRUE(src2.empty());
+ res = other.insert(std::move(nh));
+ EXPECT_THAT(other, ElementsAre(Pair(3, 4)));
+ EXPECT_EQ(res.position, other.find(3));
+ EXPECT_FALSE(res.inserted);
+ ASSERT_FALSE(res.node.empty());
+ EXPECT_EQ(res.node.key(), 3);
+ EXPECT_EQ(res.node.mapped(), 6);
+}
+
+TEST(Btree, ExtractAndInsertNodeHandleMultiMap) {
+ absl::btree_multimap<int, int> src1 = {{1, 2}, {3, 4}, {5, 6}};
+ auto nh = src1.extract(src1.find(3));
+ EXPECT_THAT(src1, ElementsAre(Pair(1, 2), Pair(5, 6)));
+ absl::btree_multimap<int, int> other;
+ auto res = other.insert(std::move(nh));
+ EXPECT_THAT(other, ElementsAre(Pair(3, 4)));
+ EXPECT_EQ(res, other.find(3));
+
+ absl::btree_multimap<int, int> src2 = {{3, 6}};
+ nh = src2.extract(src2.find(3));
+ EXPECT_TRUE(src2.empty());
+ res = other.insert(std::move(nh));
+ EXPECT_THAT(other, ElementsAre(Pair(3, 4), Pair(3, 6)));
+ EXPECT_EQ(res, ++other.begin());
+}
+
+// For multisets, insert with hint also affects correctness because we need to
+// insert immediately before the hint if possible.
+struct InsertMultiHintData {
+ int key;
+ int not_key;
+ bool operator==(const InsertMultiHintData other) const {
+ return key == other.key && not_key == other.not_key;
+ }
+};
+
+struct InsertMultiHintDataKeyCompare {
+ using is_transparent = void;
+ bool operator()(const InsertMultiHintData a,
+ const InsertMultiHintData b) const {
+ return a.key < b.key;
+ }
+ bool operator()(const int a, const InsertMultiHintData b) const {
+ return a < b.key;
+ }
+ bool operator()(const InsertMultiHintData a, const int b) const {
+ return a.key < b;
+ }
+};
+
+TEST(Btree, InsertHintNodeHandle) {
+ // For unique sets, insert with hint is just a performance optimization.
+ // Test that insert works correctly when the hint is right or wrong.
+ {
+ absl::btree_set<int> src = {1, 2, 3, 4, 5};
+ auto nh = src.extract(src.find(3));
+ EXPECT_THAT(src, ElementsAre(1, 2, 4, 5));
+ absl::btree_set<int> other = {0, 100};
+ // Test a correct hint.
+ auto it = other.insert(other.lower_bound(3), std::move(nh));
+ EXPECT_THAT(other, ElementsAre(0, 3, 100));
+ EXPECT_EQ(it, other.find(3));
+
+ nh = src.extract(src.find(5));
+ // Test an incorrect hint.
+ it = other.insert(other.end(), std::move(nh));
+ EXPECT_THAT(other, ElementsAre(0, 3, 5, 100));
+ EXPECT_EQ(it, other.find(5));
+ }
+
+ absl::btree_multiset<InsertMultiHintData, InsertMultiHintDataKeyCompare> src =
+ {{1, 2}, {3, 4}, {3, 5}};
+ auto nh = src.extract(src.lower_bound(3));
+ EXPECT_EQ(nh.value(), (InsertMultiHintData{3, 4}));
+ absl::btree_multiset<InsertMultiHintData, InsertMultiHintDataKeyCompare>
+ other = {{3, 1}, {3, 2}, {3, 3}};
+ auto it = other.insert(--other.end(), std::move(nh));
+ EXPECT_THAT(
+ other, ElementsAre(InsertMultiHintData{3, 1}, InsertMultiHintData{3, 2},
+ InsertMultiHintData{3, 4}, InsertMultiHintData{3, 3}));
+ EXPECT_EQ(it, --(--other.end()));
+
+ nh = src.extract(src.find(3));
+ EXPECT_EQ(nh.value(), (InsertMultiHintData{3, 5}));
+ it = other.insert(other.begin(), std::move(nh));
+ EXPECT_THAT(other,
+ ElementsAre(InsertMultiHintData{3, 5}, InsertMultiHintData{3, 1},
+ InsertMultiHintData{3, 2}, InsertMultiHintData{3, 4},
+ InsertMultiHintData{3, 3}));
+ EXPECT_EQ(it, other.begin());
+}
+
+struct IntCompareToCmp {
+ absl::weak_ordering operator()(int a, int b) const {
+ if (a < b) return absl::weak_ordering::less;
+ if (a > b) return absl::weak_ordering::greater;
+ return absl::weak_ordering::equivalent;
+ }
+};
+
+TEST(Btree, MergeIntoUniqueContainers) {
+ absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3};
+ absl::btree_multiset<int> src2 = {3, 4, 4, 5};
+ absl::btree_set<int> dst;
+
+ dst.merge(src1);
+ EXPECT_TRUE(src1.empty());
+ EXPECT_THAT(dst, ElementsAre(1, 2, 3));
+ dst.merge(src2);
+ EXPECT_THAT(src2, ElementsAre(3, 4));
+ EXPECT_THAT(dst, ElementsAre(1, 2, 3, 4, 5));
+}
+
+TEST(Btree, MergeIntoUniqueContainersWithCompareTo) {
+ absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3};
+ absl::btree_multiset<int> src2 = {3, 4, 4, 5};
+ absl::btree_set<int, IntCompareToCmp> dst;
+
+ dst.merge(src1);
+ EXPECT_TRUE(src1.empty());
+ EXPECT_THAT(dst, ElementsAre(1, 2, 3));
+ dst.merge(src2);
+ EXPECT_THAT(src2, ElementsAre(3, 4));
+ EXPECT_THAT(dst, ElementsAre(1, 2, 3, 4, 5));
+}
+
+TEST(Btree, MergeIntoMultiContainers) {
+ absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3};
+ absl::btree_multiset<int> src2 = {3, 4, 4, 5};
+ absl::btree_multiset<int> dst;
+
+ dst.merge(src1);
+ EXPECT_TRUE(src1.empty());
+ EXPECT_THAT(dst, ElementsAre(1, 2, 3));
+ dst.merge(src2);
+ EXPECT_TRUE(src2.empty());
+ EXPECT_THAT(dst, ElementsAre(1, 2, 3, 3, 4, 4, 5));
+}
+
+TEST(Btree, MergeIntoMultiContainersWithCompareTo) {
+ absl::btree_set<int, IntCompareToCmp> src1 = {1, 2, 3};
+ absl::btree_multiset<int> src2 = {3, 4, 4, 5};
+ absl::btree_multiset<int, IntCompareToCmp> dst;
+
+ dst.merge(src1);
+ EXPECT_TRUE(src1.empty());
+ EXPECT_THAT(dst, ElementsAre(1, 2, 3));
+ dst.merge(src2);
+ EXPECT_TRUE(src2.empty());
+ EXPECT_THAT(dst, ElementsAre(1, 2, 3, 3, 4, 4, 5));
+}
+
+TEST(Btree, MergeIntoMultiMapsWithDifferentComparators) {
+ absl::btree_map<int, int, IntCompareToCmp> src1 = {{1, 1}, {2, 2}, {3, 3}};
+ absl::btree_multimap<int, int, std::greater<int>> src2 = {
+ {5, 5}, {4, 1}, {4, 4}, {3, 2}};
+ absl::btree_multimap<int, int> dst;
+
+ dst.merge(src1);
+ EXPECT_TRUE(src1.empty());
+ EXPECT_THAT(dst, ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3)));
+ dst.merge(src2);
+ EXPECT_TRUE(src2.empty());
+ EXPECT_THAT(dst, ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3), Pair(3, 2),
+ Pair(4, 1), Pair(4, 4), Pair(5, 5)));
+}
+
+struct KeyCompareToWeakOrdering {
+ template <typename T>
+ absl::weak_ordering operator()(const T &a, const T &b) const {
+ return a < b ? absl::weak_ordering::less
+ : a == b ? absl::weak_ordering::equivalent
+ : absl::weak_ordering::greater;
+ }
+};
+
+struct KeyCompareToStrongOrdering {
+ template <typename T>
+ absl::strong_ordering operator()(const T &a, const T &b) const {
+ return a < b ? absl::strong_ordering::less
+ : a == b ? absl::strong_ordering::equal
+ : absl::strong_ordering::greater;
+ }
+};
+
+TEST(Btree, UserProvidedKeyCompareToComparators) {
+ absl::btree_set<int, KeyCompareToWeakOrdering> weak_set = {1, 2, 3};
+ EXPECT_TRUE(weak_set.contains(2));
+ EXPECT_FALSE(weak_set.contains(4));
+
+ absl::btree_set<int, KeyCompareToStrongOrdering> strong_set = {1, 2, 3};
+ EXPECT_TRUE(strong_set.contains(2));
+ EXPECT_FALSE(strong_set.contains(4));
+}
+
+TEST(Btree, TryEmplaceBasicTest) {
+ absl::btree_map<int, std::string> m;
+
+ // Should construct a std::string from the literal.
+ m.try_emplace(1, "one");
+ EXPECT_EQ(1, m.size());
+
+ // Try other std::string constructors and const lvalue key.
+ const int key(42);
+ m.try_emplace(key, 3, 'a');
+ m.try_emplace(2, std::string("two"));
+
+ EXPECT_TRUE(std::is_sorted(m.begin(), m.end()));
+ EXPECT_THAT(m, ElementsAreArray(std::vector<std::pair<int, std::string>>{
+ {1, "one"}, {2, "two"}, {42, "aaa"}}));
+}
+
+TEST(Btree, TryEmplaceWithHintWorks) {
+ // Use a counting comparator here to verify that hint is used.
+ int calls = 0;
+ auto cmp = [&calls](int x, int y) {
+ ++calls;
+ return x < y;
+ };
+ using Cmp = decltype(cmp);
+
+ absl::btree_map<int, int, Cmp> m(cmp);
+ for (int i = 0; i < 128; ++i) {
+ m.emplace(i, i);
+ }
+
+ // Sanity check for the comparator
+ calls = 0;
+ m.emplace(127, 127);
+ EXPECT_GE(calls, 4);
+
+ // Try with begin hint:
+ calls = 0;
+ auto it = m.try_emplace(m.begin(), -1, -1);
+ EXPECT_EQ(129, m.size());
+ EXPECT_EQ(it, m.begin());
+ EXPECT_LE(calls, 2);
+
+ // Try with end hint:
+ calls = 0;
+ std::pair<int, int> pair1024 = {1024, 1024};
+ it = m.try_emplace(m.end(), pair1024.first, pair1024.second);
+ EXPECT_EQ(130, m.size());
+ EXPECT_EQ(it, --m.end());
+ EXPECT_LE(calls, 2);
+
+ // Try value already present, bad hint; ensure no duplicate added:
+ calls = 0;
+ it = m.try_emplace(m.end(), 16, 17);
+ EXPECT_EQ(130, m.size());
+ EXPECT_GE(calls, 4);
+ EXPECT_EQ(it, m.find(16));
+
+ // Try value already present, hint points directly to it:
+ calls = 0;
+ it = m.try_emplace(it, 16, 17);
+ EXPECT_EQ(130, m.size());
+ EXPECT_LE(calls, 2);
+ EXPECT_EQ(it, m.find(16));
+
+ m.erase(2);
+ EXPECT_EQ(129, m.size());
+ auto hint = m.find(3);
+ // Try emplace in the middle of two other elements.
+ calls = 0;
+ m.try_emplace(hint, 2, 2);
+ EXPECT_EQ(130, m.size());
+ EXPECT_LE(calls, 2);
+
+ EXPECT_TRUE(std::is_sorted(m.begin(), m.end()));
+}
+
+TEST(Btree, TryEmplaceWithBadHint) {
+ absl::btree_map<int, int> m = {{1, 1}, {9, 9}};
+
+ // Bad hint (too small), should still emplace:
+ auto it = m.try_emplace(m.begin(), 2, 2);
+ EXPECT_EQ(it, ++m.begin());
+ EXPECT_THAT(m, ElementsAreArray(
+ std::vector<std::pair<int, int>>{{1, 1}, {2, 2}, {9, 9}}));
+
+ // Bad hint, too large this time:
+ it = m.try_emplace(++(++m.begin()), 0, 0);
+ EXPECT_EQ(it, m.begin());
+ EXPECT_THAT(m, ElementsAreArray(std::vector<std::pair<int, int>>{
+ {0, 0}, {1, 1}, {2, 2}, {9, 9}}));
+}
+
+TEST(Btree, TryEmplaceMaintainsSortedOrder) {
+ absl::btree_map<int, std::string> m;
+ std::pair<int, std::string> pair5 = {5, "five"};
+
+ // Test both lvalue & rvalue emplace.
+ m.try_emplace(10, "ten");
+ m.try_emplace(pair5.first, pair5.second);
+ EXPECT_EQ(2, m.size());
+ EXPECT_TRUE(std::is_sorted(m.begin(), m.end()));
+
+ int int100{100};
+ m.try_emplace(int100, "hundred");
+ m.try_emplace(1, "one");
+ EXPECT_EQ(4, m.size());
+ EXPECT_TRUE(std::is_sorted(m.begin(), m.end()));
+}
+
+TEST(Btree, TryEmplaceWithHintAndNoValueArgsWorks) {
+ absl::btree_map<int, int> m;
+ m.try_emplace(m.end(), 1);
+ EXPECT_EQ(0, m[1]);
+}
+
+TEST(Btree, TryEmplaceWithHintAndMultipleValueArgsWorks) {
+ absl::btree_map<int, std::string> m;
+ m.try_emplace(m.end(), 1, 10, 'a');
+ 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;
+
+ // 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));
+
+ 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);
+
+ for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(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);
+ }
+}
+
+TEST(Btree, EmptyTree) {
+ absl::btree_set<int> s;
+ EXPECT_TRUE(s.empty());
+ EXPECT_EQ(s.size(), 0);
+ EXPECT_GT(s.max_size(), 0);
+}
+
+bool IsEven(int k) { return k % 2 == 0; }
+
+TEST(Btree, EraseIf) {
+ // Test that erase_if works with all the container types and supports lambdas.
+ {
+ absl::btree_set<int> s = {1, 3, 5, 6, 100};
+ erase_if(s, [](int k) { return k > 3; });
+ EXPECT_THAT(s, ElementsAre(1, 3));
+ }
+ {
+ absl::btree_multiset<int> s = {1, 3, 3, 5, 6, 6, 100};
+ erase_if(s, [](int k) { return k <= 3; });
+ EXPECT_THAT(s, ElementsAre(5, 6, 6, 100));
+ }
+ {
+ absl::btree_map<int, int> m = {{1, 1}, {3, 3}, {6, 6}, {100, 100}};
+ erase_if(m, [](std::pair<const int, int> kv) { return kv.first > 3; });
+ EXPECT_THAT(m, ElementsAre(Pair(1, 1), Pair(3, 3)));
+ }
+ {
+ absl::btree_multimap<int, int> m = {{1, 1}, {3, 3}, {3, 6},
+ {6, 6}, {6, 7}, {100, 6}};
+ erase_if(m, [](std::pair<const int, int> kv) { return kv.second == 6; });
+ EXPECT_THAT(m, ElementsAre(Pair(1, 1), Pair(3, 3), Pair(6, 7)));
+ }
+ // Test that erasing all elements from a large set works and test support for
+ // function pointers.
+ {
+ absl::btree_set<int> s;
+ for (int i = 0; i < 1000; ++i) s.insert(2 * i);
+ erase_if(s, IsEven);
+ EXPECT_THAT(s, IsEmpty());
+ }
+ // Test that erase_if supports other format of function pointers.
+ {
+ absl::btree_set<int> s = {1, 3, 5, 6, 100};
+ erase_if(s, &IsEven);
+ EXPECT_THAT(s, ElementsAre(1, 3, 5));
+ }
+}
+
+TEST(Btree, InsertOrAssign) {
+ absl::btree_map<int, int> m = {{1, 1}, {3, 3}};
+ using value_type = typename decltype(m)::value_type;
+
+ auto ret = m.insert_or_assign(4, 4);
+ EXPECT_EQ(*ret.first, value_type(4, 4));
+ EXPECT_TRUE(ret.second);
+ ret = m.insert_or_assign(3, 100);
+ EXPECT_EQ(*ret.first, value_type(3, 100));
+ EXPECT_FALSE(ret.second);
+
+ auto hint_ret = m.insert_or_assign(ret.first, 3, 200);
+ EXPECT_EQ(*hint_ret, value_type(3, 200));
+ hint_ret = m.insert_or_assign(m.find(1), 0, 1);
+ EXPECT_EQ(*hint_ret, value_type(0, 1));
+ // Test with bad hint.
+ hint_ret = m.insert_or_assign(m.end(), -1, 1);
+ EXPECT_EQ(*hint_ret, value_type(-1, 1));
+
+ EXPECT_THAT(m, ElementsAre(Pair(-1, 1), Pair(0, 1), Pair(1, 1), Pair(3, 200),
+ Pair(4, 4)));
+}
+
+TEST(Btree, InsertOrAssignMovableOnly) {
+ absl::btree_map<int, MovableOnlyInstance> m;
+ using value_type = typename decltype(m)::value_type;
+
+ auto ret = m.insert_or_assign(4, MovableOnlyInstance(4));
+ EXPECT_EQ(*ret.first, value_type(4, MovableOnlyInstance(4)));
+ EXPECT_TRUE(ret.second);
+ ret = m.insert_or_assign(4, MovableOnlyInstance(100));
+ EXPECT_EQ(*ret.first, value_type(4, MovableOnlyInstance(100)));
+ EXPECT_FALSE(ret.second);
+
+ auto hint_ret = m.insert_or_assign(ret.first, 3, MovableOnlyInstance(200));
+ EXPECT_EQ(*hint_ret, value_type(3, MovableOnlyInstance(200)));
+
+ EXPECT_EQ(m.size(), 2);
+}
+
+TEST(Btree, BitfieldArgument) {
+ union {
+ int n : 1;
+ };
+ n = 0;
+ absl::btree_map<int, int> m;
+ m.erase(n);
+ m.count(n);
+ m.find(n);
+ m.contains(n);
+ m.equal_range(n);
+ m.insert_or_assign(n, n);
+ m.insert_or_assign(m.end(), n, n);
+ m.try_emplace(n);
+ m.try_emplace(m.end(), n);
+ m.at(n);
+ m[n];
+}
+
+} // namespace
+} // namespace container_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/container/btree_test.h b/absl/container/btree_test.h
new file mode 100644
index 00000000..218ba41d
--- /dev/null
+++ b/absl/container/btree_test.h
@@ -0,0 +1,155 @@
+// 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_BTREE_TEST_H_
+#define ABSL_CONTAINER_BTREE_TEST_H_
+
+#include <algorithm>
+#include <cassert>
+#include <random>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/container/btree_map.h"
+#include "absl/container/btree_set.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/time/time.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+
+// Like remove_const but propagates the removal through std::pair.
+template <typename T>
+struct remove_pair_const {
+ using type = typename std::remove_const<T>::type;
+};
+template <typename T, typename U>
+struct remove_pair_const<std::pair<T, U> > {
+ using type = std::pair<typename remove_pair_const<T>::type,
+ typename remove_pair_const<U>::type>;
+};
+
+// Utility class to provide an accessor for a key given a value. The default
+// behavior is to treat the value as a pair and return the first element.
+template <typename K, typename V>
+struct KeyOfValue {
+ struct type {
+ const K& operator()(const V& p) const { return p.first; }
+ };
+};
+
+// Partial specialization of KeyOfValue class for when the key and value are
+// the same type such as in set<> and btree_set<>.
+template <typename K>
+struct KeyOfValue<K, K> {
+ struct type {
+ const K& operator()(const K& k) const { return k; }
+ };
+};
+
+inline char* GenerateDigits(char buf[16], unsigned val, unsigned maxval) {
+ assert(val <= maxval);
+ constexpr unsigned kBase = 64; // avoid integer division.
+ unsigned p = 15;
+ buf[p--] = 0;
+ while (maxval > 0) {
+ buf[p--] = ' ' + (val % kBase);
+ val /= kBase;
+ maxval /= kBase;
+ }
+ return buf + p + 1;
+}
+
+template <typename K>
+struct Generator {
+ int maxval;
+ explicit Generator(int m) : maxval(m) {}
+ K operator()(int i) const {
+ assert(i <= maxval);
+ return K(i);
+ }
+};
+
+template <>
+struct Generator<absl::Time> {
+ int maxval;
+ explicit Generator(int m) : maxval(m) {}
+ absl::Time operator()(int i) const { return absl::FromUnixMillis(i); }
+};
+
+template <>
+struct Generator<std::string> {
+ int maxval;
+ explicit Generator(int m) : maxval(m) {}
+ std::string operator()(int i) const {
+ char buf[16];
+ return GenerateDigits(buf, i, maxval);
+ }
+};
+
+template <typename T, typename U>
+struct Generator<std::pair<T, U> > {
+ Generator<typename remove_pair_const<T>::type> tgen;
+ Generator<typename remove_pair_const<U>::type> ugen;
+
+ explicit Generator(int m) : tgen(m), ugen(m) {}
+ std::pair<T, U> operator()(int i) const {
+ return std::make_pair(tgen(i), ugen(i));
+ }
+};
+
+// Generate n values for our tests and benchmarks. Value range is [0, maxval].
+inline std::vector<int> GenerateNumbersWithSeed(int n, int maxval, int seed) {
+ // NOTE: Some tests rely on generated numbers not changing between test runs.
+ // We use std::minstd_rand0 because it is well-defined, but don't use
+ // std::uniform_int_distribution because platforms use different algorithms.
+ std::minstd_rand0 rng(seed);
+
+ std::vector<int> values;
+ absl::flat_hash_set<int> unique_values;
+ if (values.size() < n) {
+ for (int i = values.size(); i < n; i++) {
+ int value;
+ do {
+ value = static_cast<int>(rng()) % (maxval + 1);
+ } while (!unique_values.insert(value).second);
+
+ values.push_back(value);
+ }
+ }
+ return values;
+}
+
+// Generates n values in the range [0, maxval].
+template <typename V>
+std::vector<V> GenerateValuesWithSeed(int n, int maxval, int seed) {
+ const std::vector<int> nums = GenerateNumbersWithSeed(n, maxval, seed);
+ Generator<V> gen(maxval);
+ std::vector<V> vec;
+
+ vec.reserve(n);
+ for (int i = 0; i < n; i++) {
+ vec.push_back(gen(nums[i]));
+ }
+
+ return vec;
+}
+
+} // namespace container_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_CONTAINER_BTREE_TEST_H_
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h
index 1e0da5eb..a9ce99ba 100644
--- a/absl/container/fixed_array.h
+++ b/absl/container/fixed_array.h
@@ -31,7 +31,6 @@
#define ABSL_CONTAINER_FIXED_ARRAY_H_
#include <algorithm>
-#include <array>
#include <cassert>
#include <cstddef>
#include <initializer_list>
@@ -51,7 +50,7 @@
#include "absl/memory/memory.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1);
@@ -387,8 +386,7 @@ class FixedArray {
// error: call to int __builtin___sprintf_chk(etc...)
// will always overflow destination buffer [-Werror]
//
- template <typename OuterT = value_type,
- typename InnerT = absl::remove_extent_t<OuterT>,
+ template <typename OuterT, typename InnerT = absl::remove_extent_t<OuterT>,
size_t InnerN = std::extent<OuterT>::value>
struct StorageElementWrapper {
InnerT array[InnerN];
@@ -397,8 +395,6 @@ class FixedArray {
using StorageElement =
absl::conditional_t<std::is_array<value_type>::value,
StorageElementWrapper<value_type>, value_type>;
- using StorageElementBuffer =
- absl::aligned_storage_t<sizeof(StorageElement), alignof(StorageElement)>;
static pointer AsValueType(pointer ptr) { return ptr; }
static pointer AsValueType(StorageElementWrapper<value_type>* ptr) {
@@ -408,25 +404,25 @@ class FixedArray {
static_assert(sizeof(StorageElement) == sizeof(value_type), "");
static_assert(alignof(StorageElement) == alignof(value_type), "");
- struct NonEmptyInlinedStorage {
- StorageElement* data() {
- return reinterpret_cast<StorageElement*>(inlined_storage_.data());
- }
+ class NonEmptyInlinedStorage {
+ public:
+ StorageElement* data() { return reinterpret_cast<StorageElement*>(buff_); }
+ void AnnotateConstruct(size_type n);
+ void AnnotateDestruct(size_type n);
#ifdef ADDRESS_SANITIZER
void* RedzoneBegin() { return &redzone_begin_; }
void* RedzoneEnd() { return &redzone_end_ + 1; }
#endif // ADDRESS_SANITIZER
- void AnnotateConstruct(size_type);
- void AnnotateDestruct(size_type);
-
+ private:
ADDRESS_SANITIZER_REDZONE(redzone_begin_);
- std::array<StorageElementBuffer, inline_elements> inlined_storage_;
+ alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])];
ADDRESS_SANITIZER_REDZONE(redzone_end_);
};
- struct EmptyInlinedStorage {
+ class EmptyInlinedStorage {
+ public:
StorageElement* data() { return nullptr; }
void AnnotateConstruct(size_type) {}
void AnnotateDestruct(size_type) {}
@@ -460,9 +456,7 @@ class FixedArray {
size_type size() const { return size_alloc_.template get<0>(); }
StorageElement* begin() const { return data_; }
StorageElement* end() const { return begin() + size(); }
- allocator_type& alloc() {
- return size_alloc_.template get<1>();
- }
+ allocator_type& alloc() { return size_alloc_.template get<1>(); }
private:
static bool UsingInlinedStorage(size_type n) {
@@ -515,7 +509,7 @@ void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct(
#endif // ADDRESS_SANITIZER
static_cast<void>(n); // Mark used when not in asan mode
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_FIXED_ARRAY_H_
diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc
index 4a67bb46..a5bb009d 100644
--- a/absl/container/fixed_array_exception_safety_test.cc
+++ b/absl/container/fixed_array_exception_safety_test.cc
@@ -12,14 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "absl/base/config.h"
+#include "absl/container/fixed_array.h"
+
+#ifdef ABSL_HAVE_EXCEPTIONS
+
#include <initializer_list>
#include "gtest/gtest.h"
#include "absl/base/internal/exception_safety_testing.h"
-#include "absl/container/fixed_array.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
@@ -33,10 +37,19 @@ constexpr int kUpdatedValue = 10;
using ::testing::TestThrowingCtor;
using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
+using ThrowAlloc =
+ testing::ThrowingAllocator<Thrower, testing::AllocSpec::kEverythingThrows>;
+using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
+using MoveThrowAlloc =
+ testing::ThrowingAllocator<MoveThrower,
+ testing::AllocSpec::kEverythingThrows>;
+
using FixedArr = absl::FixedArray<Thrower, kInlined>;
+using FixedArrWithAlloc = absl::FixedArray<Thrower, kInlined, ThrowAlloc>;
-using MoveThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>;
using MoveFixedArr = absl::FixedArray<MoveThrower, kInlined>;
+using MoveFixedArrWithAlloc =
+ absl::FixedArray<MoveThrower, kInlined, MoveThrowAlloc>;
TEST(FixedArrayExceptionSafety, CopyConstructor) {
auto small = FixedArr(kSmallSize);
@@ -46,6 +59,14 @@ TEST(FixedArrayExceptionSafety, CopyConstructor) {
TestThrowingCtor<FixedArr>(large);
}
+TEST(FixedArrayExceptionSafety, CopyConstructorWithAlloc) {
+ auto small = FixedArrWithAlloc(kSmallSize);
+ TestThrowingCtor<FixedArrWithAlloc>(small);
+
+ auto large = FixedArrWithAlloc(kLargeSize);
+ TestThrowingCtor<FixedArrWithAlloc>(large);
+}
+
TEST(FixedArrayExceptionSafety, MoveConstructor) {
TestThrowingCtor<FixedArr>(FixedArr(kSmallSize));
TestThrowingCtor<FixedArr>(FixedArr(kLargeSize));
@@ -55,16 +76,35 @@ TEST(FixedArrayExceptionSafety, MoveConstructor) {
TestThrowingCtor<MoveFixedArr>(MoveFixedArr(kLargeSize));
}
+TEST(FixedArrayExceptionSafety, MoveConstructorWithAlloc) {
+ TestThrowingCtor<FixedArrWithAlloc>(FixedArrWithAlloc(kSmallSize));
+ TestThrowingCtor<FixedArrWithAlloc>(FixedArrWithAlloc(kLargeSize));
+
+ // TypeSpec::kNoThrowMove
+ TestThrowingCtor<MoveFixedArrWithAlloc>(MoveFixedArrWithAlloc(kSmallSize));
+ TestThrowingCtor<MoveFixedArrWithAlloc>(MoveFixedArrWithAlloc(kLargeSize));
+}
+
TEST(FixedArrayExceptionSafety, SizeConstructor) {
TestThrowingCtor<FixedArr>(kSmallSize);
TestThrowingCtor<FixedArr>(kLargeSize);
}
+TEST(FixedArrayExceptionSafety, SizeConstructorWithAlloc) {
+ TestThrowingCtor<FixedArrWithAlloc>(kSmallSize);
+ TestThrowingCtor<FixedArrWithAlloc>(kLargeSize);
+}
+
TEST(FixedArrayExceptionSafety, SizeValueConstructor) {
TestThrowingCtor<FixedArr>(kSmallSize, Thrower());
TestThrowingCtor<FixedArr>(kLargeSize, Thrower());
}
+TEST(FixedArrayExceptionSafety, SizeValueConstructorWithAlloc) {
+ TestThrowingCtor<FixedArrWithAlloc>(kSmallSize, Thrower());
+ TestThrowingCtor<FixedArrWithAlloc>(kLargeSize, Thrower());
+}
+
TEST(FixedArrayExceptionSafety, IteratorConstructor) {
auto small = FixedArr(kSmallSize);
TestThrowingCtor<FixedArr>(small.begin(), small.end());
@@ -73,6 +113,14 @@ TEST(FixedArrayExceptionSafety, IteratorConstructor) {
TestThrowingCtor<FixedArr>(large.begin(), large.end());
}
+TEST(FixedArrayExceptionSafety, IteratorConstructorWithAlloc) {
+ auto small = FixedArrWithAlloc(kSmallSize);
+ TestThrowingCtor<FixedArrWithAlloc>(small.begin(), small.end());
+
+ auto large = FixedArrWithAlloc(kLargeSize);
+ TestThrowingCtor<FixedArrWithAlloc>(large.begin(), large.end());
+}
+
TEST(FixedArrayExceptionSafety, InitListConstructor) {
constexpr int small_inlined = 3;
using SmallFixedArr = absl::FixedArray<Thrower, small_inlined>;
@@ -86,7 +134,22 @@ TEST(FixedArrayExceptionSafety, InitListConstructor) {
Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}});
}
-testing::AssertionResult ReadMemory(FixedArr* fixed_arr) {
+TEST(FixedArrayExceptionSafety, InitListConstructorWithAlloc) {
+ constexpr int small_inlined = 3;
+ using SmallFixedArrWithAlloc =
+ absl::FixedArray<Thrower, small_inlined, ThrowAlloc>;
+
+ TestThrowingCtor<SmallFixedArrWithAlloc>(std::initializer_list<Thrower>{});
+ // Test inlined allocation
+ TestThrowingCtor<SmallFixedArrWithAlloc>(
+ std::initializer_list<Thrower>{Thrower{}, Thrower{}});
+ // Test out of line allocation
+ TestThrowingCtor<SmallFixedArrWithAlloc>(std::initializer_list<Thrower>{
+ Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}});
+}
+
+template <typename FixedArrT>
+testing::AssertionResult ReadMemory(FixedArrT* fixed_arr) {
// Marked volatile to prevent optimization. Used for running asan tests.
volatile int sum = 0;
for (const auto& thrower : *fixed_arr) {
@@ -97,7 +160,7 @@ testing::AssertionResult ReadMemory(FixedArr* fixed_arr) {
TEST(FixedArrayExceptionSafety, Fill) {
auto test_fill = testing::MakeExceptionSafetyTester()
- .WithContracts(ReadMemory)
+ .WithContracts(ReadMemory<FixedArr>)
.WithOperation([&](FixedArr* fixed_arr_ptr) {
auto thrower =
Thrower(kUpdatedValue, testing::nothrow_ctor);
@@ -112,7 +175,28 @@ TEST(FixedArrayExceptionSafety, Fill) {
.Test());
}
+TEST(FixedArrayExceptionSafety, FillWithAlloc) {
+ auto test_fill = testing::MakeExceptionSafetyTester()
+ .WithContracts(ReadMemory<FixedArrWithAlloc>)
+ .WithOperation([&](FixedArrWithAlloc* fixed_arr_ptr) {
+ auto thrower =
+ Thrower(kUpdatedValue, testing::nothrow_ctor);
+ fixed_arr_ptr->fill(thrower);
+ });
+
+ EXPECT_TRUE(test_fill
+ .WithInitialValue(
+ FixedArrWithAlloc(kSmallSize, Thrower(kInitialValue)))
+ .Test());
+ EXPECT_TRUE(test_fill
+ .WithInitialValue(
+ FixedArrWithAlloc(kLargeSize, Thrower(kInitialValue)))
+ .Test());
+}
+
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
+
+#endif // ABSL_HAVE_EXCEPTIONS
diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc
index 2b1cf47e..c960fe51 100644
--- a/absl/container/fixed_array_test.cc
+++ b/absl/container/fixed_array_test.cc
@@ -604,19 +604,16 @@ TEST(FixedArrayTest, Fill) {
empty.fill(fill_val);
}
-// TODO(johnsoncj): Investigate InlinedStorage default initialization in GCC 4.x
#ifndef __GNUC__
TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) {
using T = char;
constexpr auto capacity = 10;
using FixedArrType = absl::FixedArray<T, capacity>;
- using FixedArrBuffType =
- absl::aligned_storage_t<sizeof(FixedArrType), alignof(FixedArrType)>;
constexpr auto scrubbed_bits = 0x95;
constexpr auto length = capacity / 2;
- FixedArrBuffType buff;
- std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrBuffType));
+ alignas(FixedArrType) unsigned char buff[sizeof(FixedArrType)];
+ std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrType));
FixedArrType* arr =
::new (static_cast<void*>(std::addressof(buff))) FixedArrType(length);
diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h
index a711398e..fcb70d86 100644
--- a/absl/container/flat_hash_map.h
+++ b/absl/container/flat_hash_map.h
@@ -42,7 +42,7 @@
#include "absl/memory/memory.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class K, class V>
struct FlatHashMapPolicy;
@@ -361,6 +361,10 @@ class flat_hash_map : public absl::container_internal::raw_hash_map<
// Inserts (via copy or move) the element of the specified key into the
// `flat_hash_map` using the position of `hint` as a non-binding suggestion
// for where to begin the insertion search.
+ //
+ // All `try_emplace()` overloads make the same guarantees regarding rvalue
+ // arguments as `std::unordered_map::try_emplace()`, namely that these
+ // functions will not move from rvalue arguments if insertions do not happen.
using Base::try_emplace;
// flat_hash_map::extract()
@@ -398,7 +402,7 @@ class flat_hash_map : public absl::container_internal::raw_hash_map<
// for the past-the-end iterator, which is invalidated.
//
// `swap()` requires that the flat hash map's hashing and key equivalence
- // functions be Swappable, and are exchaged using unqualified calls to
+ // functions be Swappable, and are exchanged using unqualified calls to
// non-member `swap()`. If the map's allocator has
// `std::allocator_traits<allocator_type>::propagate_on_container_swap::value`
// set to `true`, the allocators are also exchanged using an unqualified call
@@ -528,6 +532,15 @@ class flat_hash_map : public absl::container_internal::raw_hash_map<
using Base::key_eq;
};
+// erase_if(flat_hash_map<>, Pred)
+//
+// Erases all elements that satisfy the predicate `pred` from the container `c`.
+template <typename K, typename V, typename H, typename E, typename A,
+ typename Predicate>
+void erase_if(flat_hash_map<K, V, H, E, A>& c, Predicate pred) {
+ container_internal::EraseIf(pred, &c);
+}
+
namespace container_internal {
template <class K, class V>
@@ -581,7 +594,7 @@ struct IsUnorderedContainer<
} // namespace container_algorithm_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_
diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc
index 3f11a52c..728b693a 100644
--- a/absl/container/flat_hash_map_test.cc
+++ b/absl/container/flat_hash_map_test.cc
@@ -14,6 +14,8 @@
#include "absl/container/flat_hash_map.h"
+#include <memory>
+
#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"
@@ -22,12 +24,13 @@
#include "absl/types/any.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
using ::absl::container_internal::hash_internal::Enum;
using ::absl::container_internal::hash_internal::EnumClass;
using ::testing::_;
+using ::testing::IsEmpty;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
@@ -47,6 +50,11 @@ INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, LookupTest, MapTypes);
INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, MembersTest, MapTypes);
INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ModifiersTest, MapTypes);
+using UniquePtrMapTypes = ::testing::Types<Map<int, std::unique_ptr<int>>>;
+
+INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, UniquePtrModifiersTest,
+ UniquePtrMapTypes);
+
TEST(FlatHashMap, StandardLayout) {
struct Int {
explicit Int(size_t value) : value(value) {}
@@ -208,42 +216,44 @@ TEST(FlatHashMap, MergeExtractInsert) {
EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9)));
}
-#if (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) && \
- !defined(__EMSCRIPTEN__)
-TEST(FlatHashMap, Any) {
- absl::flat_hash_map<int, absl::any> m;
- m.emplace(1, 7);
- auto it = m.find(1);
- ASSERT_NE(it, m.end());
- EXPECT_EQ(7, absl::any_cast<int>(it->second));
-
- m.emplace(std::piecewise_construct, std::make_tuple(2), std::make_tuple(8));
- it = m.find(2);
- ASSERT_NE(it, m.end());
- EXPECT_EQ(8, absl::any_cast<int>(it->second));
-
- m.emplace(std::piecewise_construct, std::make_tuple(3),
- std::make_tuple(absl::any(9)));
- it = m.find(3);
- ASSERT_NE(it, m.end());
- EXPECT_EQ(9, absl::any_cast<int>(it->second));
-
- struct H {
- size_t operator()(const absl::any&) const { return 0; }
- };
- struct E {
- bool operator()(const absl::any&, const absl::any&) const { return true; }
- };
- absl::flat_hash_map<absl::any, int, H, E> m2;
- m2.emplace(1, 7);
- auto it2 = m2.find(1);
- ASSERT_NE(it2, m2.end());
- EXPECT_EQ(7, it2->second);
+bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; }
+
+TEST(FlatHashMap, EraseIf) {
+ // Erase all elements.
+ {
+ flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s, [](std::pair<const int, int>) { return true; });
+ EXPECT_THAT(s, IsEmpty());
+ }
+ // Erase no elements.
+ {
+ flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s, [](std::pair<const int, int>) { return false; });
+ EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3),
+ Pair(4, 4), Pair(5, 5)));
+ }
+ // Erase specific elements.
+ {
+ flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s,
+ [](std::pair<const int, int> kvp) { return kvp.first % 2 == 1; });
+ EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4)));
+ }
+ // Predicate is function reference.
+ {
+ flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s, FirstIsEven);
+ EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
+ }
+ // Predicate is function pointer.
+ {
+ flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s, &FirstIsEven);
+ EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
+ }
}
-#endif // (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) &&
- // !defined(__EMSCRIPTEN__)
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h
index 8adbbcd5..94be6e3d 100644
--- a/absl/container/flat_hash_set.h
+++ b/absl/container/flat_hash_set.h
@@ -40,7 +40,7 @@
#include "absl/memory/memory.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <typename T>
struct FlatHashSetPolicy;
@@ -439,6 +439,14 @@ class flat_hash_set
using Base::key_eq;
};
+// erase_if(flat_hash_set<>, Pred)
+//
+// Erases all elements that satisfy the predicate `pred` from the container `c`.
+template <typename T, typename H, typename E, typename A, typename Predicate>
+void erase_if(flat_hash_set<T, H, E, A>& c, Predicate pred) {
+ container_internal::EraseIf(pred, &c);
+}
+
namespace container_internal {
template <class T>
@@ -489,7 +497,7 @@ struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>>
} // namespace container_algorithm_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_FLAT_HASH_SET_H_
diff --git a/absl/container/flat_hash_set_test.cc b/absl/container/flat_hash_set_test.cc
index 56140bbe..40d7f85c 100644
--- a/absl/container/flat_hash_set_test.cc
+++ b/absl/container/flat_hash_set_test.cc
@@ -25,12 +25,13 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
using ::absl::container_internal::hash_internal::Enum;
using ::absl::container_internal::hash_internal::EnumClass;
+using ::testing::IsEmpty;
using ::testing::Pointee;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;
@@ -124,7 +125,42 @@ TEST(FlatHashSet, MergeExtractInsert) {
EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23)));
}
+bool IsEven(int k) { return k % 2 == 0; }
+
+TEST(FlatHashSet, EraseIf) {
+ // Erase all elements.
+ {
+ flat_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, [](int) { return true; });
+ EXPECT_THAT(s, IsEmpty());
+ }
+ // Erase no elements.
+ {
+ flat_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, [](int) { return false; });
+ EXPECT_THAT(s, UnorderedElementsAre(1, 2, 3, 4, 5));
+ }
+ // Erase specific elements.
+ {
+ flat_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, [](int k) { return k % 2 == 1; });
+ EXPECT_THAT(s, UnorderedElementsAre(2, 4));
+ }
+ // Predicate is function reference.
+ {
+ flat_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, IsEven);
+ EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5));
+ }
+ // Predicate is function pointer.
+ {
+ flat_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, &IsEven);
+ EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5));
+ }
+}
+
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 27186b15..2388d471 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -54,7 +54,7 @@
#include "absl/memory/memory.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// InlinedVector
// -----------------------------------------------------------------------------
@@ -70,9 +70,10 @@ class InlinedVector {
static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity.");
using Storage = inlined_vector_internal::Storage<T, N, A>;
- using rvalue_reference = typename Storage::rvalue_reference;
- using MoveIterator = typename Storage::MoveIterator;
+
using AllocatorTraits = typename Storage::AllocatorTraits;
+ using RValueReference = typename Storage::RValueReference;
+ using MoveIterator = typename Storage::MoveIterator;
using IsMemcpyOk = typename Storage::IsMemcpyOk;
template <typename Iterator>
@@ -93,10 +94,10 @@ class InlinedVector {
using value_type = typename Storage::value_type;
using pointer = typename Storage::pointer;
using const_pointer = typename Storage::const_pointer;
- using reference = typename Storage::reference;
- using const_reference = typename Storage::const_reference;
using size_type = typename Storage::size_type;
using difference_type = typename Storage::difference_type;
+ using reference = typename Storage::reference;
+ using const_reference = typename Storage::const_reference;
using iterator = typename Storage::iterator;
using const_iterator = typename Storage::const_iterator;
using reverse_iterator = typename Storage::reverse_iterator;
@@ -534,7 +535,6 @@ class InlinedVector {
}
erase(data() + i, data() + size());
-
std::copy(first, last, std::back_inserter(*this));
}
@@ -565,7 +565,7 @@ class InlinedVector {
// Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using
// move semantics, returning an `iterator` to the newly inserted element.
- iterator insert(const_iterator pos, rvalue_reference v) {
+ iterator insert(const_iterator pos, RValueReference v) {
return emplace(pos, std::move(v));
}
@@ -662,7 +662,7 @@ class InlinedVector {
// Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()`
// using move semantics.
- void push_back(rvalue_reference v) {
+ void push_back(RValueReference v) {
static_cast<void>(emplace_back(std::move(v)));
}
@@ -714,6 +714,7 @@ class InlinedVector {
inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(),
size());
storage_.DeallocateIfAllocated();
+
storage_.SetInlinedSize(0);
}
@@ -841,7 +842,7 @@ H AbslHashValue(H h, const absl::InlinedVector<T, N, A>& a) {
return H::combine(H::combine_contiguous(std::move(h), a.data(), size), size);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INLINED_VECTOR_H_
diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc
index b99bbd62..3f2b4ed2 100644
--- a/absl/container/inlined_vector_benchmark.cc
+++ b/absl/container/inlined_vector_benchmark.cc
@@ -25,42 +25,45 @@
namespace {
void BM_InlinedVectorFill(benchmark::State& state) {
+ const int len = state.range(0);
absl::InlinedVector<int, 8> v;
- int val = 10;
+ v.reserve(len);
for (auto _ : state) {
+ v.resize(0); // Use resize(0) as InlinedVector releases storage on clear().
+ for (int i = 0; i < len; ++i) {
+ v.push_back(i);
+ }
benchmark::DoNotOptimize(v);
- v.push_back(val);
}
}
-BENCHMARK(BM_InlinedVectorFill)->Range(0, 1024);
+BENCHMARK(BM_InlinedVectorFill)->Range(1, 256);
void BM_InlinedVectorFillRange(benchmark::State& state) {
const int len = state.range(0);
- std::unique_ptr<int[]> ia(new int[len]);
- for (int i = 0; i < len; i++) {
- ia[i] = i;
- }
- auto* from = ia.get();
- auto* to = from + len;
+ const std::vector<int> src(len, len);
+ absl::InlinedVector<int, 8> v;
+ v.reserve(len);
for (auto _ : state) {
- benchmark::DoNotOptimize(from);
- benchmark::DoNotOptimize(to);
- absl::InlinedVector<int, 8> v(from, to);
+ benchmark::DoNotOptimize(src);
+ v.assign(src.begin(), src.end());
benchmark::DoNotOptimize(v);
}
}
-BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024);
+BENCHMARK(BM_InlinedVectorFillRange)->Range(1, 256);
void BM_StdVectorFill(benchmark::State& state) {
+ const int len = state.range(0);
std::vector<int> v;
- int val = 10;
+ v.reserve(len);
for (auto _ : state) {
+ v.clear();
+ for (int i = 0; i < len; ++i) {
+ v.push_back(i);
+ }
benchmark::DoNotOptimize(v);
- benchmark::DoNotOptimize(val);
- v.push_back(val);
}
}
-BENCHMARK(BM_StdVectorFill)->Range(0, 1024);
+BENCHMARK(BM_StdVectorFill)->Range(1, 256);
// The purpose of the next two benchmarks is to verify that
// absl::InlinedVector is efficient when moving is more efficent than
diff --git a/absl/container/inlined_vector_exception_safety_test.cc b/absl/container/inlined_vector_exception_safety_test.cc
index ff0da75b..0e6a05b5 100644
--- a/absl/container/inlined_vector_exception_safety_test.cc
+++ b/absl/container/inlined_vector_exception_safety_test.cc
@@ -12,6 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "absl/container/inlined_vector.h"
+
+#include "absl/base/config.h"
+
+#if defined(ABSL_HAVE_EXCEPTIONS)
+
#include <array>
#include <initializer_list>
#include <iterator>
@@ -20,7 +26,6 @@
#include "gtest/gtest.h"
#include "absl/base/internal/exception_safety_testing.h"
-#include "absl/container/inlined_vector.h"
namespace {
@@ -57,8 +62,8 @@ using ThrowAllocMovableThrowerVec =
\
: std::initializer_list<T>{T(0, testing::nothrow_ctor), \
T(1, testing::nothrow_ctor)})
-static_assert((kLargeSize == 8 || kSmallSize == 2),
- "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...).");
+static_assert(kLargeSize == 8, "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)");
+static_assert(kSmallSize == 2, "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)");
template <typename TheVecT, size_t... TheSizes>
class TestParams {
@@ -359,9 +364,11 @@ TYPED_TEST(OneSizeTest, EmplaceBack) {
using VecT = typename TypeParam::VecT;
constexpr static auto size = TypeParam::GetSizeAt(0);
+ // For testing calls to `emplace_back(...)` that reallocate.
VecT full_vec{size};
full_vec.resize(full_vec.capacity());
+ // For testing calls to `emplace_back(...)` that don't reallocate.
VecT nonfull_vec{size};
nonfull_vec.reserve(size + 1);
@@ -369,12 +376,11 @@ TYPED_TEST(OneSizeTest, EmplaceBack) {
InlinedVectorInvariants<VecT>);
EXPECT_TRUE(tester.WithInitialValue(nonfull_vec).Test([](VecT* vec) {
- vec->emplace_back(); //
+ vec->emplace_back();
}));
- EXPECT_TRUE(tester.WithInitialValue(full_vec).Test([](VecT* vec) {
- vec->emplace_back(); //
- }));
+ EXPECT_TRUE(tester.WithInitialValue(full_vec).Test(
+ [](VecT* vec) { vec->emplace_back(); }));
}
TYPED_TEST(OneSizeTest, PopBack) {
@@ -413,6 +419,19 @@ TYPED_TEST(OneSizeTest, Erase) {
EXPECT_TRUE(tester.Test([](VecT* vec) {
auto it = vec->begin();
+ vec->erase(it, it);
+ }));
+ EXPECT_TRUE(tester.Test([](VecT* vec) {
+ auto it = vec->begin() + (vec->size() / 2);
+ vec->erase(it, it);
+ }));
+ EXPECT_TRUE(tester.Test([](VecT* vec) {
+ auto it = vec->begin() + (vec->size() - 1);
+ vec->erase(it, it);
+ }));
+
+ EXPECT_TRUE(tester.Test([](VecT* vec) {
+ auto it = vec->begin();
vec->erase(it, it + 1);
}));
EXPECT_TRUE(tester.Test([](VecT* vec) {
@@ -447,9 +466,7 @@ TYPED_TEST(TwoSizeTest, Reserve) {
.WithInitialValue(VecT{from_size})
.WithContracts(InlinedVectorInvariants<VecT>);
- EXPECT_TRUE(tester.Test([](VecT* vec) {
- vec->reserve(to_capacity); //
- }));
+ EXPECT_TRUE(tester.Test([](VecT* vec) { vec->reserve(to_capacity); }));
}
TYPED_TEST(OneSizeTest, ShrinkToFit) {
@@ -487,3 +504,5 @@ TYPED_TEST(TwoSizeTest, Swap) {
}
} // namespace
+
+#endif // defined(ABSL_HAVE_EXCEPTIONS)
diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc
index bada4fec..2c9b0d0e 100644
--- a/absl/container/inlined_vector_test.cc
+++ b/absl/container/inlined_vector_test.cc
@@ -1689,7 +1689,11 @@ TEST(AllocatorSupportTest, ScopedAllocatorWorksInlined) {
inlined_case.emplace_back();
int64_t absl_responsible_for_count = total_allocated_byte_count;
+
+ // MSVC's allocator preemptively allocates in debug mode
+#if !defined(_MSC_VER)
EXPECT_EQ(absl_responsible_for_count, 0);
+#endif // !defined(_MSC_VER)
inlined_case[0].emplace_back();
EXPECT_GT(total_allocated_byte_count, absl_responsible_for_count);
@@ -1750,6 +1754,30 @@ TEST(AllocatorSupportTest, SizeAllocConstructor) {
}
}
+TEST(InlinedVectorTest, MinimumAllocatorCompilesUsingTraits) {
+ using T = int;
+ using A = std::allocator<T>;
+ using ATraits = absl::allocator_traits<A>;
+
+ struct MinimumAllocator {
+ using value_type = T;
+
+ value_type* allocate(size_t n) {
+ A a;
+ return ATraits::allocate(a, n);
+ }
+
+ void deallocate(value_type* p, size_t n) {
+ A a;
+ ATraits::deallocate(a, p, n);
+ }
+ };
+
+ absl::InlinedVector<T, 1, MinimumAllocator> vec;
+ vec.emplace_back();
+ vec.resize(0);
+}
+
TEST(InlinedVectorTest, AbslHashValueWorks) {
using V = absl::InlinedVector<int, 4>;
std::vector<V> cases;
diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h
new file mode 100644
index 00000000..fd5c0e7a
--- /dev/null
+++ b/absl/container/internal/btree.h
@@ -0,0 +1,2614 @@
+// 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.
+
+// A btree implementation of the STL set and map interfaces. A btree is smaller
+// and generally also faster than STL set/map (refer to the benchmarks below).
+// The red-black tree implementation of STL set/map has an overhead of 3
+// pointers (left, right and parent) plus the node color information for each
+// stored value. So a set<int32_t> consumes 40 bytes for each value stored in
+// 64-bit mode. This btree implementation stores multiple values on fixed
+// size nodes (usually 256 bytes) and doesn't store child pointers for leaf
+// nodes. The result is that a btree_set<int32_t> may use much less memory per
+// stored value. For the random insertion benchmark in btree_bench.cc, a
+// btree_set<int32_t> with node-size of 256 uses 5.1 bytes per stored value.
+//
+// The packing of multiple values on to each node of a btree has another effect
+// besides better space utilization: better cache locality due to fewer cache
+// lines being accessed. Better cache locality translates into faster
+// operations.
+//
+// CAVEATS
+//
+// Insertions and deletions on a btree can cause splitting, merging or
+// rebalancing of btree nodes. And even without these operations, insertions
+// and deletions on a btree will move values around within a node. In both
+// cases, the result is that insertions and deletions can invalidate iterators
+// pointing to values other than the one being inserted/deleted. Therefore, this
+// container does not provide pointer stability. This is notably different from
+// STL set/map which takes care to not invalidate iterators on insert/erase
+// except, of course, for iterators pointing to the value being erased. A
+// partial workaround when erasing is available: erase() returns an iterator
+// pointing to the item just after the one that was erased (or end() if none
+// exists).
+
+#ifndef ABSL_CONTAINER_INTERNAL_BTREE_H_
+#define ABSL_CONTAINER_INTERNAL_BTREE_H_
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <new>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/macros.h"
+#include "absl/container/internal/common.h"
+#include "absl/container/internal/compressed_tuple.h"
+#include "absl/container/internal/container_memory.h"
+#include "absl/container/internal/layout.h"
+#include "absl/memory/memory.h"
+#include "absl/meta/type_traits.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/compare.h"
+#include "absl/utility/utility.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+
+// A helper class that indicates if the Compare parameter is a key-compare-to
+// comparator.
+template <typename Compare, typename T>
+using btree_is_key_compare_to =
+ std::is_convertible<absl::result_of_t<Compare(const T &, const T &)>,
+ absl::weak_ordering>;
+
+struct StringBtreeDefaultLess {
+ using is_transparent = void;
+
+ StringBtreeDefaultLess() = default;
+
+ // Compatibility constructor.
+ StringBtreeDefaultLess(std::less<std::string>) {} // NOLINT
+ StringBtreeDefaultLess(std::less<string_view>) {} // NOLINT
+
+ absl::weak_ordering operator()(absl::string_view lhs,
+ absl::string_view rhs) const {
+ return compare_internal::compare_result_as_ordering(lhs.compare(rhs));
+ }
+};
+
+struct StringBtreeDefaultGreater {
+ using is_transparent = void;
+
+ StringBtreeDefaultGreater() = default;
+
+ StringBtreeDefaultGreater(std::greater<std::string>) {} // NOLINT
+ StringBtreeDefaultGreater(std::greater<string_view>) {} // NOLINT
+
+ absl::weak_ordering operator()(absl::string_view lhs,
+ absl::string_view rhs) const {
+ return compare_internal::compare_result_as_ordering(rhs.compare(lhs));
+ }
+};
+
+// A helper class to convert a boolean comparison into a three-way "compare-to"
+// comparison that returns a negative value to indicate less-than, zero to
+// indicate equality and a positive value to indicate greater-than. This helper
+// class is specialized for less<std::string>, greater<std::string>,
+// less<string_view>, and greater<string_view>.
+//
+// key_compare_to_adapter is provided so that btree users
+// automatically get the more efficient compare-to code when using common
+// google string types with common comparison functors.
+// These string-like specializations also turn on heterogeneous lookup by
+// default.
+template <typename Compare>
+struct key_compare_to_adapter {
+ using type = Compare;
+};
+
+template <>
+struct key_compare_to_adapter<std::less<std::string>> {
+ using type = StringBtreeDefaultLess;
+};
+
+template <>
+struct key_compare_to_adapter<std::greater<std::string>> {
+ using type = StringBtreeDefaultGreater;
+};
+
+template <>
+struct key_compare_to_adapter<std::less<absl::string_view>> {
+ using type = StringBtreeDefaultLess;
+};
+
+template <>
+struct key_compare_to_adapter<std::greater<absl::string_view>> {
+ using type = StringBtreeDefaultGreater;
+};
+
+template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
+ bool Multi, typename SlotPolicy>
+struct common_params {
+ // If Compare is a common comparator for a std::string-like type, then we adapt it
+ // to use heterogeneous lookup and to be a key-compare-to comparator.
+ using key_compare = typename key_compare_to_adapter<Compare>::type;
+ // A type which indicates if we have a key-compare-to functor or a plain old
+ // key-compare functor.
+ using is_key_compare_to = btree_is_key_compare_to<key_compare, Key>;
+
+ using allocator_type = Alloc;
+ using key_type = Key;
+ using size_type = std::make_signed<size_t>::type;
+ using difference_type = ptrdiff_t;
+
+ // True if this is a multiset or multimap.
+ using is_multi_container = std::integral_constant<bool, Multi>;
+
+ using slot_policy = SlotPolicy;
+ using slot_type = typename slot_policy::slot_type;
+ using value_type = typename slot_policy::value_type;
+ using init_type = typename slot_policy::mutable_value_type;
+ using pointer = value_type *;
+ using const_pointer = const value_type *;
+ using reference = value_type &;
+ using const_reference = const value_type &;
+
+ enum {
+ kTargetNodeSize = TargetNodeSize,
+
+ // Upper bound for the available space for values. This is largest for leaf
+ // nodes, which have overhead of at least a pointer + 4 bytes (for storing
+ // 3 field_types and an enum).
+ kNodeValueSpace =
+ TargetNodeSize - /*minimum overhead=*/(sizeof(void *) + 4),
+ };
+
+ // This is an integral type large enough to hold as many
+ // ValueSize-values as will fit a node of TargetNodeSize bytes.
+ using node_count_type =
+ absl::conditional_t<(kNodeValueSpace / sizeof(value_type) >
+ (std::numeric_limits<uint8_t>::max)()),
+ uint16_t, uint8_t>; // NOLINT
+
+ // The following methods are necessary for passing this struct as PolicyTraits
+ // for node_handle and/or are used within btree.
+ static value_type &element(slot_type *slot) {
+ return slot_policy::element(slot);
+ }
+ static const value_type &element(const slot_type *slot) {
+ return slot_policy::element(slot);
+ }
+ template <class... Args>
+ static void construct(Alloc *alloc, slot_type *slot, Args &&... args) {
+ slot_policy::construct(alloc, slot, std::forward<Args>(args)...);
+ }
+ static void construct(Alloc *alloc, slot_type *slot, slot_type *other) {
+ slot_policy::construct(alloc, slot, other);
+ }
+ static void destroy(Alloc *alloc, slot_type *slot) {
+ slot_policy::destroy(alloc, slot);
+ }
+ static void transfer(Alloc *alloc, slot_type *new_slot, slot_type *old_slot) {
+ construct(alloc, new_slot, old_slot);
+ destroy(alloc, old_slot);
+ }
+ static void swap(Alloc *alloc, slot_type *a, slot_type *b) {
+ slot_policy::swap(alloc, a, b);
+ }
+ static void move(Alloc *alloc, slot_type *src, slot_type *dest) {
+ slot_policy::move(alloc, src, dest);
+ }
+ static void move(Alloc *alloc, slot_type *first, slot_type *last,
+ slot_type *result) {
+ slot_policy::move(alloc, first, last, result);
+ }
+};
+
+// A parameters structure for holding the type parameters for a btree_map.
+// Compare and Alloc should be nothrow copy-constructible.
+template <typename Key, typename Data, typename Compare, typename Alloc,
+ int TargetNodeSize, bool Multi>
+struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi,
+ map_slot_policy<Key, Data>> {
+ using super_type = typename map_params::common_params;
+ using mapped_type = Data;
+ // This type allows us to move keys when it is safe to do so. It is safe
+ // for maps in which value_type and mutable_value_type are layout compatible.
+ using slot_policy = typename super_type::slot_policy;
+ using slot_type = typename super_type::slot_type;
+ using value_type = typename super_type::value_type;
+ using init_type = typename super_type::init_type;
+
+ using key_compare = typename super_type::key_compare;
+ // Inherit from key_compare for empty base class optimization.
+ struct value_compare : private key_compare {
+ value_compare() = default;
+ explicit value_compare(const key_compare &cmp) : key_compare(cmp) {}
+
+ template <typename T, typename U>
+ auto operator()(const T &left, const U &right) const
+ -> decltype(std::declval<key_compare>()(left.first, right.first)) {
+ return key_compare::operator()(left.first, right.first);
+ }
+ };
+ using is_map_container = std::true_type;
+
+ static const Key &key(const value_type &x) { return x.first; }
+ static const Key &key(const init_type &x) { return x.first; }
+ static const Key &key(const slot_type *x) { return slot_policy::key(x); }
+ static mapped_type &value(value_type *value) { return value->second; }
+};
+
+// This type implements the necessary functions from the
+// absl::container_internal::slot_type interface.
+template <typename Key>
+struct set_slot_policy {
+ using slot_type = Key;
+ using value_type = Key;
+ using mutable_value_type = Key;
+
+ static value_type &element(slot_type *slot) { return *slot; }
+ static const value_type &element(const slot_type *slot) { return *slot; }
+
+ template <typename Alloc, class... Args>
+ static void construct(Alloc *alloc, slot_type *slot, Args &&... args) {
+ absl::allocator_traits<Alloc>::construct(*alloc, slot,
+ std::forward<Args>(args)...);
+ }
+
+ template <typename Alloc>
+ static void construct(Alloc *alloc, slot_type *slot, slot_type *other) {
+ absl::allocator_traits<Alloc>::construct(*alloc, slot, std::move(*other));
+ }
+
+ template <typename Alloc>
+ static void destroy(Alloc *alloc, slot_type *slot) {
+ absl::allocator_traits<Alloc>::destroy(*alloc, slot);
+ }
+
+ template <typename Alloc>
+ static void swap(Alloc * /*alloc*/, slot_type *a, slot_type *b) {
+ using std::swap;
+ swap(*a, *b);
+ }
+
+ template <typename Alloc>
+ static void move(Alloc * /*alloc*/, slot_type *src, slot_type *dest) {
+ *dest = std::move(*src);
+ }
+
+ template <typename Alloc>
+ static void move(Alloc *alloc, slot_type *first, slot_type *last,
+ slot_type *result) {
+ for (slot_type *src = first, *dest = result; src != last; ++src, ++dest)
+ move(alloc, src, dest);
+ }
+};
+
+// A parameters structure for holding the type parameters for a btree_set.
+// Compare and Alloc should be nothrow copy-constructible.
+template <typename Key, typename Compare, typename Alloc, int TargetNodeSize,
+ bool Multi>
+struct set_params : common_params<Key, Compare, Alloc, TargetNodeSize, Multi,
+ set_slot_policy<Key>> {
+ using value_type = Key;
+ using slot_type = typename set_params::common_params::slot_type;
+ using value_compare = typename set_params::common_params::key_compare;
+ using is_map_container = std::false_type;
+
+ static const Key &key(const value_type &x) { return x; }
+ static const Key &key(const slot_type *x) { return *x; }
+};
+
+// An adapter class that converts a lower-bound compare into an upper-bound
+// compare. Note: there is no need to make a version of this adapter specialized
+// for key-compare-to functors because the upper-bound (the first value greater
+// than the input) is never an exact match.
+template <typename Compare>
+struct upper_bound_adapter {
+ explicit upper_bound_adapter(const Compare &c) : comp(c) {}
+ template <typename K, typename LK>
+ bool operator()(const K &a, const LK &b) const {
+ // Returns true when a is not greater than b.
+ return !compare_internal::compare_result_as_less_than(comp(b, a));
+ }
+
+ private:
+ Compare comp;
+};
+
+enum class MatchKind : uint8_t { kEq, kNe };
+
+template <typename V, bool IsCompareTo>
+struct SearchResult {
+ V value;
+ MatchKind match;
+
+ static constexpr bool HasMatch() { return true; }
+ bool IsEq() const { return match == MatchKind::kEq; }
+};
+
+// When we don't use CompareTo, `match` is not present.
+// This ensures that callers can't use it accidentally when it provides no
+// useful information.
+template <typename V>
+struct SearchResult<V, false> {
+ V value;
+
+ static constexpr bool HasMatch() { return false; }
+ static constexpr bool IsEq() { return false; }
+};
+
+// A node in the btree holding. The same node type is used for both internal
+// and leaf nodes in the btree, though the nodes are allocated in such a way
+// that the children array is only valid in internal nodes.
+template <typename Params>
+class btree_node {
+ using is_key_compare_to = typename Params::is_key_compare_to;
+ using is_multi_container = typename Params::is_multi_container;
+ using field_type = typename Params::node_count_type;
+ using allocator_type = typename Params::allocator_type;
+ using slot_type = typename Params::slot_type;
+
+ public:
+ using params_type = Params;
+ using key_type = typename Params::key_type;
+ using value_type = typename Params::value_type;
+ using pointer = typename Params::pointer;
+ using const_pointer = typename Params::const_pointer;
+ using reference = typename Params::reference;
+ using const_reference = typename Params::const_reference;
+ using key_compare = typename Params::key_compare;
+ using size_type = typename Params::size_type;
+ using difference_type = typename Params::difference_type;
+
+ // Btree decides whether to use linear node search as follows:
+ // - If the key is arithmetic and the comparator is std::less or
+ // std::greater, choose linear.
+ // - Otherwise, choose binary.
+ // TODO(ezb): Might make sense to add condition(s) based on node-size.
+ using use_linear_search = std::integral_constant<
+ bool,
+ std::is_arithmetic<key_type>::value &&
+ (std::is_same<std::less<key_type>, key_compare>::value ||
+ std::is_same<std::greater<key_type>, key_compare>::value)>;
+
+ // This class is organized by gtl::Layout as if it had the following
+ // structure:
+ // // A pointer to the node's parent.
+ // btree_node *parent;
+ //
+ // // The position of the node in the node's parent.
+ // field_type position;
+ // // The index of the first populated value in `values`.
+ // // TODO(ezb): right now, `start` is always 0. Update insertion/merge
+ // // logic to allow for floating storage within nodes.
+ // field_type start;
+ // // The index after the last populated value in `values`. Currently, this
+ // // is the same as the count of values.
+ // field_type finish;
+ // // The maximum number of values the node can hold. This is an integer in
+ // // [1, kNodeValues] for root leaf nodes, kNodeValues for non-root leaf
+ // // nodes, and kInternalNodeMaxCount (as a sentinel value) for internal
+ // // nodes (even though there are still kNodeValues values in the node).
+ // // TODO(ezb): make max_count use only 4 bits and record log2(capacity)
+ // // to free extra bits for is_root, etc.
+ // field_type max_count;
+ //
+ // // The array of values. The capacity is `max_count` for leaf nodes and
+ // // kNodeValues for internal nodes. Only the values in
+ // // [start, finish) have been initialized and are valid.
+ // slot_type values[max_count];
+ //
+ // // The array of child pointers. The keys in children[i] are all less
+ // // than key(i). The keys in children[i + 1] are all greater than key(i).
+ // // There are 0 children for leaf nodes and kNodeValues + 1 children for
+ // // internal nodes.
+ // btree_node *children[kNodeValues + 1];
+ //
+ // This class is only constructed by EmptyNodeType. Normally, pointers to the
+ // layout above are allocated, cast to btree_node*, and de-allocated within
+ // the btree implementation.
+ ~btree_node() = default;
+ 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;
+
+ private:
+ using layout_type = absl::container_internal::Layout<btree_node *, field_type,
+ slot_type, btree_node *>;
+ constexpr static size_type SizeWithNValues(size_type n) {
+ return layout_type(/*parent*/ 1,
+ /*position, start, finish, max_count*/ 4,
+ /*values*/ n,
+ /*children*/ 0)
+ .AllocSize();
+ }
+ // A lower bound for the overhead of fields other than values in a leaf node.
+ constexpr static size_type MinimumOverhead() {
+ return SizeWithNValues(1) - sizeof(value_type);
+ }
+
+ // Compute how many values we can fit onto a leaf node taking into account
+ // padding.
+ constexpr static size_type NodeTargetValues(const int begin, const int end) {
+ return begin == end ? begin
+ : SizeWithNValues((begin + end) / 2 + 1) >
+ params_type::kTargetNodeSize
+ ? NodeTargetValues(begin, (begin + end) / 2)
+ : NodeTargetValues((begin + end) / 2 + 1, end);
+ }
+
+ enum {
+ kTargetNodeSize = params_type::kTargetNodeSize,
+ kNodeTargetValues = NodeTargetValues(0, params_type::kTargetNodeSize),
+
+ // We need a minimum of 3 values per internal node in order to perform
+ // splitting (1 value for the two nodes involved in the split and 1 value
+ // propagated to the parent as the delimiter for the split).
+ kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3,
+
+ // The node is internal (i.e. is not a leaf node) if and only if `max_count`
+ // has this value.
+ kInternalNodeMaxCount = 0,
+ };
+
+ // Leaves can have less than kNodeValues values.
+ constexpr static layout_type LeafLayout(const int max_values = kNodeValues) {
+ return layout_type(/*parent*/ 1,
+ /*position, start, finish, max_count*/ 4,
+ /*values*/ max_values,
+ /*children*/ 0);
+ }
+ constexpr static layout_type InternalLayout() {
+ return layout_type(/*parent*/ 1,
+ /*position, start, finish, max_count*/ 4,
+ /*values*/ kNodeValues,
+ /*children*/ kNodeValues + 1);
+ }
+ constexpr static size_type LeafSize(const int max_values = kNodeValues) {
+ return LeafLayout(max_values).AllocSize();
+ }
+ constexpr static size_type InternalSize() {
+ return InternalLayout().AllocSize();
+ }
+
+ // 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>
+ inline typename layout_type::template ElementType<N> *GetField() {
+ // We assert that we don't read from values that aren't there.
+ assert(N < 3 || !leaf());
+ return InternalLayout().template Pointer<N>(reinterpret_cast<char *>(this));
+ }
+ template <size_type N>
+ inline const typename layout_type::template ElementType<N> *GetField() const {
+ assert(N < 3 || !leaf());
+ return InternalLayout().template Pointer<N>(
+ reinterpret_cast<const char *>(this));
+ }
+ void set_parent(btree_node *p) { *GetField<0>() = p; }
+ field_type &mutable_finish() { return GetField<1>()[2]; }
+ slot_type *slot(int i) { return &GetField<2>()[i]; }
+ slot_type *start_slot() { return slot(start()); }
+ slot_type *finish_slot() { return slot(finish()); }
+ const slot_type *slot(int i) const { return &GetField<2>()[i]; }
+ void set_position(field_type v) { GetField<1>()[0] = v; }
+ void set_start(field_type v) { GetField<1>()[1] = v; }
+ void set_finish(field_type v) { GetField<1>()[2] = v; }
+ // This method is only called by the node init methods.
+ void set_max_count(field_type v) { GetField<1>()[3] = v; }
+
+ public:
+ // Whether this is a leaf node or not. This value doesn't change after the
+ // node is created.
+ bool leaf() const { return GetField<1>()[3] != kInternalNodeMaxCount; }
+
+ // Getter for the position of this node in its parent.
+ field_type position() const { return GetField<1>()[0]; }
+
+ // Getter for the offset of the first value in the `values` array.
+ field_type start() const {
+ // TODO(ezb): when floating storage is implemented, return GetField<1>()[1];
+ assert(GetField<1>()[1] == 0);
+ return 0;
+ }
+
+ // Getter for the offset after the last value in the `values` array.
+ field_type finish() const { return GetField<1>()[2]; }
+
+ // Getters for the number of values stored in this node.
+ field_type count() const {
+ assert(finish() >= start());
+ return finish() - start();
+ }
+ field_type max_count() const {
+ // Internal nodes have max_count==kInternalNodeMaxCount.
+ // Leaf nodes have max_count in [1, kNodeValues].
+ const field_type max_count = GetField<1>()[3];
+ return max_count == field_type{kInternalNodeMaxCount}
+ ? field_type{kNodeValues}
+ : max_count;
+ }
+
+ // Getter for the parent of this node.
+ btree_node *parent() const { return *GetField<0>(); }
+ // Getter for whether the node is the root of the tree. The parent of the
+ // root of the tree is the leftmost node in the tree which is guaranteed to
+ // be a leaf.
+ bool is_root() const { return parent()->leaf(); }
+ void make_root() {
+ assert(parent()->is_root());
+ set_parent(parent()->parent());
+ }
+
+ // Getters for the key/value at position i in the node.
+ const key_type &key(int i) const { return params_type::key(slot(i)); }
+ reference value(int i) { return params_type::element(slot(i)); }
+ const_reference value(int i) const { return params_type::element(slot(i)); }
+
+ // Getters/setter for the child at position i in the node.
+ btree_node *child(int i) const { return GetField<3>()[i]; }
+ btree_node *start_child() const { return child(start()); }
+ btree_node *&mutable_child(int i) { return GetField<3>()[i]; }
+ void clear_child(int i) {
+ absl::container_internal::SanitizerPoisonObject(&mutable_child(i));
+ }
+ void set_child(int i, btree_node *c) {
+ absl::container_internal::SanitizerUnpoisonObject(&mutable_child(i));
+ mutable_child(i) = c;
+ c->set_position(i);
+ }
+ void init_child(int i, btree_node *c) {
+ set_child(i, c);
+ c->set_parent(this);
+ }
+
+ // Returns the position of the first value whose key is not less than k.
+ template <typename K>
+ SearchResult<int, is_key_compare_to::value> lower_bound(
+ const K &k, const key_compare &comp) const {
+ return use_linear_search::value ? linear_search(k, comp)
+ : binary_search(k, comp);
+ }
+ // Returns the position of the first value whose key is greater than k.
+ template <typename K>
+ int upper_bound(const K &k, const key_compare &comp) const {
+ auto upper_compare = upper_bound_adapter<key_compare>(comp);
+ return use_linear_search::value ? linear_search(k, upper_compare).value
+ : binary_search(k, upper_compare).value;
+ }
+
+ template <typename K, typename Compare>
+ SearchResult<int, btree_is_key_compare_to<Compare, key_type>::value>
+ linear_search(const K &k, const Compare &comp) const {
+ return linear_search_impl(k, start(), finish(), comp,
+ btree_is_key_compare_to<Compare, key_type>());
+ }
+
+ template <typename K, typename Compare>
+ SearchResult<int, btree_is_key_compare_to<Compare, key_type>::value>
+ binary_search(const K &k, const Compare &comp) const {
+ return binary_search_impl(k, start(), finish(), comp,
+ btree_is_key_compare_to<Compare, key_type>());
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // linear search performed using plain compare.
+ template <typename K, typename Compare>
+ SearchResult<int, false> linear_search_impl(
+ const K &k, int s, const int e, const Compare &comp,
+ std::false_type /* IsCompareTo */) const {
+ while (s < e) {
+ if (!comp(key(s), k)) {
+ break;
+ }
+ ++s;
+ }
+ return {s};
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // linear search performed using compare-to.
+ template <typename K, typename Compare>
+ SearchResult<int, true> linear_search_impl(
+ const K &k, int s, const int e, const Compare &comp,
+ std::true_type /* IsCompareTo */) const {
+ while (s < e) {
+ const absl::weak_ordering c = comp(key(s), k);
+ if (c == 0) {
+ return {s, MatchKind::kEq};
+ } else if (c > 0) {
+ break;
+ }
+ ++s;
+ }
+ return {s, MatchKind::kNe};
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // binary search performed using plain compare.
+ template <typename K, typename Compare>
+ SearchResult<int, false> binary_search_impl(
+ const K &k, int s, int e, const Compare &comp,
+ std::false_type /* IsCompareTo */) const {
+ while (s != e) {
+ const int mid = (s + e) >> 1;
+ if (comp(key(mid), k)) {
+ s = mid + 1;
+ } else {
+ e = mid;
+ }
+ }
+ return {s};
+ }
+
+ // Returns the position of the first value whose key is not less than k using
+ // binary search performed using compare-to.
+ template <typename K, typename CompareTo>
+ SearchResult<int, true> binary_search_impl(
+ const K &k, int s, int e, const CompareTo &comp,
+ std::true_type /* IsCompareTo */) const {
+ if (is_multi_container::value) {
+ MatchKind exact_match = MatchKind::kNe;
+ while (s != e) {
+ const int mid = (s + e) >> 1;
+ const absl::weak_ordering c = comp(key(mid), k);
+ if (c < 0) {
+ s = mid + 1;
+ } else {
+ e = mid;
+ if (c == 0) {
+ // Need to return the first value whose key is not less than k,
+ // which requires continuing the binary search if this is a
+ // multi-container.
+ exact_match = MatchKind::kEq;
+ }
+ }
+ }
+ return {s, exact_match};
+ } else { // Not a multi-container.
+ while (s != e) {
+ const int mid = (s + e) >> 1;
+ const absl::weak_ordering c = comp(key(mid), k);
+ if (c < 0) {
+ s = mid + 1;
+ } else if (c > 0) {
+ e = mid;
+ } else {
+ return {mid, MatchKind::kEq};
+ }
+ }
+ return {s, MatchKind::kNe};
+ }
+ }
+
+ // Emplaces a value at position i, shifting all existing values and
+ // children at positions >= i to the right by 1.
+ template <typename... Args>
+ void emplace_value(size_type i, allocator_type *alloc, Args &&... args);
+
+ // Removes the value at position i, shifting all existing values and children
+ // at positions > i to the left by 1.
+ void remove_value(int i, allocator_type *alloc);
+
+ // Removes the values at positions [i, i + to_erase), shifting all values
+ // after that range to the left by to_erase. Does not change children at all.
+ void remove_values_ignore_children(int i, int to_erase,
+ allocator_type *alloc);
+
+ // Rebalances a node with its right sibling.
+ void rebalance_right_to_left(int to_move, btree_node *right,
+ allocator_type *alloc);
+ void rebalance_left_to_right(int to_move, btree_node *right,
+ allocator_type *alloc);
+
+ // Splits a node, moving a portion of the node's values to its right sibling.
+ void split(int insert_position, btree_node *dest, allocator_type *alloc);
+
+ // Merges a node with its right sibling, moving all of the values and the
+ // delimiting key in the parent node onto itself.
+ void merge(btree_node *sibling, allocator_type *alloc);
+
+ // Swap the contents of "this" and "src".
+ void swap(btree_node *src, allocator_type *alloc);
+
+ // Node allocation/deletion routines.
+ static btree_node *init_leaf(btree_node *n, btree_node *parent,
+ int max_count) {
+ n->set_parent(parent);
+ n->set_position(0);
+ n->set_start(0);
+ n->set_finish(0);
+ n->set_max_count(max_count);
+ absl::container_internal::SanitizerPoisonMemoryRegion(
+ n->start_slot(), max_count * sizeof(slot_type));
+ return n;
+ }
+ static btree_node *init_internal(btree_node *n, btree_node *parent) {
+ init_leaf(n, parent, kNodeValues);
+ // Set `max_count` to a sentinel value to indicate that this node is
+ // internal.
+ n->set_max_count(kInternalNodeMaxCount);
+ absl::container_internal::SanitizerPoisonMemoryRegion(
+ &n->mutable_child(n->start()),
+ (kNodeValues + 1) * sizeof(btree_node *));
+ return n;
+ }
+ void destroy(allocator_type *alloc) {
+ for (int i = start(); i < finish(); ++i) {
+ value_destroy(i, alloc);
+ }
+ }
+
+ public:
+ // Exposed only for tests.
+ static bool testonly_uses_linear_node_search() {
+ return use_linear_search::value;
+ }
+
+ private:
+ template <typename... Args>
+ void value_init(const size_type i, allocator_type *alloc, Args &&... args) {
+ absl::container_internal::SanitizerUnpoisonObject(slot(i));
+ params_type::construct(alloc, slot(i), std::forward<Args>(args)...);
+ }
+ void value_destroy(const size_type i, allocator_type *alloc) {
+ params_type::destroy(alloc, slot(i));
+ absl::container_internal::SanitizerPoisonObject(slot(i));
+ }
+
+ // Move n values starting at value i in this node into the values starting at
+ // value j in node x.
+ void uninitialized_move_n(const size_type n, const size_type i,
+ const size_type j, btree_node *x,
+ allocator_type *alloc) {
+ absl::container_internal::SanitizerUnpoisonMemoryRegion(
+ x->slot(j), n * sizeof(slot_type));
+ for (slot_type *src = slot(i), *end = src + n, *dest = x->slot(j);
+ src != end; ++src, ++dest) {
+ params_type::construct(alloc, dest, src);
+ }
+ }
+
+ // Destroys a range of n values, starting at index i.
+ void value_destroy_n(const size_type i, const size_type n,
+ allocator_type *alloc) {
+ for (int j = 0; j < n; ++j) {
+ value_destroy(i + j, alloc);
+ }
+ }
+
+ template <typename P>
+ friend class btree;
+ template <typename N, typename R, typename P>
+ friend struct btree_iterator;
+ friend class BtreeNodePeer;
+};
+
+template <typename Node, typename Reference, typename Pointer>
+struct btree_iterator {
+ private:
+ using key_type = typename Node::key_type;
+ using size_type = typename Node::size_type;
+ using params_type = typename Node::params_type;
+
+ using node_type = Node;
+ using normal_node = typename std::remove_const<Node>::type;
+ using const_node = const Node;
+ using normal_pointer = typename params_type::pointer;
+ using normal_reference = typename params_type::reference;
+ using const_pointer = typename params_type::const_pointer;
+ 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>;
+ using const_iterator =
+ btree_iterator<const_node, const_reference, const_pointer>;
+
+ public:
+ // These aliases are public for std::iterator_traits.
+ using difference_type = typename Node::difference_type;
+ using value_type = typename params_type::value_type;
+ using pointer = Pointer;
+ using reference = Reference;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ btree_iterator() : node(nullptr), position(-1) {}
+ explicit btree_iterator(Node *n) : node(n), position(n->start()) {}
+ btree_iterator(Node *n, int p) : node(n), position(p) {}
+
+ // NOTE: this SFINAE allows for implicit conversions from iterator to
+ // const_iterator, but it specifically avoids defining copy constructors so
+ // that btree_iterator can be trivially copyable. This is for performance and
+ // binary size reasons.
+ template <typename N, typename R, typename P,
+ absl::enable_if_t<
+ std::is_same<btree_iterator<N, R, P>, iterator>::value &&
+ std::is_same<btree_iterator, const_iterator>::value,
+ int> = 0>
+ btree_iterator(const btree_iterator<N, R, P> &x) // NOLINT
+ : node(x.node), position(x.position) {}
+
+ private:
+ // This SFINAE allows explicit conversions from const_iterator to
+ // iterator, but also avoids defining a copy constructor.
+ // NOTE: the const_cast is safe because this constructor is only called by
+ // non-const methods and the container owns the nodes.
+ template <typename N, typename R, typename P,
+ absl::enable_if_t<
+ std::is_same<btree_iterator<N, R, P>, const_iterator>::value &&
+ std::is_same<btree_iterator, iterator>::value,
+ int> = 0>
+ explicit btree_iterator(const btree_iterator<N, R, P> &x)
+ : node(const_cast<node_type *>(x.node)), position(x.position) {}
+
+ // Increment/decrement the iterator.
+ void increment() {
+ if (node->leaf() && ++position < node->finish()) {
+ return;
+ }
+ increment_slow();
+ }
+ void increment_slow();
+
+ void decrement() {
+ if (node->leaf() && --position >= node->start()) {
+ return;
+ }
+ decrement_slow();
+ }
+ void decrement_slow();
+
+ public:
+ bool operator==(const const_iterator &x) const {
+ return node == x.node && position == x.position;
+ }
+ bool operator!=(const const_iterator &x) const {
+ return node != x.node || position != x.position;
+ }
+
+ // Accessors for the key/value the iterator is pointing at.
+ reference operator*() const { return node->value(position); }
+ pointer operator->() const { return &node->value(position); }
+
+ btree_iterator &operator++() {
+ increment();
+ return *this;
+ }
+ btree_iterator &operator--() {
+ decrement();
+ return *this;
+ }
+ btree_iterator operator++(int) {
+ btree_iterator tmp = *this;
+ ++*this;
+ return tmp;
+ }
+ btree_iterator operator--(int) {
+ btree_iterator tmp = *this;
+ --*this;
+ return tmp;
+ }
+
+ private:
+ template <typename Params>
+ friend class btree;
+ template <typename Tree>
+ friend class btree_container;
+ template <typename Tree>
+ friend class btree_set_container;
+ template <typename Tree>
+ friend class btree_map_container;
+ template <typename Tree>
+ friend class btree_multiset_container;
+ template <typename N, typename R, typename P>
+ friend struct btree_iterator;
+ template <typename TreeType, typename CheckerType>
+ friend class base_checker;
+
+ const key_type &key() const { return node->key(position); }
+ slot_type *slot() { return node->slot(position); }
+
+ // The node in the tree the iterator is pointing at.
+ Node *node;
+ // The position within the node of the tree the iterator is pointing at.
+ // TODO(ezb): make this a field_type
+ int position;
+};
+
+template <typename Params>
+class btree {
+ using node_type = btree_node<Params>;
+ using is_key_compare_to = typename Params::is_key_compare_to;
+
+ // 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 {
+ using field_type = typename node_type::field_type;
+ node_type *parent;
+ field_type position = 0;
+ field_type start = 0;
+ field_type finish = 0;
+ // max_count must be != kInternalNodeMaxCount (so that this node is regarded
+ // 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
+ constexpr EmptyNodeType(node_type *p) : parent(p) {}
+#endif
+ };
+
+ 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));
+ return const_cast<EmptyNodeType *>(&empty_node);
+#endif
+ }
+
+ enum {
+ kNodeValues = node_type::kNodeValues,
+ kMinNodeValues = kNodeValues / 2,
+ };
+
+ struct node_stats {
+ using size_type = typename Params::size_type;
+
+ node_stats(size_type l, size_type i) : leaf_nodes(l), internal_nodes(i) {}
+
+ node_stats &operator+=(const node_stats &x) {
+ leaf_nodes += x.leaf_nodes;
+ internal_nodes += x.internal_nodes;
+ return *this;
+ }
+
+ size_type leaf_nodes;
+ size_type internal_nodes;
+ };
+
+ public:
+ using key_type = typename Params::key_type;
+ using value_type = typename Params::value_type;
+ using size_type = typename Params::size_type;
+ using difference_type = typename Params::difference_type;
+ using key_compare = typename Params::key_compare;
+ using value_compare = typename Params::value_compare;
+ using allocator_type = typename Params::allocator_type;
+ using reference = typename Params::reference;
+ using const_reference = typename Params::const_reference;
+ using pointer = typename Params::pointer;
+ using const_pointer = typename Params::const_pointer;
+ using iterator = btree_iterator<node_type, reference, pointer>;
+ using const_iterator = typename iterator::const_iterator;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using node_handle_type = node_handle<Params, Params, allocator_type>;
+
+ // Internal types made public for use by btree_container types.
+ using params_type = Params;
+ using slot_type = typename Params::slot_type;
+
+ private:
+ // For use in copy_or_move_values_in_order.
+ const value_type &maybe_move_from_iterator(const_iterator x) { return *x; }
+ value_type &&maybe_move_from_iterator(iterator x) { return std::move(*x); }
+
+ // Copies or moves (depending on the template parameter) the values in
+ // x into this btree in their order in x. This btree must be empty before this
+ // method is called. This method is used in copy construction, copy
+ // assignment, and move assignment.
+ template <typename Btree>
+ void copy_or_move_values_in_order(Btree *x);
+
+ // Validates that various assumptions/requirements are true at compile time.
+ constexpr static bool static_assert_validation();
+
+ public:
+ btree(const key_compare &comp, const allocator_type &alloc);
+
+ btree(const btree &x);
+ btree(btree &&x) noexcept
+ : root_(std::move(x.root_)),
+ rightmost_(absl::exchange(x.rightmost_, EmptyNode())),
+ size_(absl::exchange(x.size_, 0)) {
+ x.mutable_root() = EmptyNode();
+ }
+
+ ~btree() {
+ // Put static_asserts in destructor to avoid triggering them before the type
+ // is complete.
+ static_assert(static_assert_validation(), "This call must be elided.");
+ clear();
+ }
+
+ // Assign the contents of x to *this.
+ btree &operator=(const btree &x);
+ btree &operator=(btree &&x) noexcept;
+
+ iterator begin() { return iterator(leftmost()); }
+ const_iterator begin() const { return const_iterator(leftmost()); }
+ iterator end() { return iterator(rightmost_, rightmost_->finish()); }
+ const_iterator end() const {
+ return const_iterator(rightmost_, rightmost_->finish());
+ }
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
+ }
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(begin());
+ }
+
+ // Finds the first element whose key is not less than key.
+ template <typename K>
+ iterator lower_bound(const K &key) {
+ return internal_end(internal_lower_bound(key));
+ }
+ template <typename K>
+ const_iterator lower_bound(const K &key) const {
+ return internal_end(internal_lower_bound(key));
+ }
+
+ // Finds the first element whose key is greater than key.
+ template <typename K>
+ iterator upper_bound(const K &key) {
+ return internal_end(internal_upper_bound(key));
+ }
+ template <typename K>
+ const_iterator upper_bound(const K &key) const {
+ return internal_end(internal_upper_bound(key));
+ }
+
+ // Finds the range of values which compare equal to key. The first member of
+ // the returned pair is equal to lower_bound(key). The second member pair of
+ // the pair is equal to upper_bound(key).
+ template <typename K>
+ std::pair<iterator, iterator> equal_range(const K &key) {
+ return {lower_bound(key), upper_bound(key)};
+ }
+ template <typename K>
+ std::pair<const_iterator, const_iterator> equal_range(const K &key) const {
+ return {lower_bound(key), upper_bound(key)};
+ }
+
+ // Inserts a value into the btree only if it does not already exist. The
+ // boolean return value indicates whether insertion succeeded or failed.
+ // Requirement: if `key` already exists in the btree, does not consume `args`.
+ // Requirement: `key` is never referenced after consuming `args`.
+ template <typename... Args>
+ std::pair<iterator, bool> insert_unique(const key_type &key, Args &&... args);
+
+ // Inserts with hint. Checks to see if the value should be placed immediately
+ // before `position` in the tree. If so, then the insertion will take
+ // amortized constant time. If not, the insertion will take amortized
+ // logarithmic time as if a call to insert_unique() were made.
+ // Requirement: if `key` already exists in the btree, does not consume `args`.
+ // Requirement: `key` is never referenced after consuming `args`.
+ template <typename... Args>
+ std::pair<iterator, bool> insert_hint_unique(iterator position,
+ const key_type &key,
+ Args &&... args);
+
+ // Insert a range of values into the btree.
+ template <typename InputIterator>
+ void insert_iterator_unique(InputIterator b, InputIterator e);
+
+ // Inserts a value into the btree.
+ template <typename ValueType>
+ iterator insert_multi(const key_type &key, ValueType &&v);
+
+ // Inserts a value into the btree.
+ template <typename ValueType>
+ iterator insert_multi(ValueType &&v) {
+ return insert_multi(params_type::key(v), std::forward<ValueType>(v));
+ }
+
+ // Insert with hint. Check to see if the value should be placed immediately
+ // before position in the tree. If it does, then the insertion will take
+ // amortized constant time. If not, the insertion will take amortized
+ // logarithmic time as if a call to insert_multi(v) were made.
+ template <typename ValueType>
+ iterator insert_hint_multi(iterator position, ValueType &&v);
+
+ // Insert a range of values into the btree.
+ template <typename InputIterator>
+ void insert_iterator_multi(InputIterator b, InputIterator e);
+
+ // Erase the specified iterator from the btree. The iterator must be valid
+ // (i.e. not equal to end()). Return an iterator pointing to the node after
+ // the one that was erased (or end() if none exists).
+ // Requirement: does not read the value at `*iter`.
+ iterator erase(iterator iter);
+
+ // Erases range. Returns the number of keys erased and an iterator pointing
+ // to the element after the last erased element.
+ std::pair<size_type, iterator> erase_range(iterator begin, iterator end);
+
+ // Erases the specified key from the btree. Returns 1 if an element was
+ // erased and 0 otherwise.
+ template <typename K>
+ size_type erase_unique(const K &key);
+
+ // Erases all of the entries matching the specified key from the
+ // btree. Returns the number of elements erased.
+ template <typename K>
+ size_type erase_multi(const K &key);
+
+ // Finds the iterator corresponding to a key or returns end() if the key is
+ // not present.
+ template <typename K>
+ iterator find(const K &key) {
+ return internal_end(internal_find(key));
+ }
+ template <typename K>
+ const_iterator find(const K &key) const {
+ return internal_end(internal_find(key));
+ }
+
+ // Returns a count of the number of times the key appears in the btree.
+ template <typename K>
+ size_type count_unique(const K &key) const {
+ const iterator begin = internal_find(key);
+ if (begin.node == nullptr) {
+ // The key doesn't exist in the tree.
+ return 0;
+ }
+ return 1;
+ }
+ // Returns a count of the number of times the key appears in the btree.
+ template <typename K>
+ size_type count_multi(const K &key) const {
+ const auto range = equal_range(key);
+ return std::distance(range.first, range.second);
+ }
+
+ // Clear the btree, deleting all of the values it contains.
+ void clear();
+
+ // Swap the contents of *this and x.
+ void swap(btree &x);
+
+ const key_compare &key_comp() const noexcept {
+ return root_.template get<0>();
+ }
+ template <typename K, typename LK>
+ bool compare_keys(const K &x, const LK &y) const {
+ return compare_internal::compare_result_as_less_than(key_comp()(x, y));
+ }
+
+ value_compare value_comp() const { return value_compare(key_comp()); }
+
+ // Verifies the structure of the btree.
+ void verify() const;
+
+ // Size routines.
+ size_type size() const { return size_; }
+ size_type max_size() const { return (std::numeric_limits<size_type>::max)(); }
+ bool empty() const { return size_ == 0; }
+
+ // The height of the btree. An empty tree will have height 0.
+ size_type height() const {
+ size_type h = 0;
+ if (!empty()) {
+ // Count the length of the chain from the leftmost node up to the
+ // root. We actually count from the root back around to the level below
+ // the root, but the calculation is the same because of the circularity
+ // of that traversal.
+ const node_type *n = root();
+ do {
+ ++h;
+ n = n->parent();
+ } while (n != root());
+ }
+ return h;
+ }
+
+ // The number of internal, leaf and total nodes used by the btree.
+ size_type leaf_nodes() const { return internal_stats(root()).leaf_nodes; }
+ size_type internal_nodes() const {
+ return internal_stats(root()).internal_nodes;
+ }
+ size_type nodes() const {
+ node_stats stats = internal_stats(root());
+ return stats.leaf_nodes + stats.internal_nodes;
+ }
+
+ // The total number of bytes used by the btree.
+ size_type bytes_used() const {
+ node_stats stats = internal_stats(root());
+ if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) {
+ return sizeof(*this) + node_type::LeafSize(root()->max_count());
+ } else {
+ return sizeof(*this) + stats.leaf_nodes * node_type::LeafSize() +
+ stats.internal_nodes * node_type::InternalSize();
+ }
+ }
+
+ // The average number of bytes used per value stored in the btree.
+ static double average_bytes_per_value() {
+ // Returns the number of bytes per value on a leaf node that is 75%
+ // full. Experimentally, this matches up nicely with the computed number of
+ // bytes per value in trees that had their values inserted in random order.
+ return node_type::LeafSize() / (kNodeValues * 0.75);
+ }
+
+ // The fullness of the btree. Computed as the number of elements in the btree
+ // divided by the maximum number of elements a tree with the current number
+ // of nodes could hold. A value of 1 indicates perfect space
+ // utilization. Smaller values indicate space wastage.
+ // Returns 0 for empty trees.
+ double fullness() const {
+ if (empty()) return 0.0;
+ return static_cast<double>(size()) / (nodes() * kNodeValues);
+ }
+ // The overhead of the btree structure in bytes per node. Computed as the
+ // total number of bytes used by the btree minus the number of bytes used for
+ // storing elements divided by the number of elements.
+ // Returns 0 for empty trees.
+ double overhead() const {
+ if (empty()) return 0.0;
+ return (bytes_used() - size() * sizeof(value_type)) /
+ static_cast<double>(size());
+ }
+
+ // The allocator used by the btree.
+ allocator_type get_allocator() const { return allocator(); }
+
+ private:
+ // Internal accessor routines.
+ node_type *root() { return root_.template get<2>(); }
+ const node_type *root() const { return root_.template get<2>(); }
+ node_type *&mutable_root() noexcept { return root_.template get<2>(); }
+ key_compare *mutable_key_comp() noexcept { return &root_.template get<0>(); }
+
+ // The leftmost node is stored as the parent of the root node.
+ node_type *leftmost() { return root()->parent(); }
+ const node_type *leftmost() const { return root()->parent(); }
+
+ // Allocator routines.
+ allocator_type *mutable_allocator() noexcept {
+ return &root_.template get<1>();
+ }
+ const allocator_type &allocator() const noexcept {
+ return root_.template get<1>();
+ }
+
+ // Allocates a correctly aligned node of at least size bytes using the
+ // allocator.
+ node_type *allocate(const size_type size) {
+ return reinterpret_cast<node_type *>(
+ absl::container_internal::Allocate<node_type::Alignment()>(
+ mutable_allocator(), size));
+ }
+
+ // Node creation/deletion routines.
+ node_type *new_internal_node(node_type *parent) {
+ node_type *p = allocate(node_type::InternalSize());
+ return node_type::init_internal(p, parent);
+ }
+ node_type *new_leaf_node(node_type *parent) {
+ node_type *p = allocate(node_type::LeafSize());
+ return node_type::init_leaf(p, parent, kNodeValues);
+ }
+ node_type *new_leaf_root_node(const int max_count) {
+ node_type *p = allocate(node_type::LeafSize(max_count));
+ return node_type::init_leaf(p, p, max_count);
+ }
+
+ // Deletion helper routines.
+ void erase_same_node(iterator begin, iterator end);
+ iterator erase_from_leaf_node(iterator begin, size_type to_erase);
+ iterator rebalance_after_delete(iterator iter);
+
+ // Deallocates a node of a certain size in bytes using the allocator.
+ void deallocate(const size_type size, node_type *node) {
+ absl::container_internal::Deallocate<node_type::Alignment()>(
+ mutable_allocator(), node, size);
+ }
+
+ void delete_internal_node(node_type *node) {
+ node->destroy(mutable_allocator());
+ deallocate(node_type::InternalSize(), node);
+ }
+ void delete_leaf_node(node_type *node) {
+ node->destroy(mutable_allocator());
+ deallocate(node_type::LeafSize(node->max_count()), node);
+ }
+
+ // Rebalances or splits the node iter points to.
+ void rebalance_or_split(iterator *iter);
+
+ // Merges the values of left, right and the delimiting key on their parent
+ // onto left, removing the delimiting key and deleting right.
+ void merge_nodes(node_type *left, node_type *right);
+
+ // Tries to merge node with its left or right sibling, and failing that,
+ // rebalance with its left or right sibling. Returns true if a merge
+ // occurred, at which point it is no longer valid to access node. Returns
+ // false if no merging took place.
+ bool try_merge_or_rebalance(iterator *iter);
+
+ // Tries to shrink the height of the tree by 1.
+ void try_shrink();
+
+ iterator internal_end(iterator iter) {
+ return iter.node != nullptr ? iter : end();
+ }
+ const_iterator internal_end(const_iterator iter) const {
+ return iter.node != nullptr ? iter : end();
+ }
+
+ // Emplaces a value into the btree immediately before iter. Requires that
+ // key(v) <= iter.key() and (--iter).key() <= key(v).
+ template <typename... Args>
+ iterator internal_emplace(iterator iter, Args &&... args);
+
+ // Returns an iterator pointing to the first value >= the value "iter" is
+ // pointing at. Note that "iter" might be pointing to an invalid location such
+ // as iter.position == iter.node->finish(). This routine simply moves iter up
+ // in the tree to a valid location.
+ // Requires: iter.node is non-null.
+ template <typename IterType>
+ static IterType internal_last(IterType iter);
+
+ // Returns an iterator pointing to the leaf position at which key would
+ // reside in the tree. We provide 2 versions of internal_locate. The first
+ // version uses a less-than comparator and is incapable of distinguishing when
+ // there is an exact match. The second version is for the key-compare-to
+ // specialization and distinguishes exact matches. The key-compare-to
+ // specialization allows the caller to avoid a subsequent comparison to
+ // determine if an exact match was made, which is important for keys with
+ // expensive comparison, such as strings.
+ template <typename K>
+ SearchResult<iterator, is_key_compare_to::value> internal_locate(
+ const K &key) const;
+
+ template <typename K>
+ SearchResult<iterator, false> internal_locate_impl(
+ const K &key, std::false_type /* IsCompareTo */) const;
+
+ template <typename K>
+ SearchResult<iterator, true> internal_locate_impl(
+ const K &key, std::true_type /* IsCompareTo */) const;
+
+ // Internal routine which implements lower_bound().
+ template <typename K>
+ iterator internal_lower_bound(const K &key) const;
+
+ // Internal routine which implements upper_bound().
+ template <typename K>
+ iterator internal_upper_bound(const K &key) const;
+
+ // Internal routine which implements find().
+ template <typename K>
+ iterator internal_find(const K &key) const;
+
+ // Deletes a node and all of its children.
+ void internal_clear(node_type *node);
+
+ // Verifies the tree structure of node.
+ int internal_verify(const node_type *node, const key_type *lo,
+ const key_type *hi) const;
+
+ node_stats internal_stats(const node_type *node) const {
+ // The root can be a static empty node.
+ if (node == nullptr || (node == root() && empty())) {
+ return node_stats(0, 0);
+ }
+ if (node->leaf()) {
+ return node_stats(1, 0);
+ }
+ node_stats res(0, 1);
+ for (int i = node->start(); i <= node->finish(); ++i) {
+ res += internal_stats(node->child(i));
+ }
+ return res;
+ }
+
+ public:
+ // Exposed only for tests.
+ static bool testonly_uses_linear_node_search() {
+ return node_type::testonly_uses_linear_node_search();
+ }
+
+ private:
+ // We use compressed tuple in order to save space because key_compare and
+ // allocator_type are usually empty.
+ absl::container_internal::CompressedTuple<key_compare, allocator_type,
+ node_type *>
+ root_;
+
+ // A pointer to the rightmost node. Note that the leftmost node is stored as
+ // the root's parent.
+ node_type *rightmost_;
+
+ // Number of values.
+ size_type size_;
+};
+
+////
+// btree_node methods
+template <typename P>
+template <typename... Args>
+inline void btree_node<P>::emplace_value(const size_type i,
+ allocator_type *alloc,
+ Args &&... args) {
+ assert(i >= start());
+ assert(i <= finish());
+ // Shift old values to create space for new value and then construct it in
+ // place.
+ if (i < finish()) {
+ value_init(finish(), alloc, slot(finish() - 1));
+ for (size_type j = finish() - 1; j > i; --j)
+ params_type::move(alloc, slot(j - 1), slot(j));
+ value_destroy(i, alloc);
+ }
+ value_init(i, alloc, std::forward<Args>(args)...);
+ set_finish(finish() + 1);
+
+ if (!leaf() && finish() > i + 1) {
+ for (int j = finish(); j > i + 1; --j) {
+ set_child(j, child(j - 1));
+ }
+ clear_child(i + 1);
+ }
+}
+
+template <typename P>
+inline void btree_node<P>::remove_value(const int i, allocator_type *alloc) {
+ if (!leaf() && finish() > i + 1) {
+ assert(child(i + 1)->count() == 0);
+ for (size_type j = i + 1; j < finish(); ++j) {
+ set_child(j, child(j + 1));
+ }
+ clear_child(finish());
+ }
+
+ remove_values_ignore_children(i, /*to_erase=*/1, alloc);
+}
+
+template <typename P>
+inline void btree_node<P>::remove_values_ignore_children(
+ const int i, const int to_erase, allocator_type *alloc) {
+ params_type::move(alloc, slot(i + to_erase), finish_slot(), slot(i));
+ value_destroy_n(finish() - to_erase, to_erase, alloc);
+ set_finish(finish() - to_erase);
+}
+
+template <typename P>
+void btree_node<P>::rebalance_right_to_left(const int to_move,
+ btree_node *right,
+ allocator_type *alloc) {
+ assert(parent() == right->parent());
+ assert(position() + 1 == right->position());
+ assert(right->count() >= count());
+ assert(to_move >= 1);
+ assert(to_move <= right->count());
+
+ // 1) Move the delimiting value in the parent to the left node.
+ value_init(finish(), alloc, parent()->slot(position()));
+
+ // 2) Move the (to_move - 1) values from the right node to the left node.
+ right->uninitialized_move_n(to_move - 1, right->start(), finish() + 1, this,
+ alloc);
+
+ // 3) Move the new delimiting value to the parent from the right node.
+ params_type::move(alloc, right->slot(to_move - 1),
+ parent()->slot(position()));
+
+ // 4) Shift the values in the right node to their correct position.
+ params_type::move(alloc, right->slot(to_move), right->finish_slot(),
+ right->start_slot());
+
+ // 5) Destroy the now-empty to_move entries in the right node.
+ right->value_destroy_n(right->finish() - to_move, to_move, alloc);
+
+ if (!leaf()) {
+ // Move the child pointers from the right to the left node.
+ for (int i = 0; i < to_move; ++i) {
+ init_child(finish() + i + 1, right->child(i));
+ }
+ for (int i = right->start(); i <= right->finish() - to_move; ++i) {
+ assert(i + to_move <= right->max_count());
+ right->init_child(i, right->child(i + to_move));
+ right->clear_child(i + to_move);
+ }
+ }
+
+ // Fixup `finish` on the left and right nodes.
+ set_finish(finish() + to_move);
+ right->set_finish(right->finish() - to_move);
+}
+
+template <typename P>
+void btree_node<P>::rebalance_left_to_right(const int to_move,
+ btree_node *right,
+ allocator_type *alloc) {
+ assert(parent() == right->parent());
+ assert(position() + 1 == right->position());
+ assert(count() >= right->count());
+ assert(to_move >= 1);
+ assert(to_move <= count());
+
+ // Values in the right node are shifted to the right to make room for the
+ // new to_move values. Then, the delimiting value in the parent and the
+ // other (to_move - 1) values in the left node are moved into the right node.
+ // Lastly, a new delimiting value is moved from the left node into the
+ // parent, and the remaining empty left node entries are destroyed.
+
+ if (right->count() >= to_move) {
+ // The original location of the right->count() values are sufficient to hold
+ // the new to_move entries from the parent and left node.
+
+ // 1) Shift existing values in the right node to their correct positions.
+ right->uninitialized_move_n(to_move, right->finish() - to_move,
+ right->finish(), right, alloc);
+ for (slot_type *src = right->slot(right->finish() - to_move - 1),
+ *dest = right->slot(right->finish() - 1),
+ *end = right->start_slot();
+ src >= end; --src, --dest) {
+ params_type::move(alloc, src, dest);
+ }
+
+ // 2) Move the delimiting value in the parent to the right node.
+ params_type::move(alloc, parent()->slot(position()),
+ right->slot(to_move - 1));
+
+ // 3) Move the (to_move - 1) values from the left node to the right node.
+ params_type::move(alloc, slot(finish() - (to_move - 1)), finish_slot(),
+ right->start_slot());
+ } else {
+ // The right node does not have enough initialized space to hold the new
+ // to_move entries, so part of them will move to uninitialized space.
+
+ // 1) Shift existing values in the right node to their correct positions.
+ right->uninitialized_move_n(right->count(), right->start(),
+ right->start() + to_move, right, alloc);
+
+ // 2) Move the delimiting value in the parent to the right node.
+ right->value_init(to_move - 1, alloc, parent()->slot(position()));
+
+ // 3) Move the (to_move - 1) values from the left node to the right node.
+ const size_type uninitialized_remaining = to_move - right->count() - 1;
+ uninitialized_move_n(uninitialized_remaining,
+ finish() - uninitialized_remaining, right->finish(),
+ right, alloc);
+ params_type::move(alloc, slot(finish() - (to_move - 1)),
+ slot(finish() - uninitialized_remaining),
+ right->start_slot());
+ }
+
+ // 4) Move the new delimiting value to the parent from the left node.
+ params_type::move(alloc, slot(finish() - to_move),
+ parent()->slot(position()));
+
+ // 5) Destroy the now-empty to_move entries in the left node.
+ value_destroy_n(finish() - to_move, to_move, alloc);
+
+ if (!leaf()) {
+ // Move the child pointers from the left to the right node.
+ for (int i = right->finish(); i >= right->start(); --i) {
+ right->init_child(i + to_move, right->child(i));
+ right->clear_child(i);
+ }
+ for (int i = 1; i <= to_move; ++i) {
+ right->init_child(i - 1, child(finish() - to_move + i));
+ clear_child(finish() - to_move + i);
+ }
+ }
+
+ // Fixup the counts on the left and right nodes.
+ set_finish(finish() - to_move);
+ right->set_finish(right->finish() + to_move);
+}
+
+template <typename P>
+void btree_node<P>::split(const int insert_position, btree_node *dest,
+ allocator_type *alloc) {
+ assert(dest->count() == 0);
+ assert(max_count() == kNodeValues);
+
+ // We bias the split based on the position being inserted. If we're
+ // inserting at the beginning of the left node then bias the split to put
+ // more values on the right node. If we're inserting at the end of the
+ // right node then bias the split to put more values on the left node.
+ if (insert_position == start()) {
+ dest->set_finish(dest->start() + finish() - 1);
+ } else if (insert_position == kNodeValues) {
+ dest->set_finish(dest->start());
+ } else {
+ dest->set_finish(dest->start() + count() / 2);
+ }
+ set_finish(finish() - dest->count());
+ assert(count() >= 1);
+
+ // Move values from the left sibling to the right sibling.
+ uninitialized_move_n(dest->count(), finish(), dest->start(), dest, alloc);
+
+ // Destroy the now-empty entries in the left node.
+ value_destroy_n(finish(), dest->count(), alloc);
+
+ // The split key is the largest value in the left sibling.
+ --mutable_finish();
+ parent()->emplace_value(position(), alloc, finish_slot());
+ value_destroy(finish(), alloc);
+ parent()->init_child(position() + 1, dest);
+
+ if (!leaf()) {
+ for (int i = dest->start(), j = finish() + 1; i <= dest->finish();
+ ++i, ++j) {
+ assert(child(j) != nullptr);
+ dest->init_child(i, child(j));
+ clear_child(j);
+ }
+ }
+}
+
+template <typename P>
+void btree_node<P>::merge(btree_node *src, allocator_type *alloc) {
+ assert(parent() == src->parent());
+ assert(position() + 1 == src->position());
+
+ // Move the delimiting value to the left node.
+ value_init(finish(), alloc, parent()->slot(position()));
+
+ // Move the values from the right to the left node.
+ src->uninitialized_move_n(src->count(), src->start(), finish() + 1, this,
+ alloc);
+
+ // Destroy the now-empty entries in the right node.
+ src->value_destroy_n(src->start(), src->count(), alloc);
+
+ if (!leaf()) {
+ // Move the child pointers from the right to the left node.
+ for (int i = src->start(), j = finish() + 1; i <= src->finish(); ++i, ++j) {
+ init_child(j, src->child(i));
+ src->clear_child(i);
+ }
+ }
+
+ // Fixup `finish` on the src and dest nodes.
+ set_finish(start() + 1 + count() + src->count());
+ src->set_finish(src->start());
+
+ // Remove the value on the parent node.
+ parent()->remove_value(position(), alloc);
+}
+
+template <typename P>
+void btree_node<P>::swap(btree_node *x, allocator_type *alloc) {
+ using std::swap;
+ assert(leaf() == x->leaf());
+
+ // Determine which is the smaller/larger node.
+ btree_node *smaller = this, *larger = x;
+ if (smaller->count() > larger->count()) {
+ swap(smaller, larger);
+ }
+
+ // Swap the values.
+ for (slot_type *a = smaller->start_slot(), *b = larger->start_slot(),
+ *end = smaller->finish_slot();
+ a != end; ++a, ++b) {
+ params_type::swap(alloc, a, b);
+ }
+
+ // Move values that can't be swapped.
+ const size_type to_move = larger->count() - smaller->count();
+ larger->uninitialized_move_n(to_move, smaller->finish(), smaller->finish(),
+ smaller, alloc);
+ larger->value_destroy_n(smaller->finish(), to_move, alloc);
+
+ if (!leaf()) {
+ // Swap the child pointers.
+ std::swap_ranges(&smaller->mutable_child(smaller->start()),
+ &smaller->mutable_child(smaller->finish() + 1),
+ &larger->mutable_child(larger->start()));
+ // Update swapped children's parent pointers.
+ int i = smaller->start();
+ int j = larger->start();
+ for (; i <= smaller->finish(); ++i, ++j) {
+ smaller->child(i)->set_parent(smaller);
+ larger->child(j)->set_parent(larger);
+ }
+ // Move the child pointers that couldn't be swapped.
+ for (; j <= larger->finish(); ++i, ++j) {
+ smaller->init_child(i, larger->child(j));
+ larger->clear_child(j);
+ }
+ }
+
+ // Swap the `finish`s.
+ // TODO(ezb): with floating storage, will also need to swap starts.
+ swap(mutable_finish(), x->mutable_finish());
+}
+
+////
+// btree_iterator methods
+template <typename N, typename R, typename P>
+void btree_iterator<N, R, P>::increment_slow() {
+ if (node->leaf()) {
+ assert(position >= node->finish());
+ btree_iterator save(*this);
+ while (position == node->finish() && !node->is_root()) {
+ assert(node->parent()->child(node->position()) == node);
+ position = node->position();
+ node = node->parent();
+ }
+ if (position == node->finish()) {
+ *this = save;
+ }
+ } else {
+ assert(position < node->finish());
+ node = node->child(position + 1);
+ while (!node->leaf()) {
+ node = node->start_child();
+ }
+ position = node->start();
+ }
+}
+
+template <typename N, typename R, typename P>
+void btree_iterator<N, R, P>::decrement_slow() {
+ if (node->leaf()) {
+ assert(position <= -1);
+ btree_iterator save(*this);
+ while (position < node->start() && !node->is_root()) {
+ assert(node->parent()->child(node->position()) == node);
+ position = node->position() - 1;
+ node = node->parent();
+ }
+ if (position < node->start()) {
+ *this = save;
+ }
+ } else {
+ assert(position >= node->start());
+ node = node->child(position);
+ while (!node->leaf()) {
+ node = node->child(node->finish());
+ }
+ position = node->finish() - 1;
+ }
+}
+
+////
+// btree methods
+template <typename P>
+template <typename Btree>
+void btree<P>::copy_or_move_values_in_order(Btree *x) {
+ static_assert(std::is_same<btree, Btree>::value ||
+ std::is_same<const btree, Btree>::value,
+ "Btree type must be same or const.");
+ assert(empty());
+
+ // We can avoid key comparisons because we know the order of the
+ // values is the same order we'll store them in.
+ auto iter = x->begin();
+ if (iter == x->end()) return;
+ insert_multi(maybe_move_from_iterator(iter));
+ ++iter;
+ for (; iter != x->end(); ++iter) {
+ // If the btree is not empty, we can just insert the new value at the end
+ // of the tree.
+ internal_emplace(end(), maybe_move_from_iterator(iter));
+ }
+}
+
+template <typename P>
+constexpr bool btree<P>::static_assert_validation() {
+ static_assert(std::is_nothrow_copy_constructible<key_compare>::value,
+ "Key comparison must be nothrow copy constructible");
+ static_assert(std::is_nothrow_copy_constructible<allocator_type>::value,
+ "Allocator must be nothrow copy constructible");
+ static_assert(type_traits_internal::is_trivially_copyable<iterator>::value,
+ "iterator not trivially copyable.");
+
+ // Note: We assert that kTargetValues, which is computed from
+ // Params::kTargetNodeSize, must fit the node_type::field_type.
+ static_assert(
+ kNodeValues < (1 << (8 * sizeof(typename node_type::field_type))),
+ "target node size too large");
+
+ // Verify that key_compare returns an absl::{weak,strong}_ordering or bool.
+ using compare_result_type =
+ absl::result_of_t<key_compare(key_type, key_type)>;
+ static_assert(
+ std::is_same<compare_result_type, bool>::value ||
+ std::is_convertible<compare_result_type, absl::weak_ordering>::value,
+ "key comparison function must return absl::{weak,strong}_ordering or "
+ "bool.");
+
+ // Test the assumption made in setting kNodeValueSpace.
+ static_assert(node_type::MinimumOverhead() >= sizeof(void *) + 4,
+ "node space assumption incorrect");
+
+ return true;
+}
+
+template <typename P>
+btree<P>::btree(const key_compare &comp, const allocator_type &alloc)
+ : root_(comp, alloc, EmptyNode()), rightmost_(EmptyNode()), size_(0) {}
+
+template <typename P>
+btree<P>::btree(const btree &x) : btree(x.key_comp(), x.allocator()) {
+ copy_or_move_values_in_order(&x);
+}
+
+template <typename P>
+template <typename... Args>
+auto btree<P>::insert_unique(const key_type &key, Args &&... args)
+ -> std::pair<iterator, bool> {
+ if (empty()) {
+ mutable_root() = rightmost_ = new_leaf_root_node(1);
+ }
+
+ auto res = internal_locate(key);
+ iterator &iter = res.value;
+
+ if (res.HasMatch()) {
+ if (res.IsEq()) {
+ // The key already exists in the tree, do nothing.
+ return {iter, false};
+ }
+ } else {
+ iterator last = internal_last(iter);
+ if (last.node && !compare_keys(key, last.key())) {
+ // The key already exists in the tree, do nothing.
+ return {last, false};
+ }
+ }
+ return {internal_emplace(iter, std::forward<Args>(args)...), true};
+}
+
+template <typename P>
+template <typename... Args>
+inline auto btree<P>::insert_hint_unique(iterator position, const key_type &key,
+ Args &&... args)
+ -> std::pair<iterator, bool> {
+ if (!empty()) {
+ if (position == end() || compare_keys(key, position.key())) {
+ if (position == begin() || compare_keys(std::prev(position).key(), key)) {
+ // prev.key() < key < position.key()
+ return {internal_emplace(position, std::forward<Args>(args)...), true};
+ }
+ } else if (compare_keys(position.key(), key)) {
+ ++position;
+ if (position == end() || compare_keys(key, position.key())) {
+ // {original `position`}.key() < key < {current `position`}.key()
+ return {internal_emplace(position, std::forward<Args>(args)...), true};
+ }
+ } else {
+ // position.key() == key
+ return {position, false};
+ }
+ }
+ return insert_unique(key, std::forward<Args>(args)...);
+}
+
+template <typename P>
+template <typename InputIterator>
+void btree<P>::insert_iterator_unique(InputIterator b, InputIterator e) {
+ for (; b != e; ++b) {
+ insert_hint_unique(end(), params_type::key(*b), *b);
+ }
+}
+
+template <typename P>
+template <typename ValueType>
+auto btree<P>::insert_multi(const key_type &key, ValueType &&v) -> iterator {
+ if (empty()) {
+ mutable_root() = rightmost_ = new_leaf_root_node(1);
+ }
+
+ iterator iter = internal_upper_bound(key);
+ if (iter.node == nullptr) {
+ iter = end();
+ }
+ return internal_emplace(iter, std::forward<ValueType>(v));
+}
+
+template <typename P>
+template <typename ValueType>
+auto btree<P>::insert_hint_multi(iterator position, ValueType &&v) -> iterator {
+ if (!empty()) {
+ const key_type &key = params_type::key(v);
+ if (position == end() || !compare_keys(position.key(), key)) {
+ if (position == begin() ||
+ !compare_keys(key, std::prev(position).key())) {
+ // prev.key() <= key <= position.key()
+ return internal_emplace(position, std::forward<ValueType>(v));
+ }
+ } else {
+ ++position;
+ if (position == end() || !compare_keys(position.key(), key)) {
+ // {original `position`}.key() < key < {current `position`}.key()
+ return internal_emplace(position, std::forward<ValueType>(v));
+ }
+ }
+ }
+ return insert_multi(std::forward<ValueType>(v));
+}
+
+template <typename P>
+template <typename InputIterator>
+void btree<P>::insert_iterator_multi(InputIterator b, InputIterator e) {
+ for (; b != e; ++b) {
+ insert_hint_multi(end(), *b);
+ }
+}
+
+template <typename P>
+auto btree<P>::operator=(const btree &x) -> btree & {
+ if (this != &x) {
+ clear();
+
+ *mutable_key_comp() = x.key_comp();
+ if (absl::allocator_traits<
+ allocator_type>::propagate_on_container_copy_assignment::value) {
+ *mutable_allocator() = x.allocator();
+ }
+
+ copy_or_move_values_in_order(&x);
+ }
+ return *this;
+}
+
+template <typename P>
+auto btree<P>::operator=(btree &&x) noexcept -> btree & {
+ if (this != &x) {
+ clear();
+
+ using std::swap;
+ if (absl::allocator_traits<
+ allocator_type>::propagate_on_container_copy_assignment::value) {
+ // Note: `root_` also contains the allocator and the key comparator.
+ swap(root_, x.root_);
+ swap(rightmost_, x.rightmost_);
+ swap(size_, x.size_);
+ } else {
+ if (allocator() == x.allocator()) {
+ swap(mutable_root(), x.mutable_root());
+ swap(*mutable_key_comp(), *x.mutable_key_comp());
+ swap(rightmost_, x.rightmost_);
+ swap(size_, x.size_);
+ } else {
+ // We aren't allowed to propagate the allocator and the allocator is
+ // different so we can't take over its memory. We must move each element
+ // individually. We need both `x` and `this` to have `x`s key comparator
+ // while moving the values so we can't swap the key comparators.
+ *mutable_key_comp() = x.key_comp();
+ copy_or_move_values_in_order(&x);
+ }
+ }
+ }
+ return *this;
+}
+
+template <typename P>
+auto btree<P>::erase(iterator iter) -> iterator {
+ bool internal_delete = false;
+ if (!iter.node->leaf()) {
+ // Deletion of a value on an internal node. First, move the largest value
+ // from our left child here, then delete that position (in remove_value()
+ // below). We can get to the largest value from our left child by
+ // decrementing iter.
+ iterator internal_iter(iter);
+ --iter;
+ assert(iter.node->leaf());
+ params_type::move(mutable_allocator(), iter.node->slot(iter.position),
+ internal_iter.node->slot(internal_iter.position));
+ internal_delete = true;
+ }
+
+ // Delete the key from the leaf.
+ iter.node->remove_value(iter.position, mutable_allocator());
+ --size_;
+
+ // We want to return the next value after the one we just erased. If we
+ // erased from an internal node (internal_delete == true), then the next
+ // value is ++(++iter). If we erased from a leaf node (internal_delete ==
+ // false) then the next value is ++iter. Note that ++iter may point to an
+ // internal node and the value in the internal node may move to a leaf node
+ // (iter.node) when rebalancing is performed at the leaf level.
+
+ iterator res = rebalance_after_delete(iter);
+
+ // If we erased from an internal node, advance the iterator.
+ if (internal_delete) {
+ ++res;
+ }
+ return res;
+}
+
+template <typename P>
+auto btree<P>::rebalance_after_delete(iterator iter) -> iterator {
+ // Merge/rebalance as we walk back up the tree.
+ iterator res(iter);
+ bool first_iteration = true;
+ for (;;) {
+ if (iter.node == root()) {
+ try_shrink();
+ if (empty()) {
+ return end();
+ }
+ break;
+ }
+ if (iter.node->count() >= kMinNodeValues) {
+ break;
+ }
+ bool merged = try_merge_or_rebalance(&iter);
+ // On the first iteration, we should update `res` with `iter` because `res`
+ // may have been invalidated.
+ if (first_iteration) {
+ res = iter;
+ first_iteration = false;
+ }
+ if (!merged) {
+ break;
+ }
+ iter.position = iter.node->position();
+ iter.node = iter.node->parent();
+ }
+
+ // Adjust our return value. If we're pointing at the end of a node, advance
+ // the iterator.
+ if (res.position == res.node->finish()) {
+ res.position = res.node->finish() - 1;
+ ++res;
+ }
+
+ return res;
+}
+
+template <typename P>
+auto btree<P>::erase_range(iterator begin, iterator end)
+ -> std::pair<size_type, iterator> {
+ difference_type count = std::distance(begin, end);
+ assert(count >= 0);
+
+ if (count == 0) {
+ return {0, begin};
+ }
+
+ if (count == size_) {
+ clear();
+ return {count, this->end()};
+ }
+
+ if (begin.node == end.node) {
+ erase_same_node(begin, end);
+ size_ -= count;
+ return {count, rebalance_after_delete(begin)};
+ }
+
+ const size_type target_size = size_ - count;
+ while (size_ > target_size) {
+ if (begin.node->leaf()) {
+ const size_type remaining_to_erase = size_ - target_size;
+ const size_type remaining_in_node = begin.node->finish() - begin.position;
+ begin = erase_from_leaf_node(
+ begin, (std::min)(remaining_to_erase, remaining_in_node));
+ } else {
+ begin = erase(begin);
+ }
+ }
+ return {count, begin};
+}
+
+template <typename P>
+void btree<P>::erase_same_node(iterator begin, iterator end) {
+ assert(begin.node == end.node);
+ assert(end.position > begin.position);
+
+ node_type *node = begin.node;
+ size_type to_erase = end.position - begin.position;
+ if (!node->leaf()) {
+ // Delete all children between begin and end.
+ for (size_type i = 0; i < to_erase; ++i) {
+ internal_clear(node->child(begin.position + i + 1));
+ }
+ // Rotate children after end into new positions.
+ for (size_type i = begin.position + to_erase + 1; i <= node->finish();
+ ++i) {
+ node->set_child(i - to_erase, node->child(i));
+ node->clear_child(i);
+ }
+ }
+ node->remove_values_ignore_children(begin.position, to_erase,
+ mutable_allocator());
+
+ // Do not need to update rightmost_, because
+ // * either end == this->end(), and therefore node == rightmost_, and still
+ // exists
+ // * or end != this->end(), and therefore rightmost_ hasn't been erased, since
+ // it wasn't covered in [begin, end)
+}
+
+template <typename P>
+auto btree<P>::erase_from_leaf_node(iterator begin, size_type to_erase)
+ -> iterator {
+ node_type *node = begin.node;
+ assert(node->leaf());
+ assert(node->finish() > begin.position);
+ assert(begin.position + to_erase <= node->finish());
+
+ node->remove_values_ignore_children(begin.position, to_erase,
+ mutable_allocator());
+
+ size_ -= to_erase;
+
+ return rebalance_after_delete(begin);
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::erase_unique(const K &key) -> size_type {
+ const iterator iter = internal_find(key);
+ if (iter.node == nullptr) {
+ // The key doesn't exist in the tree, return nothing done.
+ return 0;
+ }
+ erase(iter);
+ return 1;
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::erase_multi(const K &key) -> size_type {
+ const iterator begin = internal_lower_bound(key);
+ if (begin.node == nullptr) {
+ // The key doesn't exist in the tree, return nothing done.
+ return 0;
+ }
+ // Delete all of the keys between begin and upper_bound(key).
+ const iterator end = internal_end(internal_upper_bound(key));
+ return erase_range(begin, end).first;
+}
+
+template <typename P>
+void btree<P>::clear() {
+ if (!empty()) {
+ internal_clear(root());
+ }
+ mutable_root() = EmptyNode();
+ rightmost_ = EmptyNode();
+ size_ = 0;
+}
+
+template <typename P>
+void btree<P>::swap(btree &x) {
+ using std::swap;
+ if (absl::allocator_traits<
+ allocator_type>::propagate_on_container_swap::value) {
+ // Note: `root_` also contains the allocator and the key comparator.
+ swap(root_, x.root_);
+ } else {
+ // It's undefined behavior if the allocators are unequal here.
+ assert(allocator() == x.allocator());
+ swap(mutable_root(), x.mutable_root());
+ swap(*mutable_key_comp(), *x.mutable_key_comp());
+ }
+ swap(rightmost_, x.rightmost_);
+ swap(size_, x.size_);
+}
+
+template <typename P>
+void btree<P>::verify() const {
+ assert(root() != nullptr);
+ assert(leftmost() != nullptr);
+ assert(rightmost_ != nullptr);
+ assert(empty() || size() == internal_verify(root(), nullptr, nullptr));
+ assert(leftmost() == (++const_iterator(root(), -1)).node);
+ assert(rightmost_ == (--const_iterator(root(), root()->finish())).node);
+ assert(leftmost()->leaf());
+ assert(rightmost_->leaf());
+}
+
+template <typename P>
+void btree<P>::rebalance_or_split(iterator *iter) {
+ node_type *&node = iter->node;
+ int &insert_position = iter->position;
+ assert(node->count() == node->max_count());
+ assert(kNodeValues == node->max_count());
+
+ // First try to make room on the node by rebalancing.
+ node_type *parent = node->parent();
+ if (node != root()) {
+ if (node->position() > parent->start()) {
+ // Try rebalancing with our left sibling.
+ node_type *left = parent->child(node->position() - 1);
+ assert(left->max_count() == kNodeValues);
+ if (left->count() < kNodeValues) {
+ // We bias rebalancing based on the position being inserted. If we're
+ // inserting at the end of the right node then we bias rebalancing to
+ // fill up the left node.
+ int to_move = (kNodeValues - left->count()) /
+ (1 + (insert_position < kNodeValues));
+ to_move = (std::max)(1, to_move);
+
+ if (insert_position - to_move >= node->start() ||
+ left->count() + to_move < kNodeValues) {
+ left->rebalance_right_to_left(to_move, node, mutable_allocator());
+
+ assert(node->max_count() - node->count() == to_move);
+ insert_position = insert_position - to_move;
+ if (insert_position < node->start()) {
+ insert_position = insert_position + left->count() + 1;
+ node = left;
+ }
+
+ assert(node->count() < node->max_count());
+ return;
+ }
+ }
+ }
+
+ if (node->position() < parent->finish()) {
+ // Try rebalancing with our right sibling.
+ node_type *right = parent->child(node->position() + 1);
+ assert(right->max_count() == kNodeValues);
+ if (right->count() < kNodeValues) {
+ // We bias rebalancing based on the position being inserted. If we're
+ // inserting at the beginning of the left node then we bias rebalancing
+ // to fill up the right node.
+ int to_move = (kNodeValues - right->count()) /
+ (1 + (insert_position > node->start()));
+ to_move = (std::max)(1, to_move);
+
+ if (insert_position <= node->finish() - to_move ||
+ right->count() + to_move < kNodeValues) {
+ node->rebalance_left_to_right(to_move, right, mutable_allocator());
+
+ if (insert_position > node->finish()) {
+ insert_position = insert_position - node->count() - 1;
+ node = right;
+ }
+
+ assert(node->count() < node->max_count());
+ return;
+ }
+ }
+ }
+
+ // Rebalancing failed, make sure there is room on the parent node for a new
+ // value.
+ assert(parent->max_count() == kNodeValues);
+ if (parent->count() == kNodeValues) {
+ iterator parent_iter(node->parent(), node->position());
+ rebalance_or_split(&parent_iter);
+ }
+ } else {
+ // Rebalancing not possible because this is the root node.
+ // Create a new root node and set the current root node as the child of the
+ // new root.
+ parent = new_internal_node(parent);
+ parent->init_child(parent->start(), root());
+ mutable_root() = parent;
+ // If the former root was a leaf node, then it's now the rightmost node.
+ assert(!parent->start_child()->leaf() ||
+ parent->start_child() == rightmost_);
+ }
+
+ // Split the node.
+ node_type *split_node;
+ if (node->leaf()) {
+ split_node = new_leaf_node(parent);
+ node->split(insert_position, split_node, mutable_allocator());
+ if (rightmost_ == node) rightmost_ = split_node;
+ } else {
+ split_node = new_internal_node(parent);
+ node->split(insert_position, split_node, mutable_allocator());
+ }
+
+ if (insert_position > node->finish()) {
+ insert_position = insert_position - node->count() - 1;
+ node = split_node;
+ }
+}
+
+template <typename P>
+void btree<P>::merge_nodes(node_type *left, node_type *right) {
+ left->merge(right, mutable_allocator());
+ if (right->leaf()) {
+ if (rightmost_ == right) rightmost_ = left;
+ delete_leaf_node(right);
+ } else {
+ delete_internal_node(right);
+ }
+}
+
+template <typename P>
+bool btree<P>::try_merge_or_rebalance(iterator *iter) {
+ node_type *parent = iter->node->parent();
+ if (iter->node->position() > parent->start()) {
+ // Try merging with our left sibling.
+ node_type *left = parent->child(iter->node->position() - 1);
+ assert(left->max_count() == kNodeValues);
+ if (1 + left->count() + iter->node->count() <= kNodeValues) {
+ iter->position += 1 + left->count();
+ merge_nodes(left, iter->node);
+ iter->node = left;
+ return true;
+ }
+ }
+ if (iter->node->position() < parent->finish()) {
+ // Try merging with our right sibling.
+ node_type *right = parent->child(iter->node->position() + 1);
+ assert(right->max_count() == kNodeValues);
+ if (1 + iter->node->count() + right->count() <= kNodeValues) {
+ merge_nodes(iter->node, right);
+ return true;
+ }
+ // Try rebalancing with our right sibling. We don't perform rebalancing if
+ // we deleted the first element from iter->node and the node is not
+ // empty. This is a small optimization for the common pattern of deleting
+ // from the front of the tree.
+ if (right->count() > kMinNodeValues &&
+ (iter->node->count() == 0 || iter->position > iter->node->start())) {
+ int to_move = (right->count() - iter->node->count()) / 2;
+ to_move = (std::min)(to_move, right->count() - 1);
+ iter->node->rebalance_right_to_left(to_move, right, mutable_allocator());
+ return false;
+ }
+ }
+ if (iter->node->position() > parent->start()) {
+ // Try rebalancing with our left sibling. We don't perform rebalancing if
+ // we deleted the last element from iter->node and the node is not
+ // empty. This is a small optimization for the common pattern of deleting
+ // from the back of the tree.
+ node_type *left = parent->child(iter->node->position() - 1);
+ if (left->count() > kMinNodeValues &&
+ (iter->node->count() == 0 || iter->position < iter->node->finish())) {
+ int to_move = (left->count() - iter->node->count()) / 2;
+ to_move = (std::min)(to_move, left->count() - 1);
+ left->rebalance_left_to_right(to_move, iter->node, mutable_allocator());
+ iter->position += to_move;
+ return false;
+ }
+ }
+ return false;
+}
+
+template <typename P>
+void btree<P>::try_shrink() {
+ if (root()->count() > 0) {
+ return;
+ }
+ // Deleted the last item on the root node, shrink the height of the tree.
+ if (root()->leaf()) {
+ assert(size() == 0);
+ delete_leaf_node(root());
+ mutable_root() = EmptyNode();
+ rightmost_ = EmptyNode();
+ } else {
+ node_type *child = root()->start_child();
+ child->make_root();
+ delete_internal_node(root());
+ mutable_root() = child;
+ }
+}
+
+template <typename P>
+template <typename IterType>
+inline IterType btree<P>::internal_last(IterType iter) {
+ assert(iter.node != nullptr);
+ while (iter.position == iter.node->finish()) {
+ iter.position = iter.node->position();
+ iter.node = iter.node->parent();
+ if (iter.node->leaf()) {
+ iter.node = nullptr;
+ break;
+ }
+ }
+ return iter;
+}
+
+template <typename P>
+template <typename... Args>
+inline auto btree<P>::internal_emplace(iterator iter, Args &&... args)
+ -> iterator {
+ if (!iter.node->leaf()) {
+ // We can't insert on an internal node. Instead, we'll insert after the
+ // previous value which is guaranteed to be on a leaf node.
+ --iter;
+ ++iter.position;
+ }
+ const int max_count = iter.node->max_count();
+ if (iter.node->count() == max_count) {
+ // Make room in the leaf for the new item.
+ if (max_count < kNodeValues) {
+ // Insertion into the root where the root is smaller than the full node
+ // size. Simply grow the size of the root node.
+ assert(iter.node == root());
+ iter.node =
+ new_leaf_root_node((std::min<int>)(kNodeValues, 2 * max_count));
+ iter.node->swap(root(), mutable_allocator());
+ delete_leaf_node(root());
+ mutable_root() = iter.node;
+ rightmost_ = iter.node;
+ } else {
+ rebalance_or_split(&iter);
+ }
+ }
+ iter.node->emplace_value(iter.position, mutable_allocator(),
+ std::forward<Args>(args)...);
+ ++size_;
+ return iter;
+}
+
+template <typename P>
+template <typename K>
+inline auto btree<P>::internal_locate(const K &key) const
+ -> SearchResult<iterator, is_key_compare_to::value> {
+ return internal_locate_impl(key, is_key_compare_to());
+}
+
+template <typename P>
+template <typename K>
+inline auto btree<P>::internal_locate_impl(
+ const K &key, std::false_type /* IsCompareTo */) const
+ -> SearchResult<iterator, false> {
+ iterator iter(const_cast<node_type *>(root()));
+ for (;;) {
+ iter.position = iter.node->lower_bound(key, key_comp()).value;
+ // NOTE: we don't need to walk all the way down the tree if the keys are
+ // equal, but determining equality would require doing an extra comparison
+ // on each node on the way down, and we will need to go all the way to the
+ // leaf node in the expected case.
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return {iter};
+}
+
+template <typename P>
+template <typename K>
+inline auto btree<P>::internal_locate_impl(
+ const K &key, std::true_type /* IsCompareTo */) const
+ -> SearchResult<iterator, true> {
+ iterator iter(const_cast<node_type *>(root()));
+ for (;;) {
+ SearchResult<int, true> res = iter.node->lower_bound(key, key_comp());
+ iter.position = res.value;
+ if (res.match == MatchKind::kEq) {
+ return {iter, MatchKind::kEq};
+ }
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return {iter, MatchKind::kNe};
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::internal_lower_bound(const K &key) const -> iterator {
+ iterator iter(const_cast<node_type *>(root()));
+ for (;;) {
+ iter.position = iter.node->lower_bound(key, key_comp()).value;
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return internal_last(iter);
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::internal_upper_bound(const K &key) const -> iterator {
+ iterator iter(const_cast<node_type *>(root()));
+ for (;;) {
+ iter.position = iter.node->upper_bound(key, key_comp());
+ if (iter.node->leaf()) {
+ break;
+ }
+ iter.node = iter.node->child(iter.position);
+ }
+ return internal_last(iter);
+}
+
+template <typename P>
+template <typename K>
+auto btree<P>::internal_find(const K &key) const -> iterator {
+ auto res = internal_locate(key);
+ if (res.HasMatch()) {
+ if (res.IsEq()) {
+ return res.value;
+ }
+ } else {
+ const iterator iter = internal_last(res.value);
+ if (iter.node != nullptr && !compare_keys(key, iter.key())) {
+ return iter;
+ }
+ }
+ return {nullptr, 0};
+}
+
+template <typename P>
+void btree<P>::internal_clear(node_type *node) {
+ if (!node->leaf()) {
+ for (int i = node->start(); i <= node->finish(); ++i) {
+ internal_clear(node->child(i));
+ }
+ delete_internal_node(node);
+ } else {
+ delete_leaf_node(node);
+ }
+}
+
+template <typename P>
+int btree<P>::internal_verify(const node_type *node, const key_type *lo,
+ const key_type *hi) const {
+ assert(node->count() > 0);
+ assert(node->count() <= node->max_count());
+ if (lo) {
+ assert(!compare_keys(node->key(node->start()), *lo));
+ }
+ if (hi) {
+ assert(!compare_keys(*hi, node->key(node->finish() - 1)));
+ }
+ for (int i = node->start() + 1; i < node->finish(); ++i) {
+ assert(!compare_keys(node->key(i), node->key(i - 1)));
+ }
+ int count = node->count();
+ if (!node->leaf()) {
+ for (int i = node->start(); i <= node->finish(); ++i) {
+ assert(node->child(i) != nullptr);
+ assert(node->child(i)->parent() == node);
+ assert(node->child(i)->position() == i);
+ count += internal_verify(node->child(i),
+ i == node->start() ? lo : &node->key(i - 1),
+ i == node->finish() ? hi : &node->key(i));
+ }
+ }
+ return count;
+}
+
+} // namespace container_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_BTREE_H_
diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h
new file mode 100644
index 00000000..f2e4c3a5
--- /dev/null
+++ b/absl/container/internal/btree_container.h
@@ -0,0 +1,672 @@
+// 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_BTREE_CONTAINER_H_
+#define ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_
+
+#include <algorithm>
+#include <initializer_list>
+#include <iterator>
+#include <utility>
+
+#include "absl/base/internal/throw_delegate.h"
+#include "absl/container/internal/btree.h" // IWYU pragma: export
+#include "absl/container/internal/common.h"
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace container_internal {
+
+// A common base class for btree_set, btree_map, btree_multiset, and
+// btree_multimap.
+template <typename Tree>
+class btree_container {
+ using params_type = typename Tree::params_type;
+
+ protected:
+ // Alias used for heterogeneous lookup functions.
+ // `key_arg<K>` evaluates to `K` when the functors are transparent and to
+ // `key_type` otherwise. It permits template argument deduction on `K` for the
+ // transparent case.
+ template <class K>
+ using key_arg =
+ typename KeyArg<IsTransparent<typename Tree::key_compare>::value>::
+ template type<K, typename Tree::key_type>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using value_type = typename Tree::value_type;
+ using size_type = typename Tree::size_type;
+ using difference_type = typename Tree::difference_type;
+ using key_compare = typename Tree::key_compare;
+ using value_compare = typename Tree::value_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using reference = typename Tree::reference;
+ using const_reference = typename Tree::const_reference;
+ using pointer = typename Tree::pointer;
+ using const_pointer = typename Tree::const_pointer;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+ using reverse_iterator = typename Tree::reverse_iterator;
+ using const_reverse_iterator = typename Tree::const_reverse_iterator;
+ using node_type = typename Tree::node_handle_type;
+
+ // Constructors/assignments.
+ btree_container() : tree_(key_compare(), allocator_type()) {}
+ explicit btree_container(const key_compare &comp,
+ const allocator_type &alloc = allocator_type())
+ : tree_(comp, alloc) {}
+ btree_container(const btree_container &x) = default;
+ btree_container(btree_container &&x) noexcept = default;
+ btree_container &operator=(const btree_container &x) = default;
+ btree_container &operator=(btree_container &&x) noexcept(
+ std::is_nothrow_move_assignable<Tree>::value) = default;
+
+ // Iterator routines.
+ iterator begin() { return tree_.begin(); }
+ const_iterator begin() const { return tree_.begin(); }
+ const_iterator cbegin() const { return tree_.begin(); }
+ iterator end() { return tree_.end(); }
+ const_iterator end() const { return tree_.end(); }
+ const_iterator cend() const { return tree_.end(); }
+ reverse_iterator rbegin() { return tree_.rbegin(); }
+ const_reverse_iterator rbegin() const { return tree_.rbegin(); }
+ const_reverse_iterator crbegin() const { return tree_.rbegin(); }
+ reverse_iterator rend() { return tree_.rend(); }
+ const_reverse_iterator rend() const { return tree_.rend(); }
+ const_reverse_iterator crend() const { return tree_.rend(); }
+
+ // Lookup routines.
+ template <typename K = key_type>
+ iterator find(const key_arg<K> &key) {
+ return tree_.find(key);
+ }
+ template <typename K = key_type>
+ const_iterator find(const key_arg<K> &key) const {
+ return tree_.find(key);
+ }
+ template <typename K = key_type>
+ bool contains(const key_arg<K> &key) const {
+ return find(key) != end();
+ }
+ template <typename K = key_type>
+ iterator lower_bound(const key_arg<K> &key) {
+ return tree_.lower_bound(key);
+ }
+ template <typename K = key_type>
+ const_iterator lower_bound(const key_arg<K> &key) const {
+ return tree_.lower_bound(key);
+ }
+ template <typename K = key_type>
+ iterator upper_bound(const key_arg<K> &key) {
+ return tree_.upper_bound(key);
+ }
+ template <typename K = key_type>
+ const_iterator upper_bound(const key_arg<K> &key) const {
+ return tree_.upper_bound(key);
+ }
+ template <typename K = key_type>
+ std::pair<iterator, iterator> equal_range(const key_arg<K> &key) {
+ return tree_.equal_range(key);
+ }
+ template <typename K = key_type>
+ std::pair<const_iterator, const_iterator> equal_range(
+ const key_arg<K> &key) const {
+ return tree_.equal_range(key);
+ }
+
+ // Deletion routines. Note that there is also a deletion routine that is
+ // specific to btree_set_container/btree_multiset_container.
+
+ // Erase the specified iterator from the btree. The iterator must be valid
+ // (i.e. not equal to end()). Return an iterator pointing to the node after
+ // the one that was erased (or end() if none exists).
+ iterator erase(const_iterator iter) { return tree_.erase(iterator(iter)); }
+ iterator erase(iterator iter) { return tree_.erase(iter); }
+ iterator erase(const_iterator first, const_iterator last) {
+ return tree_.erase_range(iterator(first), iterator(last)).second;
+ }
+
+ // Extract routines.
+ node_type extract(iterator position) {
+ // Use Move instead of Transfer, because the rebalancing code expects to
+ // have a valid object to scribble metadata bits on top of.
+ auto node = CommonAccess::Move<node_type>(get_allocator(), position.slot());
+ erase(position);
+ return node;
+ }
+ node_type extract(const_iterator position) {
+ return extract(iterator(position));
+ }
+
+ public:
+ // Utility routines.
+ void clear() { tree_.clear(); }
+ void swap(btree_container &x) { tree_.swap(x.tree_); }
+ void verify() const { tree_.verify(); }
+
+ // Size routines.
+ size_type size() const { return tree_.size(); }
+ size_type max_size() const { return tree_.max_size(); }
+ bool empty() const { return tree_.empty(); }
+
+ friend bool operator==(const btree_container &x, const btree_container &y) {
+ if (x.size() != y.size()) return false;
+ return std::equal(x.begin(), x.end(), y.begin());
+ }
+
+ friend bool operator!=(const btree_container &x, const btree_container &y) {
+ return !(x == y);
+ }
+
+ friend bool operator<(const btree_container &x, const btree_container &y) {
+ return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());
+ }
+
+ friend bool operator>(const btree_container &x, const btree_container &y) {
+ return y < x;
+ }
+
+ friend bool operator<=(const btree_container &x, const btree_container &y) {
+ return !(y < x);
+ }
+
+ friend bool operator>=(const btree_container &x, const btree_container &y) {
+ return !(x < y);
+ }
+
+ // The allocator used by the btree.
+ allocator_type get_allocator() const { return tree_.get_allocator(); }
+
+ // The key comparator used by the btree.
+ key_compare key_comp() const { return tree_.key_comp(); }
+ value_compare value_comp() const { return tree_.value_comp(); }
+
+ // Support absl::Hash.
+ template <typename State>
+ friend State AbslHashValue(State h, const btree_container &b) {
+ for (const auto &v : b) {
+ h = State::combine(std::move(h), v);
+ }
+ return State::combine(std::move(h), b.size());
+ }
+
+ protected:
+ Tree tree_;
+};
+
+// A common base class for btree_set and btree_map.
+template <typename Tree>
+class btree_set_container : public btree_container<Tree> {
+ using super_type = btree_container<Tree>;
+ using params_type = typename Tree::params_type;
+ using init_type = typename params_type::init_type;
+ using is_key_compare_to = typename params_type::is_key_compare_to;
+ friend class BtreeNodePeer;
+
+ protected:
+ template <class K>
+ using key_arg = typename super_type::template key_arg<K>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using value_type = typename Tree::value_type;
+ using size_type = typename Tree::size_type;
+ using key_compare = typename Tree::key_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+ using node_type = typename super_type::node_type;
+ using insert_return_type = InsertReturnType<iterator, node_type>;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_set_container() {}
+
+ // Range constructor.
+ template <class InputIterator>
+ btree_set_container(InputIterator b, InputIterator e,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : super_type(comp, alloc) {
+ insert(b, e);
+ }
+
+ // Initializer list constructor.
+ btree_set_container(std::initializer_list<init_type> init,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : btree_set_container(init.begin(), init.end(), comp, alloc) {}
+
+ // Lookup routines.
+ template <typename K = key_type>
+ size_type count(const key_arg<K> &key) const {
+ return this->tree_.count_unique(key);
+ }
+
+ // Insertion routines.
+ std::pair<iterator, bool> insert(const value_type &x) {
+ return this->tree_.insert_unique(params_type::key(x), x);
+ }
+ std::pair<iterator, bool> insert(value_type &&x) {
+ return this->tree_.insert_unique(params_type::key(x), std::move(x));
+ }
+ template <typename... Args>
+ std::pair<iterator, bool> emplace(Args &&... args) {
+ init_type v(std::forward<Args>(args)...);
+ return this->tree_.insert_unique(params_type::key(v), std::move(v));
+ }
+ iterator insert(const_iterator position, const value_type &x) {
+ return this->tree_
+ .insert_hint_unique(iterator(position), params_type::key(x), x)
+ .first;
+ }
+ iterator insert(const_iterator position, value_type &&x) {
+ return this->tree_
+ .insert_hint_unique(iterator(position), params_type::key(x),
+ std::move(x))
+ .first;
+ }
+ template <typename... Args>
+ iterator emplace_hint(const_iterator position, Args &&... args) {
+ init_type v(std::forward<Args>(args)...);
+ return this->tree_
+ .insert_hint_unique(iterator(position), params_type::key(v),
+ std::move(v))
+ .first;
+ }
+ template <typename InputIterator>
+ void insert(InputIterator b, InputIterator e) {
+ this->tree_.insert_iterator_unique(b, e);
+ }
+ void insert(std::initializer_list<init_type> init) {
+ this->tree_.insert_iterator_unique(init.begin(), init.end());
+ }
+ insert_return_type insert(node_type &&node) {
+ if (!node) return {this->end(), false, node_type()};
+ std::pair<iterator, bool> res =
+ this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)),
+ CommonAccess::GetSlot(node));
+ if (res.second) {
+ CommonAccess::Destroy(&node);
+ return {res.first, true, node_type()};
+ } else {
+ return {res.first, false, std::move(node)};
+ }
+ }
+ iterator insert(const_iterator hint, node_type &&node) {
+ if (!node) return this->end();
+ std::pair<iterator, bool> res = this->tree_.insert_hint_unique(
+ iterator(hint), params_type::key(CommonAccess::GetSlot(node)),
+ CommonAccess::GetSlot(node));
+ if (res.second) CommonAccess::Destroy(&node);
+ return res.first;
+ }
+
+ // Deletion routines.
+ template <typename K = key_type>
+ size_type erase(const key_arg<K> &key) {
+ return this->tree_.erase_unique(key);
+ }
+ using super_type::erase;
+
+ // Node extraction routines.
+ template <typename K = key_type>
+ node_type extract(const key_arg<K> &key) {
+ auto it = this->find(key);
+ return it == this->end() ? node_type() : extract(it);
+ }
+ using super_type::extract;
+
+ // Merge routines.
+ // Moves elements from `src` into `this`. If the element already exists in
+ // `this`, it is left unmodified in `src`.
+ template <
+ typename T,
+ typename absl::enable_if_t<
+ absl::conjunction<
+ std::is_same<value_type, typename T::value_type>,
+ std::is_same<allocator_type, typename T::allocator_type>,
+ std::is_same<typename params_type::is_map_container,
+ typename T::params_type::is_map_container>>::value,
+ int> = 0>
+ void merge(btree_container<T> &src) { // NOLINT
+ for (auto src_it = src.begin(); src_it != src.end();) {
+ if (insert(std::move(*src_it)).second) {
+ src_it = src.erase(src_it);
+ } else {
+ ++src_it;
+ }
+ }
+ }
+
+ template <
+ typename T,
+ typename absl::enable_if_t<
+ absl::conjunction<
+ std::is_same<value_type, typename T::value_type>,
+ std::is_same<allocator_type, typename T::allocator_type>,
+ std::is_same<typename params_type::is_map_container,
+ typename T::params_type::is_map_container>>::value,
+ int> = 0>
+ void merge(btree_container<T> &&src) {
+ merge(src);
+ }
+};
+
+// Base class for btree_map.
+template <typename Tree>
+class btree_map_container : public btree_set_container<Tree> {
+ using super_type = btree_set_container<Tree>;
+ using params_type = typename Tree::params_type;
+
+ private:
+ template <class K>
+ using key_arg = typename super_type::template key_arg<K>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using mapped_type = typename params_type::mapped_type;
+ using value_type = typename Tree::value_type;
+ using key_compare = typename Tree::key_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_map_container() {}
+
+ // Insertion routines.
+ // Note: the nullptr template arguments and extra `const M&` overloads allow
+ // for supporting bitfield arguments.
+ // Note: when we call `std::forward<M>(obj)` twice, it's safe because
+ // insert_unique/insert_hint_unique are guaranteed to not consume `obj` when
+ // `ret.second` is false.
+ template <class M>
+ std::pair<iterator, bool> insert_or_assign(const key_type &k, const M &obj) {
+ const std::pair<iterator, bool> ret = this->tree_.insert_unique(k, k, obj);
+ if (!ret.second) ret.first->second = obj;
+ return ret;
+ }
+ template <class M, key_type * = nullptr>
+ std::pair<iterator, bool> insert_or_assign(key_type &&k, const M &obj) {
+ const std::pair<iterator, bool> ret =
+ this->tree_.insert_unique(k, std::move(k), obj);
+ if (!ret.second) ret.first->second = obj;
+ return ret;
+ }
+ template <class M, M * = nullptr>
+ std::pair<iterator, bool> insert_or_assign(const key_type &k, M &&obj) {
+ const std::pair<iterator, bool> ret =
+ this->tree_.insert_unique(k, k, std::forward<M>(obj));
+ if (!ret.second) ret.first->second = std::forward<M>(obj);
+ return ret;
+ }
+ template <class M, key_type * = nullptr, M * = nullptr>
+ std::pair<iterator, bool> insert_or_assign(key_type &&k, M &&obj) {
+ const std::pair<iterator, bool> ret =
+ this->tree_.insert_unique(k, std::move(k), std::forward<M>(obj));
+ if (!ret.second) ret.first->second = std::forward<M>(obj);
+ return ret;
+ }
+ template <class M>
+ iterator insert_or_assign(const_iterator position, const key_type &k,
+ const M &obj) {
+ const std::pair<iterator, bool> ret =
+ this->tree_.insert_hint_unique(iterator(position), k, k, obj);
+ if (!ret.second) ret.first->second = obj;
+ return ret.first;
+ }
+ template <class M, key_type * = nullptr>
+ iterator insert_or_assign(const_iterator position, key_type &&k,
+ const M &obj) {
+ const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique(
+ iterator(position), k, std::move(k), obj);
+ if (!ret.second) ret.first->second = obj;
+ return ret.first;
+ }
+ template <class M, M * = nullptr>
+ iterator insert_or_assign(const_iterator position, const key_type &k,
+ M &&obj) {
+ const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique(
+ iterator(position), k, k, std::forward<M>(obj));
+ if (!ret.second) ret.first->second = std::forward<M>(obj);
+ return ret.first;
+ }
+ template <class M, key_type * = nullptr, M * = nullptr>
+ iterator insert_or_assign(const_iterator position, key_type &&k, M &&obj) {
+ const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique(
+ iterator(position), k, std::move(k), std::forward<M>(obj));
+ if (!ret.second) ret.first->second = std::forward<M>(obj);
+ return ret.first;
+ }
+ template <typename... Args>
+ std::pair<iterator, bool> try_emplace(const key_type &k, Args &&... args) {
+ return this->tree_.insert_unique(
+ k, std::piecewise_construct, std::forward_as_tuple(k),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ template <typename... Args>
+ std::pair<iterator, bool> try_emplace(key_type &&k, Args &&... args) {
+ // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k`
+ // and then using `k` unsequenced. This is safe because the move is into a
+ // forwarding reference and insert_unique guarantees that `key` is never
+ // referenced after consuming `args`.
+ const key_type &key_ref = k;
+ return this->tree_.insert_unique(
+ key_ref, std::piecewise_construct, std::forward_as_tuple(std::move(k)),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ template <typename... Args>
+ iterator try_emplace(const_iterator hint, const key_type &k,
+ Args &&... args) {
+ return this->tree_
+ .insert_hint_unique(iterator(hint), k, std::piecewise_construct,
+ std::forward_as_tuple(k),
+ std::forward_as_tuple(std::forward<Args>(args)...))
+ .first;
+ }
+ template <typename... Args>
+ iterator try_emplace(const_iterator hint, key_type &&k, Args &&... args) {
+ // Note: `key_ref` exists to avoid a ClangTidy warning about moving from `k`
+ // and then using `k` unsequenced. This is safe because the move is into a
+ // forwarding reference and insert_hint_unique guarantees that `key` is
+ // never referenced after consuming `args`.
+ const key_type &key_ref = k;
+ return this->tree_
+ .insert_hint_unique(iterator(hint), key_ref, std::piecewise_construct,
+ std::forward_as_tuple(std::move(k)),
+ std::forward_as_tuple(std::forward<Args>(args)...))
+ .first;
+ }
+ mapped_type &operator[](const key_type &k) {
+ return try_emplace(k).first->second;
+ }
+ mapped_type &operator[](key_type &&k) {
+ return try_emplace(std::move(k)).first->second;
+ }
+
+ template <typename K = key_type>
+ mapped_type &at(const key_arg<K> &key) {
+ auto it = this->find(key);
+ if (it == this->end())
+ base_internal::ThrowStdOutOfRange("absl::btree_map::at");
+ return it->second;
+ }
+ template <typename K = key_type>
+ const mapped_type &at(const key_arg<K> &key) const {
+ auto it = this->find(key);
+ if (it == this->end())
+ base_internal::ThrowStdOutOfRange("absl::btree_map::at");
+ return it->second;
+ }
+};
+
+// A common base class for btree_multiset and btree_multimap.
+template <typename Tree>
+class btree_multiset_container : public btree_container<Tree> {
+ using super_type = btree_container<Tree>;
+ using params_type = typename Tree::params_type;
+ using init_type = typename params_type::init_type;
+ using is_key_compare_to = typename params_type::is_key_compare_to;
+
+ template <class K>
+ using key_arg = typename super_type::template key_arg<K>;
+
+ public:
+ using key_type = typename Tree::key_type;
+ using value_type = typename Tree::value_type;
+ using size_type = typename Tree::size_type;
+ using key_compare = typename Tree::key_compare;
+ using allocator_type = typename Tree::allocator_type;
+ using iterator = typename Tree::iterator;
+ using const_iterator = typename Tree::const_iterator;
+ using node_type = typename super_type::node_type;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_multiset_container() {}
+
+ // Range constructor.
+ template <class InputIterator>
+ btree_multiset_container(InputIterator b, InputIterator e,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : super_type(comp, alloc) {
+ insert(b, e);
+ }
+
+ // Initializer list constructor.
+ btree_multiset_container(std::initializer_list<init_type> init,
+ const key_compare &comp = key_compare(),
+ const allocator_type &alloc = allocator_type())
+ : btree_multiset_container(init.begin(), init.end(), comp, alloc) {}
+
+ // Lookup routines.
+ template <typename K = key_type>
+ size_type count(const key_arg<K> &key) const {
+ return this->tree_.count_multi(key);
+ }
+
+ // Insertion routines.
+ iterator insert(const value_type &x) { return this->tree_.insert_multi(x); }
+ iterator insert(value_type &&x) {
+ return this->tree_.insert_multi(std::move(x));
+ }
+ iterator insert(const_iterator position, const value_type &x) {
+ return this->tree_.insert_hint_multi(iterator(position), x);
+ }
+ iterator insert(const_iterator position, value_type &&x) {
+ return this->tree_.insert_hint_multi(iterator(position), std::move(x));
+ }
+ template <typename InputIterator>
+ void insert(InputIterator b, InputIterator e) {
+ this->tree_.insert_iterator_multi(b, e);
+ }
+ void insert(std::initializer_list<init_type> init) {
+ this->tree_.insert_iterator_multi(init.begin(), init.end());
+ }
+ template <typename... Args>
+ iterator emplace(Args &&... args) {
+ return this->tree_.insert_multi(init_type(std::forward<Args>(args)...));
+ }
+ template <typename... Args>
+ iterator emplace_hint(const_iterator position, Args &&... args) {
+ return this->tree_.insert_hint_multi(
+ iterator(position), init_type(std::forward<Args>(args)...));
+ }
+ iterator insert(node_type &&node) {
+ if (!node) return this->end();
+ iterator res =
+ this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)),
+ CommonAccess::GetSlot(node));
+ CommonAccess::Destroy(&node);
+ return res;
+ }
+ iterator insert(const_iterator hint, node_type &&node) {
+ if (!node) return this->end();
+ iterator res = this->tree_.insert_hint_multi(
+ iterator(hint),
+ std::move(params_type::element(CommonAccess::GetSlot(node))));
+ CommonAccess::Destroy(&node);
+ return res;
+ }
+
+ // Deletion routines.
+ template <typename K = key_type>
+ size_type erase(const key_arg<K> &key) {
+ return this->tree_.erase_multi(key);
+ }
+ using super_type::erase;
+
+ // Node extraction routines.
+ template <typename K = key_type>
+ node_type extract(const key_arg<K> &key) {
+ auto it = this->find(key);
+ return it == this->end() ? node_type() : extract(it);
+ }
+ using super_type::extract;
+
+ // Merge routines.
+ // Moves all elements from `src` into `this`.
+ template <
+ typename T,
+ typename absl::enable_if_t<
+ absl::conjunction<
+ std::is_same<value_type, typename T::value_type>,
+ std::is_same<allocator_type, typename T::allocator_type>,
+ std::is_same<typename params_type::is_map_container,
+ typename T::params_type::is_map_container>>::value,
+ int> = 0>
+ void merge(btree_container<T> &src) { // NOLINT
+ insert(std::make_move_iterator(src.begin()),
+ std::make_move_iterator(src.end()));
+ src.clear();
+ }
+
+ template <
+ typename T,
+ typename absl::enable_if_t<
+ absl::conjunction<
+ std::is_same<value_type, typename T::value_type>,
+ std::is_same<allocator_type, typename T::allocator_type>,
+ std::is_same<typename params_type::is_map_container,
+ typename T::params_type::is_map_container>>::value,
+ int> = 0>
+ void merge(btree_container<T> &&src) {
+ merge(src);
+ }
+};
+
+// A base class for btree_multimap.
+template <typename Tree>
+class btree_multimap_container : public btree_multiset_container<Tree> {
+ using super_type = btree_multiset_container<Tree>;
+ using params_type = typename Tree::params_type;
+
+ public:
+ using mapped_type = typename params_type::mapped_type;
+
+ // Inherit constructors.
+ using super_type::super_type;
+ btree_multimap_container() {}
+};
+
+} // namespace container_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_
diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h
index a02cd5c3..5037d803 100644
--- a/absl/container/internal/common.h
+++ b/absl/container/internal/common.h
@@ -22,7 +22,7 @@
#include "absl/types/optional.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class, class = void>
@@ -56,7 +56,7 @@ class node_handle_base {
public:
using allocator_type = Alloc;
- constexpr node_handle_base() {}
+ constexpr node_handle_base() = default;
node_handle_base(node_handle_base&& other) noexcept {
*this = std::move(other);
}
@@ -109,16 +109,15 @@ class node_handle_base {
allocator_type* alloc() { return std::addressof(*alloc_); }
private:
- absl::optional<allocator_type> alloc_;
- mutable absl::aligned_storage_t<sizeof(slot_type), alignof(slot_type)>
- slot_space_;
+ absl::optional<allocator_type> alloc_ = {};
+ alignas(slot_type) mutable unsigned char slot_space_[sizeof(slot_type)] = {};
};
// For sets.
template <typename Policy, typename PolicyTraits, typename Alloc,
typename = void>
class node_handle : public node_handle_base<PolicyTraits, Alloc> {
- using Base = typename node_handle::node_handle_base;
+ using Base = node_handle_base<PolicyTraits, Alloc>;
public:
using value_type = typename PolicyTraits::value_type;
@@ -138,7 +137,7 @@ template <typename Policy, typename PolicyTraits, typename Alloc>
class node_handle<Policy, PolicyTraits, Alloc,
absl::void_t<typename Policy::mapped_type>>
: public node_handle_base<PolicyTraits, Alloc> {
- using Base = typename node_handle::node_handle_base;
+ using Base = node_handle_base<PolicyTraits, Alloc>;
public:
using key_type = typename Policy::key_type;
@@ -168,6 +167,11 @@ struct CommonAccess {
}
template <typename Node>
+ static void Destroy(Node* node) {
+ node->destroy();
+ }
+
+ template <typename Node>
static void Reset(Node* node) {
node->reset();
}
@@ -192,7 +196,7 @@ struct InsertReturnType {
};
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_H_
diff --git a/absl/container/internal/compressed_tuple.h b/absl/container/internal/compressed_tuple.h
index fbace496..4bfe92fd 100644
--- a/absl/container/internal/compressed_tuple.h
+++ b/absl/container/internal/compressed_tuple.h
@@ -48,7 +48,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <typename... Ts>
@@ -257,7 +257,7 @@ template <>
class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {};
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
diff --git a/absl/container/internal/compressed_tuple_test.cc b/absl/container/internal/compressed_tuple_test.cc
index ec893b90..1dae12db 100644
--- a/absl/container/internal/compressed_tuple_test.cc
+++ b/absl/container/internal/compressed_tuple_test.cc
@@ -48,7 +48,7 @@ struct TwoValues {
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -333,10 +333,6 @@ TEST(CompressedTupleTest, AnyElements) {
a = 0.5f;
EXPECT_EQ(absl::any_cast<float>(x.get<1>()), 0.5);
-
- // Ensure copy construction work in the face of a type with a universal
- // implicit constructor;
- CompressedTuple<absl::any> c{}, d(c); // NOLINT
}
TEST(CompressedTupleTest, Constexpr) {
@@ -409,5 +405,5 @@ TEST(CompressedTupleTest, EmptyFinalClass) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h
index eb6d7eb7..d24b0f84 100644
--- a/absl/container/internal/container_memory.h
+++ b/absl/container/internal/container_memory.h
@@ -34,7 +34,7 @@
#include "absl/utility/utility.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
// Allocates at least n bytes aligned to the specified alignment.
@@ -434,7 +434,7 @@ struct map_slot_policy {
};
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_
diff --git a/absl/container/internal/container_memory_test.cc b/absl/container/internal/container_memory_test.cc
index ea9568dc..7942c7be 100644
--- a/absl/container/internal/container_memory_test.cc
+++ b/absl/container/internal/container_memory_test.cc
@@ -23,7 +23,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -186,5 +186,5 @@ TEST(DecomposePair, NotDecomposable) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/counting_allocator.h b/absl/container/internal/counting_allocator.h
index 94a457ca..9efdc662 100644
--- a/absl/container/internal/counting_allocator.h
+++ b/absl/container/internal/counting_allocator.h
@@ -19,8 +19,10 @@
#include <cstdint>
#include <memory>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
// This is a stateful allocator, but the state lives outside of the
@@ -75,7 +77,7 @@ class CountingAllocator : public std::allocator<T> {
};
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_
diff --git a/absl/container/internal/hash_function_defaults.h b/absl/container/internal/hash_function_defaults.h
index 2155076d..401ddf4d 100644
--- a/absl/container/internal/hash_function_defaults.h
+++ b/absl/container/internal/hash_function_defaults.h
@@ -56,7 +56,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
// The hash of an object of type T is computed by using absl::Hash.
@@ -140,7 +140,7 @@ template <class T>
using hash_default_eq = typename container_internal::HashEq<T>::Eq;
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
diff --git a/absl/container/internal/hash_function_defaults_test.cc b/absl/container/internal/hash_function_defaults_test.cc
index ce6133f8..2eefc7e0 100644
--- a/absl/container/internal/hash_function_defaults_test.cc
+++ b/absl/container/internal/hash_function_defaults_test.cc
@@ -22,7 +22,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -249,7 +249,7 @@ TYPED_TEST_SUITE(StringLikeTest, StringTypesCartesianProduct);
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
enum Hash : size_t {
@@ -280,7 +280,7 @@ struct hash<Hashable<H>> {
} // namespace std
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -295,5 +295,5 @@ TEST(Delegate, HashDispatch) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc
index 36b2571b..75c4db6c 100644
--- a/absl/container/internal/hash_generator_testing.cc
+++ b/absl/container/internal/hash_generator_testing.cc
@@ -17,7 +17,7 @@
#include <deque>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace hash_internal {
namespace {
@@ -70,5 +70,5 @@ absl::string_view Generator<absl::string_view>::operator()() const {
} // namespace hash_internal
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h
index 27962c35..6869fe45 100644
--- a/absl/container/internal/hash_generator_testing.h
+++ b/absl/container/internal/hash_generator_testing.h
@@ -19,6 +19,7 @@
#define ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
#include <stdint.h>
+
#include <algorithm>
#include <iosfwd>
#include <random>
@@ -27,11 +28,12 @@
#include <utility>
#include "absl/container/internal/hash_policy_testing.h"
+#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace hash_internal {
namespace generator_internal {
@@ -130,6 +132,13 @@ struct Generator<std::tuple<Ts...>> {
}
};
+template <class T>
+struct Generator<std::unique_ptr<T>> {
+ std::unique_ptr<T> operator()() const {
+ return absl::make_unique<T>(Generator<T>()());
+ }
+};
+
template <class U>
struct Generator<U, absl::void_t<decltype(std::declval<U&>().key()),
decltype(std::declval<U&>().value())>>
@@ -146,7 +155,7 @@ using GeneratedType = decltype(
} // namespace hash_internal
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
diff --git a/absl/container/internal/hash_policy_testing.h b/absl/container/internal/hash_policy_testing.h
index 8f0d2a52..01c40d2e 100644
--- a/absl/container/internal/hash_policy_testing.h
+++ b/absl/container/internal/hash_policy_testing.h
@@ -30,7 +30,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace hash_testing_internal {
@@ -163,7 +163,7 @@ auto keys(const Set& s)
}
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions
diff --git a/absl/container/internal/hash_policy_testing_test.cc b/absl/container/internal/hash_policy_testing_test.cc
index 8fd1df00..f0b20fe3 100644
--- a/absl/container/internal/hash_policy_testing_test.cc
+++ b/absl/container/internal/hash_policy_testing_test.cc
@@ -17,7 +17,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -41,5 +41,5 @@ TEST(_, Hash) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/hash_policy_traits.h b/absl/container/internal/hash_policy_traits.h
index 3d87e821..3e1209c6 100644
--- a/absl/container/internal/hash_policy_traits.h
+++ b/absl/container/internal/hash_policy_traits.h
@@ -23,7 +23,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
// Defines how slots are initialized/destroyed/moved.
@@ -185,7 +185,7 @@ struct hash_policy_traits {
};
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_
diff --git a/absl/container/internal/hash_policy_traits_test.cc b/absl/container/internal/hash_policy_traits_test.cc
index edfaf63e..6ef8b9e0 100644
--- a/absl/container/internal/hash_policy_traits_test.cc
+++ b/absl/container/internal/hash_policy_traits_test.cc
@@ -22,7 +22,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -140,5 +140,5 @@ TEST_F(Test, with_transfer) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/hashtable_debug.h b/absl/container/internal/hashtable_debug.h
index 1d1a9c28..19d52121 100644
--- a/absl/container/internal/hashtable_debug.h
+++ b/absl/container/internal/hashtable_debug.h
@@ -38,7 +38,7 @@
#include "absl/container/internal/hashtable_debug_hooks.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
// Returns the number of probes required to lookup `key`. Returns 0 for a
@@ -104,7 +104,7 @@ size_t LowerBoundAllocatedByteSize(size_t num_elements) {
}
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_
diff --git a/absl/container/internal/hashtable_debug_hooks.h b/absl/container/internal/hashtable_debug_hooks.h
index 7b95fcef..3e9ea595 100644
--- a/absl/container/internal/hashtable_debug_hooks.h
+++ b/absl/container/internal/hashtable_debug_hooks.h
@@ -23,8 +23,10 @@
#include <type_traits>
#include <vector>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace hashtable_debug_internal {
@@ -77,7 +79,7 @@ struct HashtableDebugAccess {
} // namespace hashtable_debug_internal
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_
diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc
index 2338045d..56447251 100644
--- a/absl/container/internal/hashtablez_sampler.cc
+++ b/absl/container/internal/hashtablez_sampler.cc
@@ -21,13 +21,14 @@
#include <limits>
#include "absl/base/attributes.h"
+#include "absl/base/internal/exponential_biased.h"
#include "absl/container/internal/have_sse.h"
#include "absl/debugging/stacktrace.h"
#include "absl/memory/memory.h"
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
constexpr int HashtablezInfo::kMaxStackDepth;
@@ -38,80 +39,17 @@ ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{
ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10};
ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_max_samples{1 << 20};
-// Returns the next pseudo-random value.
-// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48
-// This is the lrand64 generator.
-uint64_t NextRandom(uint64_t rnd) {
- const uint64_t prng_mult = uint64_t{0x5DEECE66D};
- const uint64_t prng_add = 0xB;
- const uint64_t prng_mod_power = 48;
- const uint64_t prng_mod_mask = ~(~uint64_t{0} << prng_mod_power);
- return (prng_mult * rnd + prng_add) & prng_mod_mask;
-}
-
-// Generates a geometric variable with the specified mean.
-// This is done by generating a random number between 0 and 1 and applying
-// the inverse cumulative distribution function for an exponential.
-// Specifically: Let m be the inverse of the sample period, then
-// the probability distribution function is m*exp(-mx) so the CDF is
-// p = 1 - exp(-mx), so
-// q = 1 - p = exp(-mx)
-// log_e(q) = -mx
-// -log_e(q)/m = x
-// log_2(q) * (-log_e(2) * 1/m) = x
-// In the code, q is actually in the range 1 to 2**26, hence the -26 below
-//
-int64_t GetGeometricVariable(int64_t mean) {
-#if ABSL_HAVE_THREAD_LOCAL
- thread_local
-#else // ABSL_HAVE_THREAD_LOCAL
- // SampleSlow and hence GetGeometricVariable is guarded by a single mutex when
- // there are not thread locals. Thus, a single global rng is acceptable for
- // that case.
- static
-#endif // ABSL_HAVE_THREAD_LOCAL
- uint64_t rng = []() {
- // We don't get well distributed numbers from this so we call
- // NextRandom() a bunch to mush the bits around. We use a global_rand
- // to handle the case where the same thread (by memory address) gets
- // created and destroyed repeatedly.
- ABSL_CONST_INIT static std::atomic<uint32_t> global_rand(0);
- uint64_t r = reinterpret_cast<uint64_t>(&rng) +
- global_rand.fetch_add(1, std::memory_order_relaxed);
- for (int i = 0; i < 20; ++i) {
- r = NextRandom(r);
- }
- return r;
- }();
-
- rng = NextRandom(rng);
-
- // Take the top 26 bits as the random number
- // (This plus the 1<<58 sampling bound give a max possible step of
- // 5194297183973780480 bytes.)
- const uint64_t prng_mod_power = 48; // Number of bits in prng
- // The uint32_t cast is to prevent a (hard-to-reproduce) NAN
- // under piii debug for some binaries.
- double q = static_cast<uint32_t>(rng >> (prng_mod_power - 26)) + 1.0;
- // Put the computed p-value through the CDF of a geometric.
- double interval = (log2(q) - 26) * (-std::log(2.0) * mean);
-
- // Very large values of interval overflow int64_t. If we happen to
- // hit such improbable condition, we simply cheat and clamp interval
- // to largest supported value.
- if (interval > static_cast<double>(std::numeric_limits<int64_t>::max() / 2)) {
- return std::numeric_limits<int64_t>::max() / 2;
- }
-
- // Small values of interval are equivalent to just sampling next time.
- if (interval < 1) {
- return 1;
- }
- return static_cast<int64_t>(interval);
-}
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased
+ g_exponential_biased_generator;
+#endif
} // namespace
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0;
+#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+
HashtablezSampler& HashtablezSampler::Global() {
static auto* sampler = new HashtablezSampler();
return *sampler;
@@ -229,15 +167,39 @@ int64_t HashtablezSampler::Iterate(
return dropped_samples_.load(std::memory_order_relaxed);
}
+static bool ShouldForceSampling() {
+ enum ForceState {
+ kDontForce,
+ kForce,
+ kUninitialized
+ };
+ ABSL_CONST_INIT static std::atomic<ForceState> global_state{
+ kUninitialized};
+ ForceState state = global_state.load(std::memory_order_relaxed);
+ if (ABSL_PREDICT_TRUE(state == kDontForce)) return false;
+
+ if (state == kUninitialized) {
+ state = AbslContainerInternalSampleEverything() ? kForce : kDontForce;
+ global_state.store(state, std::memory_order_relaxed);
+ }
+ return state == kForce;
+}
+
HashtablezInfo* SampleSlow(int64_t* next_sample) {
- if (kAbslContainerInternalSampleEverything) {
+ if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
*next_sample = 1;
return HashtablezSampler::Global().Register();
}
+#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+ *next_sample = std::numeric_limits<int64_t>::max();
+ return nullptr;
+#else
bool first = *next_sample < 0;
- *next_sample = GetGeometricVariable(
+ *next_sample = g_exponential_biased_generator.GetStride(
g_hashtablez_sample_parameter.load(std::memory_order_relaxed));
+ // Small values of interval are equivalent to just sampling next time.
+ ABSL_ASSERT(*next_sample >= 1);
// g_hashtablez_enabled can be dynamically flipped, we need to set a threshold
// low enough that we will start sampling in a reasonable time, so we just use
@@ -252,12 +214,9 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) {
}
return HashtablezSampler::Global().Register();
+#endif
}
-#if ABSL_PER_THREAD_TLS == 1
-ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0;
-#endif // ABSL_PER_THREAD_TLS == 1
-
void UnsampleSlow(HashtablezInfo* info) {
HashtablezSampler::Global().Unregister(info);
}
@@ -306,5 +265,5 @@ void SetHashtablezMaxSamples(int32_t max) {
}
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h
index f17c425c..34d5e572 100644
--- a/absl/container/internal/hashtablez_sampler.h
+++ b/absl/container/internal/hashtablez_sampler.h
@@ -51,7 +51,7 @@
#include "absl/utility/utility.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
// Stores information about a sampled hashtable. All mutations to this *must*
@@ -66,7 +66,7 @@ struct HashtablezInfo {
// Puts the object into a clean state, fills in the logically `const` members,
// blocking for any readers that are currently sampling the object.
- void PrepareForSampling() EXCLUSIVE_LOCKS_REQUIRED(init_mu);
+ void PrepareForSampling() ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu);
// These fields are mutated by the various Record* APIs and need to be
// thread-safe.
@@ -84,7 +84,7 @@ struct HashtablezInfo {
// prevents races with sampling and resurrecting an object.
absl::Mutex init_mu;
HashtablezInfo* next;
- HashtablezInfo* dead GUARDED_BY(init_mu);
+ HashtablezInfo* dead ABSL_GUARDED_BY(init_mu);
// All of the fields below are set by `PrepareForSampling`, they must not be
// mutated in `Record*` functions. They are logically `const` in that sense.
@@ -180,23 +180,30 @@ class HashtablezInfoHandle {
HashtablezInfo* info_;
};
-#if ABSL_PER_THREAD_TLS == 1
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+#error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set
+#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
+
+#if (ABSL_PER_THREAD_TLS == 1) && !defined(ABSL_BUILD_DLL) && \
+ !defined(ABSL_CONSUME_DLL)
+#define ABSL_INTERNAL_HASHTABLEZ_SAMPLE
+#endif
+
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample;
#endif // ABSL_PER_THREAD_TLS
// Returns an RAII sampling handle that manages registration and unregistation
// with the global sampler.
inline HashtablezInfoHandle Sample() {
-#if ABSL_PER_THREAD_TLS == 0
- static auto* mu = new absl::Mutex;
- static int64_t global_next_sample = 0;
- absl::MutexLock l(mu);
-#endif // !ABSL_HAVE_THREAD_LOCAL
-
+#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) {
return HashtablezInfoHandle(nullptr);
}
return HashtablezInfoHandle(SampleSlow(&global_next_sample));
+#else
+ return HashtablezInfoHandle(nullptr);
+#endif // !ABSL_PER_THREAD_TLS
}
// Holds samples and their associated stack traces with a soft limit of
@@ -281,10 +288,10 @@ void SetHashtablezMaxSamples(int32_t max);
// initialization of static storage duration objects.
// The definition of this constant is weak, which allows us to inject a
// different value for it at link time.
-extern "C" const bool kAbslContainerInternalSampleEverything;
+extern "C" bool AbslContainerInternalSampleEverything();
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
diff --git a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
index d3f41c7c..78b9d362 100644
--- a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
+++ b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc
@@ -17,13 +17,14 @@
#include "absl/base/attributes.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
// See hashtablez_sampler.h for details.
-extern "C" ABSL_ATTRIBUTE_WEAK const bool
- kAbslContainerInternalSampleEverything = false;
+extern "C" ABSL_ATTRIBUTE_WEAK bool AbslContainerInternalSampleEverything() {
+ return false;
+}
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc
index bdae75f3..36f5ccdd 100644
--- a/absl/container/internal/hashtablez_sampler_test.cc
+++ b/absl/container/internal/hashtablez_sampler_test.cc
@@ -36,7 +36,7 @@ constexpr int kProbeLength = 8;
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
class HashtablezInfoHandlePeer {
public:
@@ -169,6 +169,7 @@ TEST(HashtablezInfoTest, RecordRehash) {
EXPECT_EQ(info.num_erases.load(), 0);
}
+#if defined(ABSL_HASHTABLEZ_SAMPLE)
TEST(HashtablezSamplerTest, SmallSampleParameter) {
SetHashtablezEnabled(true);
SetHashtablezSampleParameter(100);
@@ -212,6 +213,7 @@ TEST(HashtablezSamplerTest, Sample) {
}
EXPECT_NEAR(sample_rate, 0.01, 0.005);
}
+#endif
TEST(HashtablezSamplerTest, Handle) {
auto& sampler = HashtablezSampler::Global();
@@ -353,5 +355,5 @@ TEST(HashtablezSamplerTest, Callback) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index 123e04c9..4d80b727 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -30,7 +30,7 @@
#include "absl/types/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace inlined_vector_internal {
template <typename Iterator>
@@ -38,16 +38,17 @@ using IsAtLeastForwardIterator = std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category,
std::forward_iterator_tag>;
-template <typename AllocatorType>
-using IsMemcpyOk = absl::conjunction<
- std::is_same<std::allocator<typename AllocatorType::value_type>,
- AllocatorType>,
- absl::is_trivially_copy_constructible<typename AllocatorType::value_type>,
- absl::is_trivially_copy_assignable<typename AllocatorType::value_type>,
- absl::is_trivially_destructible<typename AllocatorType::value_type>>;
-
-template <typename AllocatorType, typename ValueType, typename SizeType>
-void DestroyElements(AllocatorType* alloc_ptr, ValueType* destroy_first,
+template <typename AllocatorType,
+ typename ValueType =
+ typename absl::allocator_traits<AllocatorType>::value_type>
+using IsMemcpyOk =
+ absl::conjunction<std::is_same<AllocatorType, std::allocator<ValueType>>,
+ absl::is_trivially_copy_constructible<ValueType>,
+ absl::is_trivially_copy_assignable<ValueType>,
+ absl::is_trivially_destructible<ValueType>>;
+
+template <typename AllocatorType, typename Pointer, typename SizeType>
+void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first,
SizeType destroy_size) {
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
@@ -57,21 +58,26 @@ void DestroyElements(AllocatorType* alloc_ptr, ValueType* destroy_first,
AllocatorTraits::destroy(*alloc_ptr, destroy_first + i);
}
-#ifndef NDEBUG
- // Overwrite unused memory with `0xab` so we can catch uninitialized usage.
- //
- // Cast to `void*` to tell the compiler that we don't care that we might be
- // scribbling on a vtable pointer.
- auto* memory_ptr = static_cast<void*>(destroy_first);
- auto memory_size = sizeof(ValueType) * destroy_size;
- std::memset(memory_ptr, 0xab, memory_size);
-#endif // NDEBUG
+#if !defined(NDEBUG)
+ {
+ using ValueType = typename AllocatorTraits::value_type;
+
+ // Overwrite unused memory with `0xab` so we can catch uninitialized
+ // usage.
+ //
+ // Cast to `void*` to tell the compiler that we don't care that we might
+ // be scribbling on a vtable pointer.
+ void* memory_ptr = destroy_first;
+ auto memory_size = destroy_size * sizeof(ValueType);
+ std::memset(memory_ptr, 0xab, memory_size);
+ }
+#endif // !defined(NDEBUG)
}
}
-template <typename AllocatorType, typename ValueType, typename ValueAdapter,
+template <typename AllocatorType, typename Pointer, typename ValueAdapter,
typename SizeType>
-void ConstructElements(AllocatorType* alloc_ptr, ValueType* construct_first,
+void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first,
ValueAdapter* values_ptr, SizeType construct_size) {
for (SizeType i = 0; i < construct_size; ++i) {
ABSL_INTERNAL_TRY {
@@ -84,8 +90,8 @@ void ConstructElements(AllocatorType* alloc_ptr, ValueType* construct_first,
}
}
-template <typename ValueType, typename ValueAdapter, typename SizeType>
-void AssignElements(ValueType* assign_first, ValueAdapter* values_ptr,
+template <typename Pointer, typename ValueAdapter, typename SizeType>
+void AssignElements(Pointer assign_first, ValueAdapter* values_ptr,
SizeType assign_size) {
for (SizeType i = 0; i < assign_size; ++i) {
values_ptr->AssignNext(assign_first + i);
@@ -94,28 +100,29 @@ void AssignElements(ValueType* assign_first, ValueAdapter* values_ptr,
template <typename AllocatorType>
struct StorageView {
- using pointer = typename AllocatorType::pointer;
- using size_type = typename AllocatorType::size_type;
+ using AllocatorTraits = absl::allocator_traits<AllocatorType>;
+ using Pointer = typename AllocatorTraits::pointer;
+ using SizeType = typename AllocatorTraits::size_type;
- pointer data;
- size_type size;
- size_type capacity;
+ Pointer data;
+ SizeType size;
+ SizeType capacity;
};
template <typename AllocatorType, typename Iterator>
class IteratorValueAdapter {
- using pointer = typename AllocatorType::pointer;
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
+ using Pointer = typename AllocatorTraits::pointer;
public:
explicit IteratorValueAdapter(const Iterator& it) : it_(it) {}
- void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) {
+ void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) {
AllocatorTraits::construct(*alloc_ptr, construct_at, *it_);
++it_;
}
- void AssignNext(pointer assign_at) {
+ void AssignNext(Pointer assign_at) {
*assign_at = *it_;
++it_;
}
@@ -126,46 +133,45 @@ class IteratorValueAdapter {
template <typename AllocatorType>
class CopyValueAdapter {
- using pointer = typename AllocatorType::pointer;
- using const_pointer = typename AllocatorType::const_pointer;
- using const_reference = typename AllocatorType::const_reference;
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
+ using ValueType = typename AllocatorTraits::value_type;
+ using Pointer = typename AllocatorTraits::pointer;
+ using ConstPointer = typename AllocatorTraits::const_pointer;
public:
- explicit CopyValueAdapter(const_reference v) : ptr_(std::addressof(v)) {}
+ explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {}
- void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) {
+ void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) {
AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_);
}
- void AssignNext(pointer assign_at) { *assign_at = *ptr_; }
+ void AssignNext(Pointer assign_at) { *assign_at = *ptr_; }
private:
- const_pointer ptr_;
+ ConstPointer ptr_;
};
template <typename AllocatorType>
class DefaultValueAdapter {
- using pointer = typename AllocatorType::pointer;
- using value_type = typename AllocatorType::value_type;
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
+ using ValueType = typename AllocatorTraits::value_type;
+ using Pointer = typename AllocatorTraits::pointer;
public:
explicit DefaultValueAdapter() {}
- void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) {
+ void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) {
AllocatorTraits::construct(*alloc_ptr, construct_at);
}
- void AssignNext(pointer assign_at) { *assign_at = value_type(); }
+ void AssignNext(Pointer assign_at) { *assign_at = ValueType(); }
};
template <typename AllocatorType>
class AllocationTransaction {
- using value_type = typename AllocatorType::value_type;
- using pointer = typename AllocatorType::pointer;
- using size_type = typename AllocatorType::size_type;
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
+ using Pointer = typename AllocatorTraits::pointer;
+ using SizeType = typename AllocatorTraits::size_type;
public:
explicit AllocationTransaction(AllocatorType* alloc_ptr)
@@ -181,25 +187,31 @@ class AllocationTransaction {
void operator=(const AllocationTransaction&) = delete;
AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); }
- pointer& GetData() { return alloc_data_.template get<1>(); }
- size_type& GetCapacity() { return capacity_; }
+ Pointer& GetData() { return alloc_data_.template get<1>(); }
+ SizeType& GetCapacity() { return capacity_; }
bool DidAllocate() { return GetData() != nullptr; }
- pointer Allocate(size_type capacity) {
+ Pointer Allocate(SizeType capacity) {
GetData() = AllocatorTraits::allocate(GetAllocator(), capacity);
GetCapacity() = capacity;
return GetData();
}
+ void Reset() {
+ GetData() = nullptr;
+ GetCapacity() = 0;
+ }
+
private:
- container_internal::CompressedTuple<AllocatorType, pointer> alloc_data_;
- size_type capacity_ = 0;
+ container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_;
+ SizeType capacity_ = 0;
};
template <typename AllocatorType>
class ConstructionTransaction {
- using pointer = typename AllocatorType::pointer;
- using size_type = typename AllocatorType::size_type;
+ using AllocatorTraits = absl::allocator_traits<AllocatorType>;
+ using Pointer = typename AllocatorTraits::pointer;
+ using SizeType = typename AllocatorTraits::size_type;
public:
explicit ConstructionTransaction(AllocatorType* alloc_ptr)
@@ -216,12 +228,12 @@ class ConstructionTransaction {
void operator=(const ConstructionTransaction&) = delete;
AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); }
- pointer& GetData() { return alloc_data_.template get<1>(); }
- size_type& GetSize() { return size_; }
+ Pointer& GetData() { return alloc_data_.template get<1>(); }
+ SizeType& GetSize() { return size_; }
bool DidConstruct() { return GetData() != nullptr; }
template <typename ValueAdapter>
- void Construct(pointer data, ValueAdapter* values_ptr, size_type size) {
+ void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) {
inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()),
data, values_ptr, size);
GetData() = data;
@@ -233,28 +245,29 @@ class ConstructionTransaction {
}
private:
- container_internal::CompressedTuple<AllocatorType, pointer> alloc_data_;
- size_type size_ = 0;
+ container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_;
+ SizeType size_ = 0;
};
template <typename T, size_t N, typename A>
class Storage {
public:
- using allocator_type = A;
- using value_type = typename allocator_type::value_type;
- using pointer = typename allocator_type::pointer;
- using const_pointer = typename allocator_type::const_pointer;
- using reference = typename allocator_type::reference;
- using const_reference = typename allocator_type::const_reference;
- using rvalue_reference = typename allocator_type::value_type&&;
- using size_type = typename allocator_type::size_type;
- using difference_type = typename allocator_type::difference_type;
+ using AllocatorTraits = absl::allocator_traits<A>;
+ using allocator_type = typename AllocatorTraits::allocator_type;
+ using value_type = typename AllocatorTraits::value_type;
+ using pointer = typename AllocatorTraits::pointer;
+ using const_pointer = typename AllocatorTraits::const_pointer;
+ using size_type = typename AllocatorTraits::size_type;
+ using difference_type = typename AllocatorTraits::difference_type;
+
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using RValueReference = value_type&&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using MoveIterator = std::move_iterator<iterator>;
- using AllocatorTraits = absl::allocator_traits<allocator_type>;
using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<allocator_type>;
using StorageView = inlined_vector_internal::StorageView<allocator_type>;
@@ -287,8 +300,7 @@ class Storage {
Storage() : metadata_() {}
- explicit Storage(const allocator_type& alloc)
- : metadata_(alloc, /* empty and inlined */ 0) {}
+ explicit Storage(const allocator_type& alloc) : metadata_(alloc, {}) {}
~Storage() {
pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
@@ -414,8 +426,8 @@ class Storage {
void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) {
SetAllocatedData(allocation_tx_ptr->GetData(),
allocation_tx_ptr->GetCapacity());
- allocation_tx_ptr->GetData() = nullptr;
- allocation_tx_ptr->GetCapacity() = 0;
+
+ allocation_tx_ptr->Reset();
}
void MemcpyFrom(const Storage& other_storage) {
@@ -442,9 +454,7 @@ class Storage {
};
struct Inlined {
- using InlinedDataElement =
- absl::aligned_storage_t<sizeof(value_type), alignof(value_type)>;
- InlinedDataElement inlined_data[N];
+ alignas(value_type) char inlined_data[sizeof(value_type[N])];
};
union Data {
@@ -465,18 +475,14 @@ auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size)
assert(GetSize() == 0);
pointer construct_data;
-
if (new_size > GetInlinedCapacity()) {
// Because this is only called from the `InlinedVector` constructors, it's
// safe to take on the allocation with size `0`. If `ConstructElements(...)`
// throws, deallocation will be automatically handled by `~Storage()`.
size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size);
- pointer new_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity);
-
- SetAllocatedData(new_data, new_capacity);
+ construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity);
+ SetAllocatedData(construct_data, new_capacity);
SetIsAllocated();
-
- construct_data = new_data;
} else {
construct_data = GetInlinedData();
}
@@ -503,9 +509,7 @@ auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void {
if (new_size > storage_view.capacity) {
size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size);
- pointer new_data = allocation_tx.Allocate(new_capacity);
-
- construct_loop = {new_data, new_size};
+ construct_loop = {allocation_tx.Allocate(new_capacity), new_size};
destroy_loop = {storage_view.data, storage_view.size};
} else if (new_size > storage_view.size) {
assign_loop = {storage_view.data, storage_view.size};
@@ -539,12 +543,12 @@ template <typename ValueAdapter>
auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void {
StorageView storage_view = MakeStorageView();
- AllocationTransaction allocation_tx(GetAllocPtr());
- ConstructionTransaction construction_tx(GetAllocPtr());
-
IteratorValueAdapter<MoveIterator> move_values(
MoveIterator(storage_view.data));
+ AllocationTransaction allocation_tx(GetAllocPtr());
+ ConstructionTransaction construction_tx(GetAllocPtr());
+
absl::Span<value_type> construct_loop;
absl::Span<value_type> move_construct_loop;
absl::Span<value_type> destroy_loop;
@@ -687,19 +691,17 @@ auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference {
MoveIterator(storage_view.data));
pointer construct_data;
-
if (storage_view.size == storage_view.capacity) {
size_type new_capacity = NextCapacity(storage_view.capacity);
- pointer new_data = allocation_tx.Allocate(new_capacity);
-
- construct_data = new_data;
+ construct_data = allocation_tx.Allocate(new_capacity);
} else {
construct_data = storage_view.data;
}
- pointer end = construct_data + storage_view.size;
+ pointer last_ptr = construct_data + storage_view.size;
- AllocatorTraits::construct(*GetAllocPtr(), end, std::forward<Args>(args)...);
+ AllocatorTraits::construct(*GetAllocPtr(), last_ptr,
+ std::forward<Args>(args)...);
if (allocation_tx.DidAllocate()) {
ABSL_INTERNAL_TRY {
@@ -708,7 +710,7 @@ auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference {
storage_view.size);
}
ABSL_INTERNAL_CATCH_ANY {
- AllocatorTraits::destroy(*GetAllocPtr(), end);
+ AllocatorTraits::destroy(*GetAllocPtr(), last_ptr);
ABSL_INTERNAL_RETHROW;
}
@@ -721,14 +723,12 @@ auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference {
}
AddSize(1);
- return *end;
+ return *last_ptr;
}
template <typename T, size_t N, typename A>
auto Storage<T, N, A>::Erase(const_iterator from, const_iterator to)
-> iterator {
- assert(from != to);
-
StorageView storage_view = MakeStorageView();
size_type erase_size = std::distance(from, to);
@@ -793,12 +793,9 @@ auto Storage<T, N, A>::ShrinkToFit() -> void {
MoveIterator(storage_view.data));
pointer construct_data;
-
if (storage_view.size > GetInlinedCapacity()) {
size_type new_capacity = storage_view.size;
- pointer new_data = allocation_tx.Allocate(new_capacity);
-
- construct_data = new_data;
+ construct_data = allocation_tx.Allocate(new_capacity);
} else {
construct_data = GetInlinedData();
}
@@ -889,7 +886,7 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void {
}
} // namespace inlined_vector_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_
diff --git a/absl/container/internal/layout.h b/absl/container/internal/layout.h
index 3924b8aa..69cc85dd 100644
--- a/absl/container/internal/layout.h
+++ b/absl/container/internal/layout.h
@@ -188,7 +188,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
// A type wrapper that instructs `Layout` to use the specific alignment for the
@@ -735,7 +735,7 @@ class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> {
};
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_
diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc
index 44d84607..8f3628a1 100644
--- a/absl/container/internal/layout_test.cc
+++ b/absl/container/internal/layout_test.cc
@@ -28,7 +28,7 @@
#include "absl/types/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -1563,5 +1563,5 @@ TEST(CompactString, Works) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/node_hash_policy.h b/absl/container/internal/node_hash_policy.h
index d7581360..4617162f 100644
--- a/absl/container/internal/node_hash_policy.h
+++ b/absl/container/internal/node_hash_policy.h
@@ -39,8 +39,10 @@
#include <type_traits>
#include <utility>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class Reference, class Policy>
@@ -84,7 +86,7 @@ struct node_hash_policy {
};
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_
diff --git a/absl/container/internal/node_hash_policy_test.cc b/absl/container/internal/node_hash_policy_test.cc
index d53b7364..84aabba9 100644
--- a/absl/container/internal/node_hash_policy_test.cc
+++ b/absl/container/internal/node_hash_policy_test.cc
@@ -21,7 +21,7 @@
#include "absl/container/internal/hash_policy_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -65,5 +65,5 @@ TEST_F(NodeTest, transfer) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h
index 00caa373..0a02757d 100644
--- a/absl/container/internal/raw_hash_map.h
+++ b/absl/container/internal/raw_hash_map.h
@@ -24,7 +24,7 @@
#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class Policy, class Hash, class Eq, class Alloc>
@@ -110,6 +110,9 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> {
return insert_or_assign(k, v).first;
}
+ // All `try_emplace()` overloads make the same guarantees regarding rvalue
+ // arguments as `std::unordered_map::try_emplace()`, namely that these
+ // functions will not move from rvalue arguments if insertions do not happen.
template <class K = key_type, class... Args,
typename std::enable_if<
!std::is_convertible<K, const_iterator>::value, int>::type = 0,
@@ -188,7 +191,7 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> {
};
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc
index 02e74e21..919ac074 100644
--- a/absl/container/internal/raw_hash_set.cc
+++ b/absl/container/internal/raw_hash_set.cc
@@ -20,7 +20,7 @@
#include "absl/base/config.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
constexpr size_t Group::kWidth;
@@ -44,5 +44,5 @@ bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) {
}
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h
index 7b379d4f..ca7be8d8 100644
--- a/absl/container/internal/raw_hash_set.h
+++ b/absl/container/internal/raw_hash_set.h
@@ -118,7 +118,7 @@
#include "absl/utility/utility.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <size_t Width>
@@ -615,13 +615,17 @@ class raw_hash_set {
iterator() {}
// PRECONDITION: not an end() iterator.
- reference operator*() const { return PolicyTraits::element(slot_); }
+ reference operator*() const {
+ assert_is_full();
+ return PolicyTraits::element(slot_);
+ }
// PRECONDITION: not an end() iterator.
pointer operator->() const { return &operator*(); }
// PRECONDITION: not an end() iterator.
iterator& operator++() {
+ assert_is_full();
++ctrl_;
++slot_;
skip_empty_or_deleted();
@@ -635,6 +639,8 @@ class raw_hash_set {
}
friend bool operator==(const iterator& a, const iterator& b) {
+ a.assert_is_valid();
+ b.assert_is_valid();
return a.ctrl_ == b.ctrl_;
}
friend bool operator!=(const iterator& a, const iterator& b) {
@@ -645,6 +651,11 @@ class raw_hash_set {
iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end()
iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {}
+ void assert_is_full() const { assert(IsFull(*ctrl_)); }
+ void assert_is_valid() const {
+ assert(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel);
+ }
+
void skip_empty_or_deleted() {
while (IsEmptyOrDeleted(*ctrl_)) {
// ctrl is not necessarily aligned to Group::kWidth. It is also likely
@@ -658,7 +669,7 @@ class raw_hash_set {
}
ctrl_t* ctrl_ = nullptr;
- // To avoid uninitialized member warnigs, put slot_ in an anonymous union.
+ // To avoid uninitialized member warnings, put slot_ in an anonymous union.
// The member is not initialized on singleton and end iterators.
union {
slot_type* slot_;
@@ -939,8 +950,11 @@ class raw_hash_set {
//
// flat_hash_map<std::string, int> m;
// m.insert(std::make_pair("abc", 42));
+ // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc
+ // bug.
template <class T, RequiresInsertable<T> = 0,
- typename std::enable_if<IsDecomposable<T>::value, int>::type = 0,
+ class T2 = T,
+ typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0,
T* = nullptr>
std::pair<iterator, bool> insert(T&& value) {
return emplace(std::forward<T>(value));
@@ -976,8 +990,10 @@ class raw_hash_set {
return emplace(std::move(value));
}
- template <class T, RequiresInsertable<T> = 0,
- typename std::enable_if<IsDecomposable<T>::value, int>::type = 0,
+ // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc
+ // bug.
+ template <class T, RequiresInsertable<T> = 0, class T2 = T,
+ typename std::enable_if<IsDecomposable<T2>::value, int>::type = 0,
T* = nullptr>
iterator insert(const_iterator, T&& value) {
return insert(std::forward<T>(value)).first;
@@ -1051,8 +1067,7 @@ class raw_hash_set {
template <class... Args, typename std::enable_if<
!IsDecomposable<Args...>::value, int>::type = 0>
std::pair<iterator, bool> emplace(Args&&... args) {
- typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type
- raw;
+ 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)...);
@@ -1068,10 +1083,15 @@ class raw_hash_set {
// Extension API: support for lazy emplace.
//
// Looks up key in the table. If found, returns the iterator to the element.
- // Otherwise calls f with one argument of type raw_hash_set::constructor. f
- // MUST call raw_hash_set::constructor with arguments as if a
- // raw_hash_set::value_type is constructed, otherwise the behavior is
- // undefined.
+ // Otherwise calls `f` with one argument of type `raw_hash_set::constructor`.
+ //
+ // `f` must abide by several restrictions:
+ // - it MUST call `raw_hash_set::constructor` with arguments as if a
+ // `raw_hash_set::value_type` is constructed,
+ // - it MUST NOT access the container before the call to
+ // `raw_hash_set::constructor`, and
+ // - it MUST NOT erase the lazily emplaced element.
+ // Doing any of these is undefined behavior.
//
// For example:
//
@@ -1134,15 +1154,16 @@ class raw_hash_set {
}
// Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`,
- // this method returns void to reduce algorithmic complexity to O(1). In
- // order to erase while iterating across a map, use the following idiom (which
- // also works for standard containers):
+ // this method returns void to reduce algorithmic complexity to O(1). The
+ // iterator is invalidated, so any increment should be done before calling
+ // erase. In order to erase while iterating across a map, use the following
+ // idiom (which also works for standard containers):
//
// for (auto it = m.begin(), end = m.end(); it != end;) {
+ // // `erase()` will invalidate `it`, so advance `it` first.
+ // auto copy_it = it++;
// if (<pred>) {
- // m.erase(it++);
- // } else {
- // ++it;
+ // m.erase(copy_it);
// }
// }
void erase(const_iterator cit) { erase(cit.inner_); }
@@ -1150,7 +1171,7 @@ class raw_hash_set {
// This overload is necessary because otherwise erase<K>(const K&) would be
// a better match if non-const iterator is passed as an argument.
void erase(iterator it) {
- assert(it != end());
+ it.assert_is_full();
PolicyTraits::destroy(&alloc_ref(), it.slot_);
erase_meta_only(it);
}
@@ -1167,12 +1188,14 @@ class raw_hash_set {
template <typename H, typename E>
void merge(raw_hash_set<Policy, H, E, Alloc>& src) { // NOLINT
assert(this != &src);
- for (auto it = src.begin(), e = src.end(); it != e; ++it) {
+ 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_))
.second) {
src.erase_meta_only(it);
}
+ it = next;
}
}
@@ -1182,6 +1205,7 @@ class raw_hash_set {
}
node_type extract(const_iterator position) {
+ position.inner_.assert_is_full();
auto node =
CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_);
erase_meta_only(position);
@@ -1531,8 +1555,7 @@ class raw_hash_set {
// mark target as FULL
// repeat procedure for current slot with moved from element (target)
ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_);
- typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type
- raw;
+ alignas(slot_type) unsigned char raw[sizeof(slot_type)];
size_t total_probe_length = 0;
slot_type* slot = reinterpret_cast<slot_type*>(&raw);
for (size_t i = 0; i != capacity_; ++i) {
@@ -1781,6 +1804,17 @@ class raw_hash_set {
settings_{0, hasher{}, key_equal{}, allocator_type{}};
};
+// Erases all elements that satisfy the predicate `pred` from the container `c`.
+template <typename P, typename H, typename E, typename A, typename Predicate>
+void EraseIf(Predicate pred, raw_hash_set<P, H, E, A>* c) {
+ for (auto it = c->begin(), last = c->end(); it != last;) {
+ auto copy_it = it++;
+ if (pred(*copy_it)) {
+ c->erase(copy_it);
+ }
+ }
+}
+
namespace hashtable_debug_internal {
template <typename Set>
struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> {
@@ -1842,7 +1876,7 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> {
} // namespace hashtable_debug_internal
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_
diff --git a/absl/container/internal/raw_hash_set_allocator_test.cc b/absl/container/internal/raw_hash_set_allocator_test.cc
index 5188b3ae..7ac4b9f7 100644
--- a/absl/container/internal/raw_hash_set_allocator_test.cc
+++ b/absl/container/internal/raw_hash_set_allocator_test.cc
@@ -20,7 +20,7 @@
#include "absl/container/internal/tracked.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -426,5 +426,5 @@ TEST_F(PropagateOnAll, Swap) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc
index 2783f5c4..a96ae68a 100644
--- a/absl/container/internal/raw_hash_set_test.cc
+++ b/absl/container/internal/raw_hash_set_test.cc
@@ -35,7 +35,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
struct RawHashSetTestOnlyAccess {
@@ -418,53 +418,6 @@ TEST(Table, Empty) {
EXPECT_TRUE(t.empty());
}
-#ifdef __GNUC__
-template <class T>
-ABSL_ATTRIBUTE_ALWAYS_INLINE inline void DoNotOptimize(const T& v) {
- asm volatile("" : : "r,m"(v) : "memory");
-}
-#endif
-
-TEST(Table, Prefetch) {
- IntTable t;
- t.emplace(1);
- // Works for both present and absent keys.
- t.prefetch(1);
- t.prefetch(2);
-
- // Do not run in debug mode, when prefetch is not implemented, or when
- // sanitizers are enabled, or on WebAssembly.
-#if defined(NDEBUG) && defined(__GNUC__) && defined(__x86_64__) && \
- !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
- !defined(THREAD_SANITIZER) && !defined(UNDEFINED_BEHAVIOR_SANITIZER) && \
- !defined(__EMSCRIPTEN__)
- const auto now = [] { return absl::base_internal::CycleClock::Now(); };
-
- // Make size enough to not fit in L2 cache (16.7 Mb)
- static constexpr int size = 1 << 22;
- for (int i = 0; i < size; ++i) t.insert(i);
-
- int64_t no_prefetch = 0, prefetch = 0;
- for (int iter = 0; iter < 10; ++iter) {
- int64_t time = now();
- for (int i = 0; i < size; ++i) {
- DoNotOptimize(t.find(i));
- }
- no_prefetch += now() - time;
-
- time = now();
- for (int i = 0; i < size; ++i) {
- t.prefetch(i + 20);
- DoNotOptimize(t.find(i));
- }
- prefetch += now() - time;
- }
-
- // no_prefetch is at least 30% slower.
- EXPECT_GE(1.0 * no_prefetch / prefetch, 1.3);
-#endif
-}
-
TEST(Table, LookupEmpty) {
IntTable t;
auto it = t.find(0);
@@ -1838,10 +1791,11 @@ TEST(TableDeathTest, EraseOfEndAsserts) {
IntTable t;
// Extra simple "regexp" as regexp support is highly varied across platforms.
- constexpr char kDeathMsg[] = "it != end";
+ constexpr char kDeathMsg[] = "IsFull";
EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg);
}
+#if defined(ABSL_HASHTABLEZ_SAMPLE)
TEST(RawHashSamplerTest, Sample) {
// Enable the feature even if the prod default is off.
SetHashtablezEnabled(true);
@@ -1862,6 +1816,7 @@ TEST(RawHashSamplerTest, Sample) {
EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
0.01, 0.005);
}
+#endif // ABSL_HASHTABLEZ_SAMPLER
TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) {
// Enable the feature even if the prod default is off.
@@ -1912,5 +1867,5 @@ TEST(Sanitizer, PoisoningOnErase) {
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/test_instance_tracker.cc b/absl/container/internal/test_instance_tracker.cc
index f4b283fd..f9947f04 100644
--- a/absl/container/internal/test_instance_tracker.cc
+++ b/absl/container/internal/test_instance_tracker.cc
@@ -15,7 +15,7 @@
#include "absl/container/internal/test_instance_tracker.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace test_internal {
int BaseCountedInstance::num_instances_ = 0;
int BaseCountedInstance::num_live_instances_ = 0;
@@ -25,5 +25,5 @@ int BaseCountedInstance::num_swaps_ = 0;
int BaseCountedInstance::num_comparisons_ = 0;
} // namespace test_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/test_instance_tracker.h b/absl/container/internal/test_instance_tracker.h
index ab7f9f22..5ff6fd71 100644
--- a/absl/container/internal/test_instance_tracker.h
+++ b/absl/container/internal/test_instance_tracker.h
@@ -21,7 +21,7 @@
#include "absl/types/compare.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace test_internal {
// A type that counts number of occurrences of the type, the live occurrences of
@@ -268,7 +268,7 @@ class MovableOnlyInstance : public BaseCountedInstance {
};
} // namespace test_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
diff --git a/absl/container/internal/tracked.h b/absl/container/internal/tracked.h
index e9e6b95d..29f5829f 100644
--- a/absl/container/internal/tracked.h
+++ b/absl/container/internal/tracked.h
@@ -16,11 +16,14 @@
#define ABSL_CONTAINER_INTERNAL_TRACKED_H_
#include <stddef.h>
+
#include <memory>
#include <utility>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
// A class that tracks its copies and moves so that it can be queried in tests.
@@ -74,7 +77,7 @@ class Tracked {
};
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_TRACKED_H_
diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h
index b64b5520..76ee95e6 100644
--- a/absl/container/internal/unordered_map_constructor_test.h
+++ b/absl/container/internal/unordered_map_constructor_test.h
@@ -24,7 +24,7 @@
#include "absl/container/internal/hash_policy_testing.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class UnordMap>
@@ -483,7 +483,7 @@ REGISTER_TYPED_TEST_CASE_P(
AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
diff --git a/absl/container/internal/unordered_map_lookup_test.h b/absl/container/internal/unordered_map_lookup_test.h
index 9ad78a79..e76421e5 100644
--- a/absl/container/internal/unordered_map_lookup_test.h
+++ b/absl/container/internal/unordered_map_lookup_test.h
@@ -21,7 +21,7 @@
#include "absl/container/internal/hash_policy_testing.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class UnordMap>
@@ -111,7 +111,7 @@ REGISTER_TYPED_TEST_CASE_P(LookupTest, At, OperatorBracket, Count, Find,
EqualRange);
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_
diff --git a/absl/container/internal/unordered_map_members_test.h b/absl/container/internal/unordered_map_members_test.h
index c4600405..7d48cdb8 100644
--- a/absl/container/internal/unordered_map_members_test.h
+++ b/absl/container/internal/unordered_map_members_test.h
@@ -21,7 +21,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class UnordMap>
@@ -81,7 +81,7 @@ TYPED_TEST_P(MembersTest, BeginEnd) {
REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd);
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
diff --git a/absl/container/internal/unordered_map_modifiers_test.h b/absl/container/internal/unordered_map_modifiers_test.h
index 89dd7894..b8c513f1 100644
--- a/absl/container/internal/unordered_map_modifiers_test.h
+++ b/absl/container/internal/unordered_map_modifiers_test.h
@@ -15,13 +15,15 @@
#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_
#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_
+#include <memory>
+
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/internal/hash_generator_testing.h"
#include "absl/container/internal/hash_policy_testing.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class UnordMap>
@@ -268,8 +270,47 @@ REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
Emplace, EmplaceHint, TryEmplace, TryEmplaceHint,
Erase, EraseRange, EraseKey, Swap);
+template <typename Type>
+struct is_unique_ptr : std::false_type {};
+
+template <typename Type>
+struct is_unique_ptr<std::unique_ptr<Type>> : std::true_type {};
+
+template <class UnordMap>
+class UniquePtrModifiersTest : public ::testing::Test {
+ protected:
+ UniquePtrModifiersTest() {
+ static_assert(is_unique_ptr<typename UnordMap::mapped_type>::value,
+ "UniquePtrModifiersTyest may only be called with a "
+ "std::unique_ptr value type.");
+ }
+};
+
+TYPED_TEST_SUITE_P(UniquePtrModifiersTest);
+
+// Test that we do not move from rvalue arguments if an insertion does not
+// happen.
+TYPED_TEST_P(UniquePtrModifiersTest, TryEmplace) {
+#ifdef UNORDERED_MAP_CXX17
+ using T = hash_internal::GeneratedType<TypeParam>;
+ using V = typename TypeParam::mapped_type;
+ T val = hash_internal::Generator<T>()();
+ TypeParam m;
+ auto p = m.try_emplace(val.first, std::move(val.second));
+ EXPECT_TRUE(p.second);
+ // A moved from std::unique_ptr is guaranteed to be nullptr.
+ EXPECT_EQ(val.second, nullptr);
+ T val2 = {val.first, hash_internal::Generator<V>()()};
+ p = m.try_emplace(val2.first, std::move(val2.second));
+ EXPECT_FALSE(p.second);
+ EXPECT_NE(val2.second, nullptr);
+#endif
+}
+
+REGISTER_TYPED_TEST_SUITE_P(UniquePtrModifiersTest, TryEmplace);
+
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_
diff --git a/absl/container/internal/unordered_map_test.cc b/absl/container/internal/unordered_map_test.cc
index 51a90af8..9cbf512f 100644
--- a/absl/container/internal/unordered_map_test.cc
+++ b/absl/container/internal/unordered_map_test.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <memory>
#include <unordered_map>
#include "absl/container/internal/unordered_map_constructor_test.h"
@@ -20,7 +21,7 @@
#include "absl/container/internal/unordered_map_modifiers_test.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -36,7 +37,14 @@ INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, LookupTest, MapTypes);
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, MembersTest, MapTypes);
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ModifiersTest, MapTypes);
+using UniquePtrMapTypes = ::testing::Types<std::unordered_map<
+ int, std::unique_ptr<int>, StatefulTestingHash, StatefulTestingEqual,
+ Alloc<std::pair<const int, std::unique_ptr<int>>>>>;
+
+INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, UniquePtrModifiersTest,
+ UniquePtrMapTypes);
+
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/internal/unordered_set_constructor_test.h b/absl/container/internal/unordered_set_constructor_test.h
index ac73a896..41165b05 100644
--- a/absl/container/internal/unordered_set_constructor_test.h
+++ b/absl/container/internal/unordered_set_constructor_test.h
@@ -26,7 +26,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class UnordMap>
@@ -490,7 +490,7 @@ REGISTER_TYPED_TEST_CASE_P(
AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
diff --git a/absl/container/internal/unordered_set_lookup_test.h b/absl/container/internal/unordered_set_lookup_test.h
index 722fb1c2..8f2f4b20 100644
--- a/absl/container/internal/unordered_set_lookup_test.h
+++ b/absl/container/internal/unordered_set_lookup_test.h
@@ -21,7 +21,7 @@
#include "absl/container/internal/hash_policy_testing.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class UnordSet>
@@ -85,7 +85,7 @@ TYPED_TEST_P(LookupTest, EqualRange) {
REGISTER_TYPED_TEST_CASE_P(LookupTest, Count, Find, EqualRange);
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_
diff --git a/absl/container/internal/unordered_set_members_test.h b/absl/container/internal/unordered_set_members_test.h
index 756a95cb..4c5e104a 100644
--- a/absl/container/internal/unordered_set_members_test.h
+++ b/absl/container/internal/unordered_set_members_test.h
@@ -21,7 +21,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class UnordSet>
@@ -80,7 +80,7 @@ TYPED_TEST_P(MembersTest, BeginEnd) {
REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd);
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_
diff --git a/absl/container/internal/unordered_set_modifiers_test.h b/absl/container/internal/unordered_set_modifiers_test.h
index d3e534d3..26be58d9 100644
--- a/absl/container/internal/unordered_set_modifiers_test.h
+++ b/absl/container/internal/unordered_set_modifiers_test.h
@@ -21,7 +21,7 @@
#include "absl/container/internal/hash_policy_testing.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class UnordSet>
@@ -184,7 +184,7 @@ REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint,
EraseKey, Swap);
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_
diff --git a/absl/container/internal/unordered_set_test.cc b/absl/container/internal/unordered_set_test.cc
index 2356e187..a134b539 100644
--- a/absl/container/internal/unordered_set_test.cc
+++ b/absl/container/internal/unordered_set_test.cc
@@ -20,7 +20,7 @@
#include "absl/container/internal/unordered_set_modifiers_test.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
@@ -37,5 +37,5 @@ INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ModifiersTest, SetTypes);
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h
index addf120f..fccea184 100644
--- a/absl/container/node_hash_map.h
+++ b/absl/container/node_hash_map.h
@@ -48,7 +48,7 @@
#include "absl/memory/memory.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <class Key, class Value>
class NodeHashMapPolicy;
@@ -352,6 +352,10 @@ class node_hash_map
// Inserts (via copy or move) the element of the specified key into the
// `node_hash_map` using the position of `hint` as a non-binding suggestion
// for where to begin the insertion search.
+ //
+ // All `try_emplace()` overloads make the same guarantees regarding rvalue
+ // arguments as `std::unordered_map::try_emplace()`, namely that these
+ // functions will not move from rvalue arguments if insertions do not happen.
using Base::try_emplace;
// node_hash_map::extract()
@@ -518,6 +522,15 @@ class node_hash_map
void resize(typename Base::size_type hint) { this->rehash(hint); }
};
+// erase_if(node_hash_map<>, Pred)
+//
+// Erases all elements that satisfy the predicate `pred` from the container `c`.
+template <typename K, typename V, typename H, typename E, typename A,
+ typename Predicate>
+void erase_if(node_hash_map<K, V, H, E, A>& c, Predicate pred) {
+ container_internal::EraseIf(pred, &c);
+}
+
namespace container_internal {
template <class Key, class Value>
@@ -578,7 +591,7 @@ struct IsUnorderedContainer<
} // namespace container_algorithm_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_NODE_HASH_MAP_H_
diff --git a/absl/container/node_hash_map_test.cc b/absl/container/node_hash_map_test.cc
index 7ce7ca02..5d74b814 100644
--- a/absl/container/node_hash_map_test.cc
+++ b/absl/container/node_hash_map_test.cc
@@ -21,11 +21,12 @@
#include "absl/container/internal/unordered_map_modifiers_test.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
using ::testing::Field;
+using ::testing::IsEmpty;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
@@ -216,7 +217,44 @@ TEST(NodeHashMap, MergeExtractInsert) {
EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70), Elem(17, 23)));
}
+bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; }
+
+TEST(NodeHashMap, EraseIf) {
+ // Erase all elements.
+ {
+ node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s, [](std::pair<const int, int>) { return true; });
+ EXPECT_THAT(s, IsEmpty());
+ }
+ // Erase no elements.
+ {
+ node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s, [](std::pair<const int, int>) { return false; });
+ EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3),
+ Pair(4, 4), Pair(5, 5)));
+ }
+ // Erase specific elements.
+ {
+ node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s,
+ [](std::pair<const int, int> kvp) { return kvp.first % 2 == 1; });
+ EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4)));
+ }
+ // Predicate is function reference.
+ {
+ node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s, FirstIsEven);
+ EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
+ }
+ // Predicate is function pointer.
+ {
+ node_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
+ erase_if(s, &FirstIsEven);
+ EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
+ }
+}
+
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h
index 103d32d2..ad54b6dc 100644
--- a/absl/container/node_hash_set.h
+++ b/absl/container/node_hash_set.h
@@ -44,7 +44,7 @@
#include "absl/memory/memory.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
template <typename T>
struct NodeHashSetPolicy;
@@ -77,7 +77,7 @@ struct NodeHashSetPolicy;
//
// // Create a node hash set of three strings
// absl::node_hash_map<std::string, std::string> ducks =
-// {"huey", "dewey"}, "louie"};
+// {"huey", "dewey", "louie"};
//
// // Insert a new element into the node hash map
// ducks.insert("donald"};
@@ -111,7 +111,7 @@ class node_hash_set
// * Initializer List constructor
//
// absl::node_hash_set<std::string> set2 =
- // {{"huey"}, {"dewey"}, {"louie"},};
+ // {{"huey"}, {"dewey"}, {"louie"}};
//
// * Copy constructor
//
@@ -435,6 +435,14 @@ class node_hash_set
void resize(typename Base::size_type hint) { this->rehash(hint); }
};
+// erase_if(node_hash_set<>, Pred)
+//
+// Erases all elements that satisfy the predicate `pred` from the container `c`.
+template <typename T, typename H, typename E, typename A, typename Predicate>
+void erase_if(node_hash_set<T, H, E, A>& c, Predicate pred) {
+ container_internal::EraseIf(pred, &c);
+}
+
namespace container_internal {
template <class T>
@@ -484,7 +492,7 @@ struct IsUnorderedContainer<absl::node_hash_set<Key, Hash, KeyEqual, Allocator>>
: std::true_type {};
} // namespace container_algorithm_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CONTAINER_NODE_HASH_SET_H_
diff --git a/absl/container/node_hash_set_test.cc b/absl/container/node_hash_set_test.cc
index 65d125ed..7ddad202 100644
--- a/absl/container/node_hash_set_test.cc
+++ b/absl/container/node_hash_set_test.cc
@@ -20,11 +20,12 @@
#include "absl/container/internal/unordered_set_modifiers_test.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace container_internal {
namespace {
using ::absl::container_internal::hash_internal::Enum;
using ::absl::container_internal::hash_internal::EnumClass;
+using ::testing::IsEmpty;
using ::testing::Pointee;
using ::testing::UnorderedElementsAre;
@@ -101,7 +102,42 @@ TEST(NodeHashSet, MergeExtractInsert) {
EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23)));
}
+bool IsEven(int k) { return k % 2 == 0; }
+
+TEST(NodeHashSet, EraseIf) {
+ // Erase all elements.
+ {
+ node_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, [](int) { return true; });
+ EXPECT_THAT(s, IsEmpty());
+ }
+ // Erase no elements.
+ {
+ node_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, [](int) { return false; });
+ EXPECT_THAT(s, UnorderedElementsAre(1, 2, 3, 4, 5));
+ }
+ // Erase specific elements.
+ {
+ node_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, [](int k) { return k % 2 == 1; });
+ EXPECT_THAT(s, UnorderedElementsAre(2, 4));
+ }
+ // Predicate is function reference.
+ {
+ node_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, IsEven);
+ EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5));
+ }
+ // Predicate is function pointer.
+ {
+ node_hash_set<int> s = {1, 2, 3, 4, 5};
+ erase_if(s, &IsEven);
+ EXPECT_THAT(s, UnorderedElementsAre(1, 3, 5));
+ }
+}
+
} // namespace
} // namespace container_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake
index 4ece4c6f..77d4ace8 100644
--- a/absl/copts/AbseilConfigureCopts.cmake
+++ b/absl/copts/AbseilConfigureCopts.cmake
@@ -5,25 +5,46 @@ set(ABSL_LSAN_LINKOPTS "")
set(ABSL_HAVE_LSAN OFF)
set(ABSL_DEFAULT_LINKOPTS "")
+if (BUILD_SHARED_LIBS AND MSVC)
+ set(ABSL_BUILD_DLL TRUE)
+ set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
+else()
+ set(ABSL_BUILD_DLL FALSE)
+endif()
+
+if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64")
+ if (MSVC)
+ set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}")
+ else()
+ set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}")
+ endif()
+elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm.*|aarch64")
+ if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
+ set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM64_FLAGS}")
+ elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
+ set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM32_FLAGS}")
+ else()
+ message(WARNING "Value of CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) is not supported.")
+ endif()
+else()
+ message(WARNING "Value of CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) is unknown and cannot be used to set ABSL_RANDOM_RANDEN_COPTS")
+ set(ABSL_RANDOM_RANDEN_COPTS "")
+endif()
+
+
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}")
- set(ABSL_EXCEPTIONS_FLAG "${ABSL_GCC_EXCEPTIONS_FLAGS}")
- set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}")
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
# MATCHES so we get both Clang and AppleClang
if(MSVC)
# clang-cl is half MSVC, half LLVM
set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_FLAGS};${ABSL_CLANG_CL_TEST_FLAGS}")
- set(ABSL_EXCEPTIONS_FLAG "${ABSL_CLANG_CL_EXCEPTIONS_FLAGS}")
set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")
- set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}")
else()
set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}")
- set(ABSL_EXCEPTIONS_FLAG "${ABSL_LLVM_EXCEPTIONS_FLAGS}")
- set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}")
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# AppleClang doesn't have lsan
# https://developer.apple.com/documentation/code_diagnostics
@@ -36,20 +57,13 @@ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}")
set(ABSL_TEST_COPTS "${ABSL_MSVC_FLAGS};${ABSL_MSVC_TEST_FLAGS}")
- set(ABSL_EXCEPTIONS_FLAG "${ABSL_MSVC_EXCEPTIONS_FLAGS}")
set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}")
- set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}")
else()
message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER}. Building with no default flags")
set(ABSL_DEFAULT_COPTS "")
set(ABSL_TEST_COPTS "")
- set(ABSL_EXCEPTIONS_FLAG "")
- set(ABSL_RANDOM_RANDEN_COPTS "")
endif()
-# This flag is used internally for Bazel builds and is kept here for consistency
-set(ABSL_EXCEPTIONS_FLAG_LINKOPTS "")
-
if("${CMAKE_CXX_STANDARD}" EQUAL 98)
message(FATAL_ERROR "Abseil requires at least C++11")
elseif(NOT "${CMAKE_CXX_STANDARD}")
diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake
index 01bd40b2..7ef6339b 100644
--- a/absl/copts/GENERATED_AbseilCopts.cmake
+++ b/absl/copts/GENERATED_AbseilCopts.cmake
@@ -3,12 +3,6 @@
# (1) Edit absl/copts/copts.py.
# (2) Run `python <path_to_absl>/copts/generate_copts.py`.
-list(APPEND ABSL_CLANG_CL_EXCEPTIONS_FLAGS
- "/U_HAS_EXCEPTIONS"
- "/D_HAS_EXCEPTIONS=1"
- "/EHsc"
-)
-
list(APPEND ABSL_CLANG_CL_FLAGS
"/W3"
"-Wno-c++98-compat-pedantic"
@@ -29,7 +23,6 @@ list(APPEND ABSL_CLANG_CL_FLAGS
"-Wno-gcc-compat"
"-Wno-global-constructors"
"-Wno-exit-time-destructors"
- "-Wno-nested-anon-types"
"-Wno-non-modular-include-in-module"
"-Wno-old-style-cast"
"-Wno-range-loop-analysis"
@@ -80,10 +73,6 @@ list(APPEND ABSL_CLANG_CL_TEST_FLAGS
"-Wno-gnu-zero-variadic-macro-arguments"
)
-list(APPEND ABSL_GCC_EXCEPTIONS_FLAGS
- "-fexceptions"
-)
-
list(APPEND ABSL_GCC_FLAGS
"-Wall"
"-Wextra"
@@ -111,10 +100,6 @@ list(APPEND ABSL_GCC_TEST_FLAGS
"-Wno-unused-private-field"
)
-list(APPEND ABSL_LLVM_EXCEPTIONS_FLAGS
- "-fexceptions"
-)
-
list(APPEND ABSL_LLVM_FLAGS
"-Wall"
"-Wextra"
@@ -137,7 +122,6 @@ list(APPEND ABSL_LLVM_FLAGS
"-Wno-gcc-compat"
"-Wno-global-constructors"
"-Wno-exit-time-destructors"
- "-Wno-nested-anon-types"
"-Wno-non-modular-include-in-module"
"-Wno-old-style-cast"
"-Wno-range-loop-analysis"
@@ -183,12 +167,6 @@ list(APPEND ABSL_LLVM_TEST_FLAGS
"-Wno-gnu-zero-variadic-macro-arguments"
)
-list(APPEND ABSL_MSVC_EXCEPTIONS_FLAGS
- "/U_HAS_EXCEPTIONS"
- "/D_HAS_EXCEPTIONS=1"
- "/EHsc"
-)
-
list(APPEND ABSL_MSVC_FLAGS
"/W3"
"/DNOMINMAX"
@@ -196,6 +174,7 @@ list(APPEND ABSL_MSVC_FLAGS
"/D_CRT_SECURE_NO_WARNINGS"
"/D_SCL_SECURE_NO_WARNINGS"
"/D_ENABLE_EXTENDED_ALIGNED_STORAGE"
+ "/bigobj"
"/wd4005"
"/wd4068"
"/wd4180"
@@ -226,8 +205,6 @@ list(APPEND ABSL_RANDOM_HWAES_ARM64_FLAGS
)
list(APPEND ABSL_RANDOM_HWAES_MSVC_X64_FLAGS
- "/O2"
- "/Ob2"
)
list(APPEND ABSL_RANDOM_HWAES_X64_FLAGS
diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl
index 82f332f4..3cc48784 100644
--- a/absl/copts/GENERATED_copts.bzl
+++ b/absl/copts/GENERATED_copts.bzl
@@ -4,12 +4,6 @@
(2) Run `python <path_to_absl>/copts/generate_copts.py`.
"""
-ABSL_CLANG_CL_EXCEPTIONS_FLAGS = [
- "/U_HAS_EXCEPTIONS",
- "/D_HAS_EXCEPTIONS=1",
- "/EHsc",
-]
-
ABSL_CLANG_CL_FLAGS = [
"/W3",
"-Wno-c++98-compat-pedantic",
@@ -30,7 +24,6 @@ ABSL_CLANG_CL_FLAGS = [
"-Wno-gcc-compat",
"-Wno-global-constructors",
"-Wno-exit-time-destructors",
- "-Wno-nested-anon-types",
"-Wno-non-modular-include-in-module",
"-Wno-old-style-cast",
"-Wno-range-loop-analysis",
@@ -81,10 +74,6 @@ ABSL_CLANG_CL_TEST_FLAGS = [
"-Wno-gnu-zero-variadic-macro-arguments",
]
-ABSL_GCC_EXCEPTIONS_FLAGS = [
- "-fexceptions",
-]
-
ABSL_GCC_FLAGS = [
"-Wall",
"-Wextra",
@@ -112,10 +101,6 @@ ABSL_GCC_TEST_FLAGS = [
"-Wno-unused-private-field",
]
-ABSL_LLVM_EXCEPTIONS_FLAGS = [
- "-fexceptions",
-]
-
ABSL_LLVM_FLAGS = [
"-Wall",
"-Wextra",
@@ -138,7 +123,6 @@ ABSL_LLVM_FLAGS = [
"-Wno-gcc-compat",
"-Wno-global-constructors",
"-Wno-exit-time-destructors",
- "-Wno-nested-anon-types",
"-Wno-non-modular-include-in-module",
"-Wno-old-style-cast",
"-Wno-range-loop-analysis",
@@ -184,12 +168,6 @@ ABSL_LLVM_TEST_FLAGS = [
"-Wno-gnu-zero-variadic-macro-arguments",
]
-ABSL_MSVC_EXCEPTIONS_FLAGS = [
- "/U_HAS_EXCEPTIONS",
- "/D_HAS_EXCEPTIONS=1",
- "/EHsc",
-]
-
ABSL_MSVC_FLAGS = [
"/W3",
"/DNOMINMAX",
@@ -197,6 +175,7 @@ ABSL_MSVC_FLAGS = [
"/D_CRT_SECURE_NO_WARNINGS",
"/D_SCL_SECURE_NO_WARNINGS",
"/D_ENABLE_EXTENDED_ALIGNED_STORAGE",
+ "/bigobj",
"/wd4005",
"/wd4068",
"/wd4180",
@@ -227,8 +206,6 @@ ABSL_RANDOM_HWAES_ARM64_FLAGS = [
]
ABSL_RANDOM_HWAES_MSVC_X64_FLAGS = [
- "/O2",
- "/Ob2",
]
ABSL_RANDOM_HWAES_X64_FLAGS = [
diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl
index 8c4efe77..9dd6bd0a 100644
--- a/absl/copts/configure_copts.bzl
+++ b/absl/copts/configure_copts.bzl
@@ -6,13 +6,12 @@ change Abseil copts, edit absl/copts/copts.py
load(
"//absl:copts/GENERATED_copts.bzl",
- "ABSL_GCC_EXCEPTIONS_FLAGS",
+ "ABSL_CLANG_CL_FLAGS",
+ "ABSL_CLANG_CL_TEST_FLAGS",
"ABSL_GCC_FLAGS",
"ABSL_GCC_TEST_FLAGS",
- "ABSL_LLVM_EXCEPTIONS_FLAGS",
"ABSL_LLVM_FLAGS",
"ABSL_LLVM_TEST_FLAGS",
- "ABSL_MSVC_EXCEPTIONS_FLAGS",
"ABSL_MSVC_FLAGS",
"ABSL_MSVC_LINKOPTS",
"ABSL_MSVC_TEST_FLAGS",
@@ -36,16 +35,6 @@ ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({
"//conditions:default": ABSL_GCC_TEST_FLAGS,
})
-ABSL_EXCEPTIONS_FLAG = select({
- "//absl:windows": ABSL_MSVC_EXCEPTIONS_FLAGS,
- "//absl:llvm_compiler": ABSL_LLVM_EXCEPTIONS_FLAGS,
- "//conditions:default": ABSL_GCC_EXCEPTIONS_FLAGS,
-})
-
-ABSL_EXCEPTIONS_FLAG_LINKOPTS = select({
- "//conditions:default": [],
-})
-
ABSL_DEFAULT_LINKOPTS = select({
"//absl:windows": ABSL_MSVC_LINKOPTS,
"//conditions:default": [],
diff --git a/absl/copts/copts.py b/absl/copts/copts.py
index 068abceb..704ef234 100644
--- a/absl/copts/copts.py
+++ b/absl/copts/copts.py
@@ -56,7 +56,6 @@ LLVM_DISABLE_WARNINGS_FLAGS = [
"-Wno-global-constructors",
"-Wno-exit-time-destructors",
###
- "-Wno-nested-anon-types",
"-Wno-non-modular-include-in-module",
"-Wno-old-style-cast",
# Warns on preferred usage of non-POD types such as string_view
@@ -109,12 +108,6 @@ LLVM_TEST_DISABLE_WARNINGS_FLAGS = [
"-Wno-gnu-zero-variadic-macro-arguments",
]
-MSVC_STYLE_EXCEPTIONS_FLAGS = [
- "/U_HAS_EXCEPTIONS",
- "/D_HAS_EXCEPTIONS=1",
- "/EHsc"
-]
-
MSVC_DEFINES = [
"/DNOMINMAX", # Don't define min and max macros (windows.h)
# Don't bloat namespace with incompatible winsock versions.
@@ -157,20 +150,18 @@ COPT_VARS = {
"-Wno-unused-parameter",
"-Wno-unused-private-field",
],
- "ABSL_GCC_EXCEPTIONS_FLAGS": ["-fexceptions"],
"ABSL_LLVM_FLAGS":
LLVM_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS,
"ABSL_LLVM_TEST_FLAGS":
LLVM_TEST_DISABLE_WARNINGS_FLAGS,
- "ABSL_LLVM_EXCEPTIONS_FLAGS": ["-fexceptions"],
"ABSL_CLANG_CL_FLAGS":
(MSVC_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS + MSVC_DEFINES),
"ABSL_CLANG_CL_TEST_FLAGS":
LLVM_TEST_DISABLE_WARNINGS_FLAGS,
- "ABSL_CLANG_CL_EXCEPTIONS_FLAGS":
- MSVC_STYLE_EXCEPTIONS_FLAGS,
"ABSL_MSVC_FLAGS":
MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES + [
+ # Increase the number of sections available in object files
+ "/bigobj",
"/wd4005", # macro-redefinition
"/wd4068", # unknown pragma
# qualifier applied to function type has no meaning; ignored
@@ -191,8 +182,6 @@ COPT_VARS = {
"/wd4996", # use of deprecated symbol
"/DNOMINMAX", # disable the min() and max() macros from <windows.h>
],
- "ABSL_MSVC_EXCEPTIONS_FLAGS":
- MSVC_STYLE_EXCEPTIONS_FLAGS,
"ABSL_MSVC_LINKOPTS": [
# Object file doesn't export any previously undefined symbols
"-ignore:4221",
@@ -207,8 +196,5 @@ COPT_VARS = {
"-maes",
"-msse4.1",
],
- "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS": [
- "/O2", # Maximize speed
- "/Ob2", # Aggressive inlining
- ],
+ "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS": [],
}
diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel
index 913cfafb..8f521bec 100644
--- a/absl/debugging/BUILD.bazel
+++ b/absl/debugging/BUILD.bazel
@@ -14,6 +14,7 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@@ -30,6 +31,14 @@ licenses(["notice"]) # Apache 2.0
cc_library(
name = "stacktrace",
srcs = [
+ "internal/stacktrace_aarch64-inl.inc",
+ "internal/stacktrace_arm-inl.inc",
+ "internal/stacktrace_config.h",
+ "internal/stacktrace_generic-inl.inc",
+ "internal/stacktrace_powerpc-inl.inc",
+ "internal/stacktrace_unimplemented-inl.inc",
+ "internal/stacktrace_win32-inl.inc",
+ "internal/stacktrace_x86-inl.inc",
"stacktrace.cc",
],
hdrs = ["stacktrace.h"],
@@ -37,7 +46,7 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":debugging_internal",
- "//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
],
)
@@ -55,27 +64,39 @@ cc_library(
"symbolize.h",
],
copts = ABSL_DEFAULT_COPTS,
- linkopts = ABSL_DEFAULT_LINKOPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS + select({
+ "//absl:windows": ["-DEFAULTLIB:dbghelp.lib"],
+ "//conditions:default": [],
+ }),
deps = [
":debugging_internal",
":demangle_internal",
"//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/base:dynamic_annotations",
"//absl/base:malloc_internal",
+ "//absl/base:raw_logging_internal",
],
)
cc_test(
name = "symbolize_test",
srcs = ["symbolize_test.cc"],
- copts = ABSL_TEST_COPTS,
- linkopts = ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS + select({
+ "//absl:windows": ["/Z7"],
+ "//conditions:default": [],
+ }),
+ linkopts = ABSL_DEFAULT_LINKOPTS + select({
+ "//absl:windows": ["/DEBUG"],
+ "//conditions:default": [],
+ }),
deps = [
":stack_consumption",
":symbolize",
"//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/memory",
"@com_google_googletest//:gtest",
],
@@ -95,8 +116,9 @@ cc_library(
deps = [
":stacktrace",
":symbolize",
- "//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
],
)
@@ -112,6 +134,8 @@ cc_library(
"//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:errno_saver",
+ "//absl/base:raw_logging_internal",
],
)
@@ -127,7 +151,7 @@ cc_test(
":failure_signal_handler",
":stacktrace",
":symbolize",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
"@com_google_googletest//:gtest",
],
@@ -143,22 +167,16 @@ cc_library(
hdrs = [
"internal/address_is_readable.h",
"internal/elf_mem_image.h",
- "internal/stacktrace_aarch64-inl.inc",
- "internal/stacktrace_arm-inl.inc",
- "internal/stacktrace_config.h",
- "internal/stacktrace_generic-inl.inc",
- "internal/stacktrace_powerpc-inl.inc",
- "internal/stacktrace_unimplemented-inl.inc",
- "internal/stacktrace_win32-inl.inc",
- "internal/stacktrace_x86-inl.inc",
"internal/vdso_support.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- "//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/base:dynamic_annotations",
+ "//absl/base:errno_saver",
+ "//absl/base:raw_logging_internal",
],
)
@@ -169,6 +187,7 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
deps = [
"//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
],
)
@@ -181,8 +200,8 @@ cc_test(
deps = [
":demangle_internal",
":stack_consumption",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/memory",
"@com_google_googletest//:gtest_main",
],
@@ -193,7 +212,10 @@ cc_library(
srcs = ["leak_check.cc"],
hdrs = ["leak_check.h"],
linkopts = ABSL_DEFAULT_LINKOPTS,
- deps = ["//absl/base:core_headers"],
+ deps = [
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ ],
)
# Adding a dependency to leak_check_disable will disable
@@ -204,6 +226,7 @@ cc_library(
srcs = ["leak_check_disable.cc"],
linkopts = ABSL_DEFAULT_LINKOPTS,
linkstatic = 1,
+ deps = ["//absl/base:config"],
alwayslink = 1,
)
@@ -225,6 +248,9 @@ cc_library(
}),
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"],
+ deps = [
+ "//absl/base:config",
+ ],
)
cc_library(
@@ -235,6 +261,9 @@ cc_library(
copts = ["-ULEAK_SANITIZER"],
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"],
+ deps = [
+ "//absl/base:config",
+ ],
)
cc_test(
@@ -292,8 +321,9 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"],
deps = [
- "//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
],
)
@@ -304,8 +334,8 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":stack_consumption",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"@com_google_googletest//:gtest_main",
],
)
diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt
index 001e2727..77336159 100644
--- a/absl/debugging/CMakeLists.txt
+++ b/absl/debugging/CMakeLists.txt
@@ -19,13 +19,21 @@ absl_cc_library(
stacktrace
HDRS
"stacktrace.h"
+ "internal/stacktrace_aarch64-inl.inc"
+ "internal/stacktrace_arm-inl.inc"
+ "internal/stacktrace_config.h"
+ "internal/stacktrace_generic-inl.inc"
+ "internal/stacktrace_powerpc-inl.inc"
+ "internal/stacktrace_unimplemented-inl.inc"
+ "internal/stacktrace_win32-inl.inc"
+ "internal/stacktrace_x86-inl.inc"
SRCS
"stacktrace.cc"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::debugging_internal
- absl::base
+ absl::config
absl::core_headers
PUBLIC
)
@@ -45,13 +53,16 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
+ $<$<BOOL:${MINGW}>:"dbghelp">
DEPS
absl::debugging_internal
absl::demangle_internal
absl::base
+ absl::config
absl::core_headers
absl::dynamic_annotations
absl::malloc_internal
+ absl::raw_logging_internal
PUBLIC
)
@@ -62,12 +73,16 @@ absl_cc_test(
"symbolize_test.cc"
COPTS
${ABSL_TEST_COPTS}
+ $<$<BOOL:${MSVC}>:-Z7>
+ LINKOPTS
+ $<$<BOOL:${MSVC}>:-DEBUG>
DEPS
absl::stack_consumption
absl::symbolize
absl::base
absl::core_headers
absl::memory
+ absl::raw_logging_internal
gmock
)
@@ -83,8 +98,9 @@ absl_cc_library(
DEPS
absl::stacktrace
absl::symbolize
- absl::base
+ absl::config
absl::core_headers
+ absl::raw_logging_internal
)
absl_cc_library(
@@ -102,6 +118,8 @@ absl_cc_library(
absl::base
absl::config
absl::core_headers
+ absl::errno_saver
+ absl::raw_logging_internal
PUBLIC
)
@@ -116,8 +134,8 @@ absl_cc_test(
absl::failure_signal_handler
absl::stacktrace
absl::symbolize
- absl::base
absl::strings
+ absl::raw_logging_internal
Threads::Threads
gmock
)
@@ -128,14 +146,6 @@ absl_cc_library(
HDRS
"internal/address_is_readable.h"
"internal/elf_mem_image.h"
- "internal/stacktrace_aarch64-inl.inc"
- "internal/stacktrace_arm-inl.inc"
- "internal/stacktrace_config.h"
- "internal/stacktrace_generic-inl.inc"
- "internal/stacktrace_powerpc-inl.inc"
- "internal/stacktrace_unimplemented-inl.inc"
- "internal/stacktrace_win32-inl.inc"
- "internal/stacktrace_x86-inl.inc"
"internal/vdso_support.h"
SRCS
"internal/address_is_readable.cc"
@@ -144,9 +154,11 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
- absl::base
absl::core_headers
+ absl::config
absl::dynamic_annotations
+ absl::errno_saver
+ absl::raw_logging_internal
)
absl_cc_library(
@@ -174,9 +186,9 @@ absl_cc_test(
DEPS
absl::demangle_internal
absl::stack_consumption
- absl::base
absl::core_headers
absl::memory
+ absl::raw_logging_internal
gmock_main
)
@@ -190,6 +202,7 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
+ absl::config
absl::core_headers
PUBLIC
)
@@ -236,7 +249,7 @@ absl_cc_test(
SRCS
"leak_check_test.cc"
COPTS
- ${ABSL_DEFAULT_COPTS}
+ ${ABSL_TEST_COPTS}
"$<$<BOOL:${ABSL_HAVE_LSAN}>:-DABSL_EXPECT_LEAK_SANITIZER>"
LINKOPTS
"${ABSL_LSAN_LINKOPTS}"
@@ -273,6 +286,7 @@ absl_cc_test(
absl::leak_check_api_enabled_for_testing
absl::leak_check_disable
absl::base
+ absl::raw_logging_internal
gmock_main
)
@@ -286,8 +300,9 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
- absl::base
+ absl::config
absl::core_headers
+ absl::raw_logging_internal
TESTONLY
)
@@ -300,8 +315,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::stack_consumption
- absl::base
absl::core_headers
+ absl::raw_logging_internal
gmock_main
)
diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc
index af651c72..1f69bfa8 100644
--- a/absl/debugging/failure_signal_handler.cc
+++ b/absl/debugging/failure_signal_handler.cc
@@ -24,6 +24,10 @@
#include <unistd.h>
#endif
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
#ifdef ABSL_HAVE_MMAP
#include <sys/mman.h>
#endif
@@ -37,6 +41,7 @@
#include <ctime>
#include "absl/base/attributes.h"
+#include "absl/base/internal/errno_saver.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/sysinfo.h"
#include "absl/debugging/internal/examine_stack.h"
@@ -44,10 +49,15 @@
#ifndef _WIN32
#define ABSL_HAVE_SIGACTION
+// Apple WatchOS and TVOS don't allow sigaltstack
+#if !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \
+ !(defined(TARGET_OS_TV) && TARGET_OS_TV)
+#define ABSL_HAVE_SIGALTSTACK
+#endif
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options;
@@ -117,7 +127,7 @@ const char* FailureSignalToString(int signo) {
} // namespace debugging_internal
-#ifndef _WIN32
+#ifdef ABSL_HAVE_SIGALTSTACK
static bool SetupAlternateStackOnce() {
#if defined(__wasm__) || defined (__asjms__)
@@ -169,7 +179,7 @@ static bool SetupAlternateStackOnce() {
// Returns the appropriate flag for sig_action.sa_flags
// if the system supports using an alternate stack.
static int MaybeSetupAlternateStack() {
-#ifndef _WIN32
+#ifdef ABSL_HAVE_SIGALTSTACK
ABSL_ATTRIBUTE_UNUSED static const bool kOnce = SetupAlternateStackOnce();
return SA_ONSTACK;
#else
@@ -205,9 +215,8 @@ static void InstallOneFailureHandler(FailureSignalData* data,
#endif
static void WriteToStderr(const char* data) {
- int old_errno = errno;
+ absl::base_internal::ErrnoSaver errno_saver;
absl::raw_logging_internal::SafeWriteToStderr(data, strlen(data));
- errno = old_errno;
}
static void WriteSignalMessage(int signo, void (*writerfn)(const char*)) {
@@ -357,5 +366,5 @@ void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) {
}
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h
index 87b202b0..f5a83962 100644
--- a/absl/debugging/failure_signal_handler.h
+++ b/absl/debugging/failure_signal_handler.h
@@ -44,8 +44,10 @@
#ifndef ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
#define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// FailureSignalHandlerOptions
//
@@ -113,7 +115,7 @@ namespace debugging_internal {
const char* FailureSignalToString(int signo);
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
diff --git a/absl/debugging/failure_signal_handler_test.cc b/absl/debugging/failure_signal_handler_test.cc
index bb2cc48e..863fb514 100644
--- a/absl/debugging/failure_signal_handler_test.cc
+++ b/absl/debugging/failure_signal_handler_test.cc
@@ -23,6 +23,7 @@
#include <fstream>
#include "gtest/gtest.h"
+#include "gmock/gmock.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/debugging/stacktrace.h"
#include "absl/debugging/symbolize.h"
@@ -31,6 +32,8 @@
namespace {
+using testing::StartsWith;
+
#if GTEST_HAS_DEATH_TEST
// For the parameterized death tests. GetParam() returns the signal number.
@@ -113,15 +116,15 @@ TEST_P(FailureSignalHandlerDeathTest, AbslFatalSignalsWithWriterFn) {
ASSERT_TRUE(error_output.is_open()) << file;
std::string error_line;
std::getline(error_output, error_line);
- EXPECT_TRUE(absl::StartsWith(
+ EXPECT_THAT(
error_line,
- absl::StrCat("*** ",
- absl::debugging_internal::FailureSignalToString(signo),
- " received at ")));
+ StartsWith(absl::StrCat(
+ "*** ", absl::debugging_internal::FailureSignalToString(signo),
+ " received at ")));
if (absl::debugging_internal::StackTraceWorksForTest()) {
std::getline(error_output, error_line);
- EXPECT_TRUE(absl::StartsWith(error_line, "PC: "));
+ EXPECT_THAT(error_line, StartsWith("PC: "));
}
}
diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc
index 64dd285b..65376063 100644
--- a/absl/debugging/internal/address_is_readable.cc
+++ b/absl/debugging/internal/address_is_readable.cc
@@ -20,14 +20,14 @@
#if !defined(__linux__) || defined(__ANDROID__)
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// On platforms other than Linux, just return true.
bool AddressIsReadable(const void* /* addr */) { return true; }
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#else
@@ -35,14 +35,16 @@ bool AddressIsReadable(const void* /* addr */) { return true; }
#include <fcntl.h>
#include <sys/syscall.h>
#include <unistd.h>
+
#include <atomic>
#include <cerrno>
#include <cstdint>
+#include "absl/base/internal/errno_saver.h"
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// Pack a pid and two file descriptors into a 64-bit word,
@@ -67,7 +69,7 @@ static void Unpack(uint64_t x, int *pid, int *read_fd, int *write_fd) {
// This is a namespace-scoped variable for correct zero-initialization.
static std::atomic<uint64_t> pid_and_fds; // initially 0, an invalid pid.
bool AddressIsReadable(const void *addr) {
- int save_errno = errno;
+ absl::base_internal::ErrnoSaver errno_saver;
// We test whether a byte is readable by using write(). Normally, this would
// be done via a cached file descriptor to /dev/null, but linux fails to
// check whether the byte is readable when the destination is /dev/null, so
@@ -126,12 +128,11 @@ bool AddressIsReadable(const void *addr) {
std::memory_order_relaxed);
}
} while (errno == EBADF);
- errno = save_errno;
return bytes_written == 1;
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif
diff --git a/absl/debugging/internal/address_is_readable.h b/absl/debugging/internal/address_is_readable.h
index dc626e5b..4bbaf4d6 100644
--- a/absl/debugging/internal/address_is_readable.h
+++ b/absl/debugging/internal/address_is_readable.h
@@ -15,8 +15,10 @@
#ifndef ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_
#define ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// Return whether the byte at *addr is readable, without faulting.
@@ -24,7 +26,7 @@ namespace debugging_internal {
bool AddressIsReadable(const void *addr);
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc
index 976e47a6..fc615c3f 100644
--- a/absl/debugging/internal/demangle.cc
+++ b/absl/debugging/internal/demangle.cc
@@ -24,7 +24,7 @@
#include <limits>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
typedef struct {
@@ -94,6 +94,8 @@ static const AbbrevPair kOperatorList[] = {
};
// List of builtin types from Itanium C++ ABI.
+//
+// Invariant: only one- or two-character type abbreviations here.
static const AbbrevPair kBuiltinTypeList[] = {
{"v", "void", 0},
{"w", "wchar_t", 0},
@@ -116,6 +118,16 @@ static const AbbrevPair kBuiltinTypeList[] = {
{"e", "long double", 0},
{"g", "__float128", 0},
{"z", "ellipsis", 0},
+
+ {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits)
+ {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits)
+ {"Dc", "decltype(auto)", 0},
+ {"Da", "auto", 0},
+ {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr)
+ {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits)
+ {"Di", "char32_t", 0},
+ {"Ds", "char16_t", 0},
+ {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits)
{nullptr, nullptr, 0},
};
@@ -1169,12 +1181,6 @@ static bool ParseType(State *state) {
}
state->parse_state = copy;
- // nullptr_t, i.e. decltype(nullptr).
- if (ParseTwoCharToken(state, "Dn")) {
- return true;
- }
- state->parse_state = copy;
-
if (ParseOneCharToken(state, 'U') && ParseSourceName(state) &&
ParseType(state)) {
return true;
@@ -1215,16 +1221,26 @@ static bool ParseCVQualifiers(State *state) {
return num_cv_qualifiers > 0;
}
-// <builtin-type> ::= v, etc.
+// <builtin-type> ::= v, etc. # single-character builtin types
// ::= u <source-name>
+// ::= Dd, etc. # two-character builtin types
+//
+// Not supported:
+// ::= DF <number> _ # _FloatN (N bits)
+//
static bool ParseBuiltinType(State *state) {
ComplexityGuard guard(state);
if (guard.IsTooComplex()) return false;
const AbbrevPair *p;
for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) {
- if (RemainingInput(state)[0] == p->abbrev[0]) {
+ // Guaranteed only 1- or 2-character strings in kBuiltinTypeList.
+ if (p->abbrev[1] == '\0') {
+ if (ParseOneCharToken(state, p->abbrev[0])) {
+ MaybeAppend(state, p->real_name);
+ return true;
+ }
+ } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) {
MaybeAppend(state, p->real_name);
- ++state->parse_state.mangled_idx;
return true;
}
}
@@ -1875,5 +1891,5 @@ bool Demangle(const char *mangled, char *out, int out_size) {
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h
index 20adbe9c..c314d9bc 100644
--- a/absl/debugging/internal/demangle.h
+++ b/absl/debugging/internal/demangle.h
@@ -53,8 +53,10 @@
#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// Demangle `mangled`. On success, return true and write the
@@ -63,7 +65,7 @@ namespace debugging_internal {
bool Demangle(const char *mangled, char *out, int out_size);
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc
index 7c50fe3a..c6f1ce18 100644
--- a/absl/debugging/internal/demangle_test.cc
+++ b/absl/debugging/internal/demangle_test.cc
@@ -23,7 +23,7 @@
#include "absl/memory/memory.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
namespace {
@@ -191,5 +191,5 @@ TEST(DemangleRegression, DeeplyNestedArrayType) {
} // namespace
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc
index a795117a..24cc0130 100644
--- a/absl/debugging/internal/elf_mem_image.cc
+++ b/absl/debugging/internal/elf_mem_image.cc
@@ -38,7 +38,7 @@
#define VERSYM_VERSION 0x7fff
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
namespace {
@@ -376,7 +376,7 @@ void ElfMemImage::SymbolIterator::Update(int increment) {
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HAVE_ELF_MEM_IMAGE
diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h
index fcd32afe..46bfade3 100644
--- a/absl/debugging/internal/elf_mem_image.h
+++ b/absl/debugging/internal/elf_mem_image.h
@@ -23,6 +23,8 @@
// used.
#include <climits>
+#include "absl/base/config.h"
+
// Maybe one day we can rewrite this file not to require the elf
// symbol extensions in glibc, but for right now we need them.
#ifdef ABSL_HAVE_ELF_MEM_IMAGE
@@ -39,7 +41,7 @@
#include <link.h> // for ElfW
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// An in-memory ELF image (may not exist on disk).
@@ -124,7 +126,7 @@ class ElfMemImage {
};
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HAVE_ELF_MEM_IMAGE
diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc
index 4739fbc5..a3dd893a 100644
--- a/absl/debugging/internal/examine_stack.cc
+++ b/absl/debugging/internal/examine_stack.cc
@@ -30,7 +30,7 @@
#include "absl/debugging/symbolize.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// Returns the program counter from signal context, nullptr if
@@ -53,6 +53,8 @@ void* GetProgramCounter(void* vuc) {
return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]);
#elif defined(__powerpc__)
return reinterpret_cast<void*>(context->uc_mcontext.regs->nip);
+#elif defined(__riscv)
+ return reinterpret_cast<void*>(context->uc_mcontext.__gregs[REG_PC]);
#elif defined(__s390__) && !defined(__s390x__)
return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff);
#elif defined(__s390__) && defined(__s390x__)
@@ -151,5 +153,5 @@ void DumpPCAndFrameSizesAndStackTrace(
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h
index 861db75d..39336913 100644
--- a/absl/debugging/internal/examine_stack.h
+++ b/absl/debugging/internal/examine_stack.h
@@ -17,8 +17,10 @@
#ifndef ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
#define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// Returns the program counter from signal context, or nullptr if
@@ -34,7 +36,7 @@ void DumpPCAndFrameSizesAndStackTrace(
void (*writerfn)(const char*, void*), void* writerfn_arg);
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc
index 8dfd94aa..875ca6d9 100644
--- a/absl/debugging/internal/stack_consumption.cc
+++ b/absl/debugging/internal/stack_consumption.cc
@@ -27,7 +27,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
namespace {
@@ -116,10 +116,11 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) {
// Set up the alt-signal-stack (and save the older one).
stack_t sigstk;
memset(&sigstk, 0, sizeof(sigstk));
- stack_t old_sigstk;
sigstk.ss_sp = altstack;
sigstk.ss_size = kAlternateStackSize;
sigstk.ss_flags = 0;
+ stack_t old_sigstk;
+ memset(&old_sigstk, 0, sizeof(old_sigstk));
ABSL_RAW_CHECK(sigaltstack(&sigstk, &old_sigstk) == 0,
"sigaltstack() failed");
@@ -153,6 +154,15 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) {
int signal_handler_stack_consumption = GetStackConsumption(altstack);
// Now restore the old alt-signal-stack and signal handlers.
+ if (old_sigstk.ss_sp == nullptr && old_sigstk.ss_size == 0 &&
+ (old_sigstk.ss_flags & SS_DISABLE)) {
+ // https://git.musl-libc.org/cgit/musl/commit/src/signal/sigaltstack.c?id=7829f42a2c8944555439380498ab8b924d0f2070
+ // The original stack has ss_size==0 and ss_flags==SS_DISABLE, but some
+ // versions of musl have a bug that rejects ss_size==0. Work around this by
+ // setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel
+ // when SS_DISABLE is set.
+ old_sigstk.ss_size = MINSIGSTKSZ;
+ }
ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0,
"sigaltstack() failed");
ABSL_RAW_CHECK(sigaction(SIGUSR1, &old_sa1, nullptr) == 0,
@@ -168,7 +178,7 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) {
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
diff --git a/absl/debugging/internal/stack_consumption.h b/absl/debugging/internal/stack_consumption.h
index 1eb37eef..5e60ec42 100644
--- a/absl/debugging/internal/stack_consumption.h
+++ b/absl/debugging/internal/stack_consumption.h
@@ -18,6 +18,8 @@
#ifndef ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_
#define ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_
+#include "absl/base/config.h"
+
// The code in this module is not portable.
// Use this feature test macro to detect its availability.
#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
@@ -27,7 +29,7 @@
#define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// Returns the stack consumption in bytes for the code exercised by
@@ -39,7 +41,7 @@ namespace debugging_internal {
int GetSignalHandlerStackConsumption(void (*signal_handler)(int));
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
diff --git a/absl/debugging/internal/stack_consumption_test.cc b/absl/debugging/internal/stack_consumption_test.cc
index 1c82d18a..80445bf4 100644
--- a/absl/debugging/internal/stack_consumption_test.cc
+++ b/absl/debugging/internal/stack_consumption_test.cc
@@ -23,7 +23,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
namespace {
@@ -44,7 +44,7 @@ TEST(SignalHandlerStackConsumptionTest, MeasuresStackConsumption) {
} // namespace
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#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 14b6e6b0..411ea308 100644
--- a/absl/debugging/internal/stacktrace_aarch64-inl.inc
+++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc
@@ -180,13 +180,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
bool StackTraceWorksForTest() {
return true;
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_
diff --git a/absl/debugging/internal/stacktrace_arm-inl.inc b/absl/debugging/internal/stacktrace_arm-inl.inc
index 4f51d180..fffda968 100644
--- a/absl/debugging/internal/stacktrace_arm-inl.inc
+++ b/absl/debugging/internal/stacktrace_arm-inl.inc
@@ -113,13 +113,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
bool StackTraceWorksForTest() {
return false;
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_
diff --git a/absl/debugging/internal/stacktrace_generic-inl.inc b/absl/debugging/internal/stacktrace_generic-inl.inc
index 39c47866..ac034c9f 100644
--- a/absl/debugging/internal/stacktrace_generic-inl.inc
+++ b/absl/debugging/internal/stacktrace_generic-inl.inc
@@ -16,6 +16,7 @@
#include <cstring>
#include "absl/debugging/stacktrace.h"
+#include "absl/base/attributes.h"
// Sometimes, we can try to get a stack trace from within a stack
// trace, because we don't block signals inside this code (which would be too
@@ -35,7 +36,7 @@ static __thread int recursive = 0;
static std::atomic<bool> disable_stacktraces(true); // Disabled until healthy.
// Waiting until static initializers run seems to be late enough.
// This file is included into stacktrace.cc so this will only run once.
-static int stacktraces_enabler = []() {
+ABSL_ATTRIBUTE_UNUSED static int stacktraces_enabler = []() {
void* unused_stack[1];
// Force the first backtrace to happen early to get the one-time shared lib
// loading (allocation) out of the way. After the first call it is much safer
@@ -86,13 +87,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
bool StackTraceWorksForTest() {
return true;
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_
diff --git a/absl/debugging/internal/stacktrace_powerpc-inl.inc b/absl/debugging/internal/stacktrace_powerpc-inl.inc
index ee6b38ff..2e7c2f40 100644
--- a/absl/debugging/internal/stacktrace_powerpc-inl.inc
+++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc
@@ -236,13 +236,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
bool StackTraceWorksForTest() {
return true;
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_
diff --git a/absl/debugging/internal/stacktrace_unimplemented-inl.inc b/absl/debugging/internal/stacktrace_unimplemented-inl.inc
index b49a929a..5b8fb191 100644
--- a/absl/debugging/internal/stacktrace_unimplemented-inl.inc
+++ b/absl/debugging/internal/stacktrace_unimplemented-inl.inc
@@ -12,13 +12,13 @@ static int UnwindImpl(void** /* result */, int* /* sizes */,
}
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
bool StackTraceWorksForTest() {
return false;
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_
diff --git a/absl/debugging/internal/stacktrace_win32-inl.inc b/absl/debugging/internal/stacktrace_win32-inl.inc
index 0cd8c339..9c2c5580 100644
--- a/absl/debugging/internal/stacktrace_win32-inl.inc
+++ b/absl/debugging/internal/stacktrace_win32-inl.inc
@@ -54,7 +54,7 @@ static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
- const void *ucp, int *min_dropped_frames) {
+ const void*, int* min_dropped_frames) {
int n = 0;
if (!RtlCaptureStackBackTrace_fn) {
// can't find a stacktrace with no function to call
@@ -73,13 +73,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
bool StackTraceWorksForTest() {
return false;
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc
index ff0fd31f..bc320ff7 100644
--- a/absl/debugging/internal/stacktrace_x86-inl.inc
+++ b/absl/debugging/internal/stacktrace_x86-inl.inc
@@ -36,6 +36,8 @@
#include "absl/base/internal/raw_logging.h"
+using absl::debugging_internal::AddressIsReadable;
+
#if defined(__linux__) && defined(__i386__)
// Count "push %reg" instructions in VDSO __kernel_vsyscall(),
// preceeding "syscall" or "sysenter".
@@ -81,7 +83,7 @@ static int CountPushInstructions(const unsigned char *const addr) {
// "mov reg,reg"
if (addr[i + 1] == 0xE5) {
// Found "mov %esp,%ebp".
- return 0;
+ return 0;
}
++i; // Skip register encoding byte.
} else if (addr[i] == 0x0F &&
@@ -171,6 +173,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) {
static const unsigned char *kernel_rt_sigreturn_address = nullptr;
static const unsigned char *kernel_vsyscall_address = nullptr;
if (num_push_instructions == -1) {
+#ifdef ABSL_HAVE_VDSO_SUPPORT
absl::debugging_internal::VDSOSupport vdso;
if (vdso.IsPresent()) {
absl::debugging_internal::VDSOSupport::SymbolInfo
@@ -199,6 +202,9 @@ static void **NextStackFrame(void **old_fp, const void *uc) {
} else {
num_push_instructions = 0;
}
+#else // ABSL_HAVE_VDSO_SUPPORT
+ num_push_instructions = 0;
+#endif // ABSL_HAVE_VDSO_SUPPORT
}
if (num_push_instructions != 0 && kernel_rt_sigreturn_address != nullptr &&
old_fp[1] == kernel_rt_sigreturn_address) {
@@ -222,7 +228,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) {
// "double fault" in case we hit the first fault due to e.g. stack
// corruption.
void *const reg_esp2 = reg_esp[num_push_instructions - 1];
- if (absl::debugging_internal::AddressIsReadable(reg_esp2)) {
+ if (AddressIsReadable(reg_esp2)) {
// Alleged %esp is readable, use it for further unwinding.
new_fp = reinterpret_cast<void **>(reg_esp2);
}
@@ -274,7 +280,7 @@ static void **NextStackFrame(void **old_fp, const void *uc) {
// Note: NextStackFrame<false>() is only called while the program
// is already on its last leg, so it's ok to be slow here.
- if (!absl::debugging_internal::AddressIsReadable(new_fp)) {
+ if (!AddressIsReadable(new_fp)) {
return nullptr;
}
}
@@ -328,13 +334,13 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count,
}
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
bool StackTraceWorksForTest() {
return true;
}
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_
diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h
index 151bc77c..5d0858b5 100644
--- a/absl/debugging/internal/symbolize.h
+++ b/absl/debugging/internal/symbolize.h
@@ -21,6 +21,8 @@
#include <cstddef>
#include <cstdint>
+#include "absl/base/config.h"
+
#ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
#error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set
#elif defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__) && \
@@ -33,7 +35,7 @@
#include <string>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// Iterates over all sections, invoking callback on each with the section name
@@ -52,13 +54,13 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
ElfW(Shdr) *out);
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
struct SymbolDecoratorArgs {
@@ -120,7 +122,7 @@ bool GetFileMappingHint(const void** start,
const char** filename);
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_
diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc
index fa88e1d8..1e8a78ac 100644
--- a/absl/debugging/internal/vdso_support.cc
+++ b/absl/debugging/internal/vdso_support.cc
@@ -38,7 +38,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
ABSL_CONST_INIT
@@ -188,7 +188,7 @@ static class VDSOInitHelper {
} vdso_init_helper;
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HAVE_VDSO_SUPPORT
diff --git a/absl/debugging/internal/vdso_support.h b/absl/debugging/internal/vdso_support.h
index bc5fdb11..6562c6c2 100644
--- a/absl/debugging/internal/vdso_support.h
+++ b/absl/debugging/internal/vdso_support.h
@@ -53,7 +53,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
// NOTE: this class may be used from within tcmalloc, and can not
@@ -150,7 +150,7 @@ class VDSOSupport {
int GetCPU();
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HAVE_ELF_MEM_IMAGE
diff --git a/absl/debugging/leak_check.cc b/absl/debugging/leak_check.cc
index 63e54ffa..ff904955 100644
--- a/absl/debugging/leak_check.cc
+++ b/absl/debugging/leak_check.cc
@@ -21,14 +21,14 @@
#ifndef LEAK_SANITIZER
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
bool HaveLeakSanitizer() { return false; }
void DoIgnoreLeak(const void*) { }
void RegisterLivePointers(const void*, size_t) { }
void UnRegisterLivePointers(const void*, size_t) { }
LeakCheckDisabler::LeakCheckDisabler() { }
LeakCheckDisabler::~LeakCheckDisabler() { }
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#else
@@ -36,7 +36,7 @@ LeakCheckDisabler::~LeakCheckDisabler() { }
#include <sanitizer/lsan_interface.h>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
bool HaveLeakSanitizer() { return true; }
void DoIgnoreLeak(const void* ptr) { __lsan_ignore_object(ptr); }
void RegisterLivePointers(const void* ptr, size_t size) {
@@ -47,7 +47,7 @@ void UnRegisterLivePointers(const void* ptr, size_t size) {
}
LeakCheckDisabler::LeakCheckDisabler() { __lsan_disable(); }
LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); }
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // LEAK_SANITIZER
diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h
index e1215e38..7a5a22dd 100644
--- a/absl/debugging/leak_check.h
+++ b/absl/debugging/leak_check.h
@@ -32,8 +32,10 @@
#include <cstddef>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// HaveLeakSanitizer()
//
@@ -105,7 +107,7 @@ void RegisterLivePointers(const void* ptr, size_t size);
// `RegisterLivePointers()`, enabling leak checking of those pointers.
void UnRegisterLivePointers(const void* ptr, size_t size);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_LEAK_CHECK_H_
diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc
index 3052fb97..1f7c7d82 100644
--- a/absl/debugging/stacktrace.cc
+++ b/absl/debugging/stacktrace.cc
@@ -57,7 +57,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
typedef int (*Unwinder)(void**, int*, int, int, const void*, int*);
@@ -136,5 +136,5 @@ int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip,
return n;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/stacktrace.h b/absl/debugging/stacktrace.h
index d7565aa4..0ec0ffda 100644
--- a/absl/debugging/stacktrace.h
+++ b/absl/debugging/stacktrace.h
@@ -31,16 +31,18 @@
#ifndef ABSL_DEBUGGING_STACKTRACE_H_
#define ABSL_DEBUGGING_STACKTRACE_H_
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// GetStackFrames()
//
// Records program counter values for up to `max_depth` frames, skipping the
-// most recent `skip_count` stack frames, and stores their corresponding values
-// and sizes in `results` and `sizes` buffers. (Note that the frame generated
-// for the `absl::GetStackFrames()` routine itself is also skipped.)
-// routine itself.
+// most recent `skip_count` stack frames, stores their corresponding values
+// and sizes in `results` and `sizes` buffers, and returns the number of frames
+// stored. (Note that the frame generated for the `absl::GetStackFrames()`
+// routine itself is also skipped.)
//
// Example:
//
@@ -55,8 +57,8 @@ inline namespace lts_2019_08_08 {
// The current stack frame would consist of three function calls: `bar()`,
// `foo()`, and then `main()`; however, since the `GetStackFrames()` call sets
// `skip_count` to `1`, it will skip the frame for `bar()`, the most recently
-// invoked function call. It will therefore return two program counters and will
-// produce values that map to the following function calls:
+// invoked function call. It will therefore return 2 and fill `result` with
+// program counters within the following functions:
//
// result[0] foo()
// result[1] main()
@@ -83,9 +85,10 @@ extern int GetStackFrames(void** result, int* sizes, int max_depth,
//
// Records program counter values obtained from a signal handler. Records
// program counter values for up to `max_depth` frames, skipping the most recent
-// `skip_count` stack frames, and stores their corresponding values and sizes in
-// `results` and `sizes` buffers. (Note that the frame generated for the
-// `absl::GetStackFramesWithContext()` routine itself is also skipped.)
+// `skip_count` stack frames, stores their corresponding values and sizes in
+// `results` and `sizes` buffers, and returns the number of frames stored. (Note
+// that the frame generated for the `absl::GetStackFramesWithContext()` routine
+// itself is also skipped.)
//
// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value
// passed to a signal handler registered via the `sa_sigaction` field of a
@@ -106,8 +109,9 @@ extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
// GetStackTrace()
//
// Records program counter values for up to `max_depth` frames, skipping the
-// most recent `skip_count` stack frames, and stores their corresponding values
-// in `results`. Note that this function is similar to `absl::GetStackFrames()`
+// most recent `skip_count` stack frames, stores their corresponding values
+// in `results`, and returns the number of frames
+// stored. Note that this function is similar to `absl::GetStackFrames()`
// except that it returns the stack trace only, and not stack frame sizes.
//
// Example:
@@ -132,9 +136,9 @@ extern int GetStackTrace(void** result, int max_depth, int skip_count);
//
// Records program counter values obtained from a signal handler. Records
// program counter values for up to `max_depth` frames, skipping the most recent
-// `skip_count` stack frames, and stores their corresponding values in
-// `results`. (Note that the frame generated for the
-// `absl::GetStackFramesWithContext()` routine itself is also skipped.)
+// `skip_count` stack frames, stores their corresponding values in `results`,
+// and returns the number of frames stored. (Note that the frame generated for
+// the `absl::GetStackFramesWithContext()` routine itself is also skipped.)
//
// The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value
// passed to a signal handler registered via the `sa_sigaction` field of a
@@ -221,7 +225,7 @@ namespace debugging_internal {
// working.
extern bool StackTraceWorksForTest();
} // namespace debugging_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_STACKTRACE_H_
diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc
index 24e3a7f0..54ed9700 100644
--- a/absl/debugging/symbolize.cc
+++ b/absl/debugging/symbolize.cc
@@ -16,12 +16,9 @@
#if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE)
#include "absl/debugging/symbolize_elf.inc"
-#elif defined(_WIN32) && defined(_DEBUG)
-// The Windows Symbolizer only works in debug mode. Note that _DEBUG
-// is the macro that defines whether or not MS C-Runtime debug info is
-// available. Note that the PDB files containing the debug info must
-// also be available to the program at runtime for the symbolizer to
-// work.
+#elif defined(_WIN32)
+// The Windows Symbolizer only works if PDB files containing the debug info
+// are available to the program at runtime.
#include "absl/debugging/symbolize_win32.inc"
#else
#include "absl/debugging/symbolize_unimplemented.inc"
diff --git a/absl/debugging/symbolize.h b/absl/debugging/symbolize.h
index ab5447c4..43d93a86 100644
--- a/absl/debugging/symbolize.h
+++ b/absl/debugging/symbolize.h
@@ -55,7 +55,7 @@
#include "absl/debugging/internal/symbolize.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// InitializeSymbolizer()
//
@@ -71,7 +71,7 @@ inline namespace lts_2019_08_08 {
// // Now you can use the symbolizer
// }
void InitializeSymbolizer(const char* argv0);
-
+//
// Symbolize()
//
// Symbolizes a program counter (instruction pointer value) `pc` and, on
@@ -89,11 +89,11 @@ void InitializeSymbolizer(const char* argv0);
// if (absl::Symbolize(pc, tmp, sizeof(tmp))) {
// symbol = tmp;
// }
-// absl::PrintF("%*p %s\n", pc, symbol);
+// absl::PrintF("%p %s\n", pc, symbol);
// }
bool Symbolize(const void *pc, char *out, int out_size);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_DEBUGGING_SYMBOLIZE_H_
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc
index 2bd7659f..c371635f 100644
--- a/absl/debugging/symbolize_elf.inc
+++ b/absl/debugging/symbolize_elf.inc
@@ -76,7 +76,7 @@
#include "absl/debugging/internal/vdso_support.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Value of argv[0]. Used by MaybeInitializeObjFile().
static char *argv0_value = nullptr;
@@ -625,6 +625,13 @@ static bool InSection(const void *address, const ElfW(Shdr) * section) {
return start <= address && address < (start + size);
}
+static const char *ComputeOffset(const char *base, ptrdiff_t offset) {
+ // Note: cast to uintptr_t to avoid undefined behavior when base evaluates to
+ // zero and offset is non-zero.
+ return reinterpret_cast<const char *>(
+ reinterpret_cast<uintptr_t>(base) + offset);
+}
+
// Read a symbol table and look for the symbol containing the
// pc. Iterate over symbols in a symbol table and look for the symbol
// containing "pc". If the symbol is found, and its name fits in
@@ -677,7 +684,8 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
// We keep the original address for opd redirection below.
const char *const original_start_address =
reinterpret_cast<const char *>(symbol.st_value);
- const char *start_address = original_start_address + relocation;
+ const char *start_address =
+ ComputeOffset(original_start_address, relocation);
if (deref_function_descriptor_pointer &&
InSection(original_start_address, opd)) {
@@ -689,8 +697,7 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
// If pc is inside the .opd section, it points to a function descriptor.
const size_t size = pc_in_opd ? kFunctionDescriptorSize : symbol.st_size;
- const void *const end_address =
- reinterpret_cast<const char *>(start_address) + size;
+ const void *const end_address = ComputeOffset(start_address, size);
if (symbol.st_value != 0 && // Skip null value symbols.
symbol.st_shndx != 0 && // Skip undefined symbols.
#ifdef STT_TLS
@@ -1469,5 +1476,5 @@ bool Symbolize(const void *pc, char *out, int out_size) {
return ok;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc
index 08068c30..a1d03aab 100644
--- a/absl/debugging/symbolize_test.cc
+++ b/absl/debugging/symbolize_test.cc
@@ -35,10 +35,29 @@
using testing::Contains;
+#ifdef _WIN32
+#define ABSL_SYMBOLIZE_TEST_NOINLINE __declspec(noinline)
+#else
+#define ABSL_SYMBOLIZE_TEST_NOINLINE ABSL_ATTRIBUTE_NOINLINE
+#endif
+
// Functions to symbolize. Use C linkage to avoid mangled names.
extern "C" {
-void nonstatic_func() { ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); }
-static void static_func() { ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); }
+ABSL_SYMBOLIZE_TEST_NOINLINE void nonstatic_func() {
+ // The next line makes this a unique function to prevent the compiler from
+ // folding identical functions together.
+ volatile int x = __LINE__;
+ static_cast<void>(x);
+ ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
+}
+
+ABSL_SYMBOLIZE_TEST_NOINLINE static void static_func() {
+ // The next line makes this a unique function to prevent the compiler from
+ // folding identical functions together.
+ volatile int x = __LINE__;
+ static_cast<void>(x);
+ ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
+}
} // extern "C"
struct Foo {
@@ -46,7 +65,11 @@ struct Foo {
};
// A C++ method that should have a mangled name.
-void ABSL_ATTRIBUTE_NOINLINE Foo::func(int) {
+ABSL_SYMBOLIZE_TEST_NOINLINE void Foo::func(int) {
+ // The next line makes this a unique function to prevent the compiler from
+ // folding identical functions together.
+ volatile int x = __LINE__;
+ static_cast<void>(x);
ABSL_BLOCK_TAIL_CALL_OPTIMIZATION();
}
@@ -80,6 +103,7 @@ static ABSL_PER_THREAD_TLS_KEYWORD char
symbolize_test_thread_big[2 * 1024 * 1024];
#endif
+#if !defined(__EMSCRIPTEN__)
// Used below to hopefully inhibit some compiler/linker optimizations
// that may remove kHpageTextPadding, kPadding0, and kPadding1 from
// the binary.
@@ -89,6 +113,7 @@ static volatile bool volatile_bool = false;
static constexpr size_t kHpageSize = 1 << 21;
const char kHpageTextPadding[kHpageSize * 4] ABSL_ATTRIBUTE_SECTION_VARIABLE(
.text) = "";
+#endif // !defined(__EMSCRIPTEN__)
static char try_symbolize_buffer[4096];
@@ -447,14 +472,15 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() {
#endif
}
-#elif defined(_WIN32) && defined(_DEBUG)
+#elif defined(_WIN32)
+#if !defined(ABSL_CONSUME_DLL)
TEST(Symbolize, Basics) {
EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func)));
// The name of an internal linkage symbol is not specified; allow either a
// mangled or an unmangled name here.
- const char* static_func_symbol = TrySymbolize((void *)(&static_func));
+ const char *static_func_symbol = TrySymbolize((void *)(&static_func));
ASSERT_TRUE(static_func_symbol != nullptr);
EXPECT_TRUE(strstr(static_func_symbol, "static_func") != nullptr);
@@ -481,11 +507,12 @@ TEST(Symbolize, Truncation) {
}
TEST(Symbolize, SymbolizeWithDemangling) {
- const char* result = TrySymbolize((void *)(&Foo::func));
+ const char *result = TrySymbolize((void *)(&Foo::func));
ASSERT_TRUE(result != nullptr);
EXPECT_TRUE(strstr(result, "Foo::func") != nullptr) << result;
}
+#endif // !defined(ABSL_CONSUME_DLL)
#else // Symbolizer unimplemented
TEST(Symbolize, Unimplemented) {
@@ -498,10 +525,12 @@ TEST(Symbolize, Unimplemented) {
#endif
int main(int argc, char **argv) {
+#if !defined(__EMSCRIPTEN__)
// Make sure kHpageTextPadding is linked into the binary.
if (volatile_bool) {
ABSL_RAW_LOG(INFO, "%s", kHpageTextPadding);
}
+#endif // !defined(__EMSCRIPTEN__)
#if ABSL_PER_THREAD_TLS
// Touch the per-thread variables.
diff --git a/absl/debugging/symbolize_unimplemented.inc b/absl/debugging/symbolize_unimplemented.inc
index 0c1c1951..db24456b 100644
--- a/absl/debugging/symbolize_unimplemented.inc
+++ b/absl/debugging/symbolize_unimplemented.inc
@@ -17,7 +17,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace debugging_internal {
@@ -36,5 +36,5 @@ bool GetFileMappingHint(const void **, const void **, uint64_t *, const char **)
void InitializeSymbolizer(const char*) {}
bool Symbolize(const void *, char *, int) { return false; }
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc
index 498ca2db..c3df46f6 100644
--- a/absl/debugging/symbolize_win32.inc
+++ b/absl/debugging/symbolize_win32.inc
@@ -17,10 +17,10 @@
#include <windows.h>
-// MSVC header DbgHelp.h has a warning for an ignored typedef.
+// MSVC header dbghelp.h has a warning for an ignored typedef.
#pragma warning(push)
#pragma warning(disable:4091)
-#include <DbgHelp.h>
+#include <dbghelp.h>
#pragma warning(pop)
#pragma comment(lib, "dbghelp.lib")
@@ -31,11 +31,11 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
static HANDLE process = NULL;
-void InitializeSymbolizer(const char *argv0) {
+void InitializeSymbolizer(const char*) {
if (process != nullptr) {
return;
}
@@ -54,13 +54,12 @@ void InitializeSymbolizer(const char *argv0) {
}
}
-bool Symbolize(const void *pc, char *out, int out_size) {
+bool Symbolize(const void* pc, char* out, int out_size) {
if (out_size <= 0) {
return false;
}
- std::aligned_storage<sizeof(SYMBOL_INFO) + MAX_SYM_NAME,
- alignof(SYMBOL_INFO)>::type buf;
- SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(&buf);
+ alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
+ SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(buf);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
if (!SymFromAddr(process, reinterpret_cast<DWORD64>(pc), nullptr, symbol)) {
@@ -78,5 +77,5 @@ bool Symbolize(const void *pc, char *out, int out_size) {
return true;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel
index 2fe61eaa..cdb4e7e8 100644
--- a/absl/flags/BUILD.bazel
+++ b/absl/flags/BUILD.bazel
@@ -1,5 +1,5 @@
#
-# Copyright 2019 The Abseil Authors.
+# 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.
@@ -14,6 +14,7 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@@ -26,12 +27,35 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
cc_library(
- name = "internal",
+ name = "flag_internal",
+ srcs = [
+ "internal/flag.cc",
+ ],
+ hdrs = [
+ "internal/flag.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = ["//absl/base:__subpackages__"],
+ deps = [
+ ":config",
+ ":handle",
+ ":registry",
+ "//absl/base",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/memory",
+ "//absl/strings",
+ "//absl/synchronization",
+ ],
+)
+
+cc_library(
+ name = "program_name",
srcs = [
"internal/program_name.cc",
],
hdrs = [
- "internal/path_util.h",
"internal/program_name.h",
],
copts = ABSL_DEFAULT_COPTS,
@@ -40,12 +64,31 @@ cc_library(
"//absl/flags:__pkg__",
],
deps = [
+ ":path_util",
+ "//absl/base:config",
+ "//absl/base:core_headers",
"//absl/strings",
"//absl/synchronization",
],
)
cc_library(
+ name = "path_util",
+ hdrs = [
+ "internal/path_util.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = [
+ "//absl/flags:__pkg__",
+ ],
+ deps = [
+ "//absl/base:config",
+ "//absl/strings",
+ ],
+)
+
+cc_library(
name = "config",
srcs = [
"usage_config.cc",
@@ -57,7 +100,9 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":internal",
+ ":path_util",
+ ":program_name",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/strings",
"//absl/synchronization",
@@ -75,7 +120,9 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ "//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:log_severity",
"//absl/strings",
"//absl/strings:str_format",
],
@@ -83,9 +130,6 @@ cc_library(
cc_library(
name = "handle",
- srcs = [
- "internal/commandlineflag.cc",
- ],
hdrs = [
"internal/commandlineflag.h",
],
@@ -97,10 +141,9 @@ cc_library(
deps = [
":config",
":marshalling",
- "//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/strings",
- "//absl/synchronization",
"//absl/types:optional",
],
)
@@ -123,9 +166,9 @@ cc_library(
deps = [
":config",
":handle",
- "//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
- "//absl/base:dynamic_annotations",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
"//absl/synchronization",
],
@@ -139,16 +182,17 @@ cc_library(
hdrs = [
"declare.h",
"flag.h",
- "internal/flag.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
+ ":flag_internal",
":handle",
":marshalling",
":registry",
"//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/strings",
],
@@ -170,10 +214,14 @@ cc_library(
deps = [
":config",
":flag",
+ ":flag_internal",
":handle",
- ":internal",
+ ":path_util",
+ ":program_name",
+ ":registry",
+ "//absl/base:config",
+ "//absl/base:core_headers",
"//absl/strings",
- "//absl/synchronization",
],
)
@@ -189,6 +237,8 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":usage_internal",
+ "//absl/base:config",
+ "//absl/base:core_headers",
"//absl/strings",
"//absl/synchronization",
],
@@ -206,18 +256,21 @@ cc_library(
deps = [
":config",
":flag",
+ ":flag_internal",
":handle",
- ":internal",
+ ":program_name",
":registry",
":usage",
":usage_internal",
+ "//absl/base:config",
+ "//absl/base:core_headers",
"//absl/strings",
"//absl/synchronization",
],
)
############################################################################
-# Unit tests in alpahabetical order.
+# Unit tests in alphabetical order.
cc_test(
name = "commandlineflag_test",
@@ -228,6 +281,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ ":config",
":flag",
":handle",
":registry",
@@ -261,12 +315,34 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ ":config",
":flag",
+ ":flag_internal",
+ ":handle",
+ ":registry",
+ "//absl/base:core_headers",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
+cc_binary(
+ name = "flag_benchmark",
+ testonly = 1,
+ srcs = [
+ "flag_benchmark.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":flag",
+ "//absl/time",
+ "//absl/types:optional",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
+
cc_test(
name = "marshalling_test",
size = "small",
@@ -290,7 +366,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":internal",
+ ":path_util",
"@com_google_googletest//:gtest_main",
],
)
@@ -306,7 +382,8 @@ cc_test(
deps = [
":flag",
":parse",
- "//absl/base",
+ ":registry",
+ "//absl/base:raw_logging_internal",
"//absl/base:scoped_set_env",
"//absl/strings",
"//absl/types:span",
@@ -323,7 +400,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":internal",
+ ":program_name",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
@@ -339,9 +416,10 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":flag",
+ ":handle",
+ ":marshalling",
":registry",
"//absl/memory",
- "//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
@@ -356,7 +434,8 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":config",
- ":internal",
+ ":path_util",
+ ":program_name",
"//absl/strings",
"@com_google_googletest//:gtest_main",
],
@@ -373,8 +452,10 @@ cc_test(
deps = [
":config",
":flag",
- ":internal",
":parse",
+ ":path_util",
+ ":program_name",
+ ":registry",
":usage",
":usage_internal",
"//absl/memory",
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt
index fa1d4e17..1d25f0de 100644
--- a/absl/flags/CMakeLists.txt
+++ b/absl/flags/CMakeLists.txt
@@ -19,20 +19,60 @@ absl_cc_library(
NAME
flags_internal
SRCS
+ "internal/flag.cc"
+ HDRS
+ "internal/flag.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::base
+ absl::config
+ absl::flags_config
+ absl::flags_handle
+ absl::flags_registry
+ absl::synchronization
+ PUBLIC
+)
+
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+ NAME
+ flags_program_name
+ SRCS
"internal/program_name.cc"
HDRS
- "internal/path_util.h"
"internal/program_name.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
+ absl::core_headers
+ absl::flags_path_util
absl::strings
absl::synchronization
PUBLIC
)
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+ NAME
+ flags_path_util
+ HDRS
+ "internal/path_util.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::config
+ absl::strings
+ PUBLIC
+)
+
absl_cc_library(
NAME
flags_config
@@ -46,7 +86,9 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::flags_internal
+ absl::config
+ absl::flags_path_util
+ absl::flags_program_name
absl::core_headers
absl::strings
absl::synchronization
@@ -64,7 +106,9 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::core_headers
+ absl::log_severity
absl::strings
absl::str_format
)
@@ -73,8 +117,6 @@ absl_cc_library(
absl_cc_library(
NAME
flags_handle
- SRCS
- "internal/commandlineflag.cc"
HDRS
"internal/commandlineflag.h"
COPTS
@@ -82,13 +124,14 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::flags_config
absl::flags_marshalling
- absl::base
absl::core_headers
+ absl::optional
+ absl::raw_logging_internal
absl::strings
absl::synchronization
- absl::optional
)
# Internal-only target, do not depend on directly.
@@ -106,11 +149,11 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::flags_config
absl::flags_handle
- absl::base
absl::core_headers
- absl::dynamic_annotations
+ absl::raw_logging_internal
absl::strings
absl::synchronization
)
@@ -123,14 +166,15 @@ absl_cc_library(
HDRS
"declare.h"
"flag.h"
- "internal/flag.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::flags_config
absl::flags_handle
+ absl::flags_internal
absl::flags_marshalling
absl::flags_registry
absl::base
@@ -151,10 +195,14 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
absl::flags_config
absl::flags
absl::flags_handle
absl::flags_internal
+ absl::flags_path_util
+ absl::flags_program_name
+ absl::flags_registry
absl::strings
absl::synchronization
)
@@ -171,6 +219,8 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
+ absl::core_headers
absl::flags_usage_internal
absl::strings
absl::synchronization
@@ -189,10 +239,13 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
+ absl::config
+ absl::core_headers
absl::flags_config
absl::flags
absl::flags_handle
absl::flags_internal
+ absl::flags_program_name
absl::flags_registry
absl::flags_usage
absl::strings
@@ -211,6 +264,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags
+ absl::flags_config
absl::flags_handle
absl::flags_registry
absl::memory
@@ -239,7 +293,12 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
+ absl::core_headers
absl::flags
+ absl::flags_config
+ absl::flags_handle
+ absl::flags_internal
+ absl::flags_registry
absl::strings
gtest_main
)
@@ -265,8 +324,9 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags
- absl::base
absl::flags_parse
+ absl::flags_registry
+ absl::raw_logging_internal
absl::scoped_set_env
absl::span
absl::strings
@@ -281,7 +341,7 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::flags_internal
+ absl::flags_path_util
gtest_main
)
@@ -293,7 +353,7 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::flags_internal
+ absl::flags_program_name
absl::strings
gtest_main
)
@@ -307,6 +367,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags
+ absl::flags_handle
+ absl::flags_marshalling
absl::flags_registry
absl::memory
absl::strings
@@ -322,7 +384,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::flags_config
- absl::flags_internal
+ absl::flags_path_util
+ absl::flags_program_name
absl::strings
gtest_main
)
@@ -337,8 +400,10 @@ absl_cc_test(
DEPS
absl::flags_config
absl::flags
- absl::flags_internal
+ absl::flags_path_util
+ absl::flags_program_name
absl::flags_parse
+ absl::flags_registry
absl::flags_usage
absl::memory
absl::strings
diff --git a/absl/flags/config.h b/absl/flags/config.h
index a9fd97ad..001f8fea 100644
--- a/absl/flags/config.h
+++ b/absl/flags/config.h
@@ -45,4 +45,23 @@
#define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES
#endif
+// ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD macro is used for using atomics with
+// double words, e.g. absl::Duration.
+// For reasons in bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878, modern
+// versions of GCC do not support cmpxchg16b instruction in standard atomics.
+#ifdef ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD
+#error "ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD should not be defined."
+#elif defined(__clang__) && defined(__x86_64__) && \
+ defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)
+#define ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD 1
+#endif
+
+// ABSL_FLAGS_INTERNAL_HAS_RTTI macro is used for selecting if we can use RTTI
+// for flag type identification.
+#ifdef ABSL_FLAGS_INTERNAL_HAS_RTTI
+#error ABSL_FLAGS_INTERNAL_HAS_RTTI cannot be directly set
+#elif !defined(__GNUC__) || defined(__GXX_RTTI)
+#define ABSL_FLAGS_INTERNAL_HAS_RTTI 1
+#endif // !defined(__GNUC__) || defined(__GXX_RTTI)
+
#endif // ABSL_FLAGS_CONFIG_H_
diff --git a/absl/flags/declare.h b/absl/flags/declare.h
index 7ef1d432..0f8cc6a5 100644
--- a/absl/flags/declare.h
+++ b/absl/flags/declare.h
@@ -25,10 +25,11 @@
#ifndef ABSL_FLAGS_DECLARE_H_
#define ABSL_FLAGS_DECLARE_H_
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// absl::Flag<T> represents a flag of type 'T' created by ABSL_FLAG.
@@ -40,10 +41,15 @@ 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
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// ABSL_DECLARE_FLAG()
diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc
index 0858259b..f7a457bf 100644
--- a/absl/flags/flag.cc
+++ b/absl/flags/flag.cc
@@ -15,32 +15,26 @@
#include "absl/flags/flag.h"
-#include <cstring>
+#include "absl/base/config.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/flag.h"
namespace absl {
-inline namespace lts_2019_08_08 {
-
-// We want to validate the type mismatch between type definition and
-// declaration. The lock-free implementation does not allow us to do it,
-// so in debug builds we always use the slower implementation, which always
-// validates the type.
-#ifndef NDEBUG
-#define ABSL_FLAGS_ATOMIC_GET(T) \
- T GetFlag(const absl::Flag<T>& flag) { return flag.Get(); }
-#else
-#define ABSL_FLAGS_ATOMIC_GET(T) \
- T GetFlag(const absl::Flag<T>& flag) { \
- T result; \
- if (flag.AtomicGet(&result)) { \
- return result; \
- } \
- return flag.Get(); \
- }
-#endif
+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_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET)
+ABSL_CONST_INIT static absl::Mutex construction_guard(absl::kConstInit);
-#undef ABSL_FLAGS_ATOMIC_GET
+absl::Mutex* GetGlobalConstructionGuard() { return &construction_guard; }
+
+} // namespace flags_internal
+
+#endif
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/flag.h b/absl/flags/flag.h
index 28925927..cff02c1f 100644
--- a/absl/flags/flag.h
+++ b/absl/flags/flag.h
@@ -29,16 +29,21 @@
#ifndef ABSL_FLAGS_FLAG_H_
#define ABSL_FLAGS_FLAG_H_
+#include <string>
+#include <type_traits>
+
#include "absl/base/attributes.h"
#include "absl/base/casts.h"
+#include "absl/base/config.h"
#include "absl/flags/config.h"
#include "absl/flags/declare.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/flag.h"
+#include "absl/flags/internal/registry.h"
#include "absl/flags/marshalling.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Flag
//
@@ -64,8 +69,103 @@ inline namespace lts_2019_08_08 {
// ABSL_FLAG(int, count, 0, "Count of items to process");
//
// No public methods of `absl::Flag<T>` are part of the Abseil Flags API.
+#if !defined(_MSC_VER) || defined(__clang__)
template <typename T>
using Flag = flags_internal::Flag<T>;
+#else
+// MSVC debug builds do not implement initialization with constexpr constructors
+// correctly. To work around this we add a level of indirection, so that the
+// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias
+// to that class) and dynamically allocates an instance when necessary. We also
+// forward all calls to internal::Flag methods via trampoline methods. In this
+// setup the `absl::Flag` class does not have constructor and virtual methods,
+// all the data members are public and thus MSVC is able to initialize it at
+// link time. To deal with multiple threads accessing the flag for the first
+// time concurrently we use an atomic boolean indicating if flag object is
+// initialized. We also employ the double-checked locking pattern where the
+// second level of protection is a global Mutex, so if two threads attempt to
+// construct the flag concurrently only one wins.
+// This solution is based on a recomendation here:
+// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454
+
+namespace flags_internal {
+absl::Mutex* GetGlobalConstructionGuard();
+} // namespace flags_internal
+
+template <typename T>
+class Flag {
+ public:
+ // No constructor and destructor to ensure this is an aggregate type.
+ // Visual Studio 2015 still requires the constructor for class to be
+ // constexpr initializable.
+#if _MSC_VER <= 1900
+ constexpr Flag(const char* name, const char* filename,
+ const flags_internal::HelpGenFunc help_gen,
+ const flags_internal::FlagDfltGenFunc default_value_gen)
+ : name_(name),
+ filename_(filename),
+ help_gen_(help_gen),
+ default_value_gen_(default_value_gen),
+ inited_(false),
+ impl_(nullptr) {}
+#endif
+
+ flags_internal::Flag<T>* GetImpl() const {
+ if (!inited_.load(std::memory_order_acquire)) {
+ absl::MutexLock l(flags_internal::GetGlobalConstructionGuard());
+
+ if (inited_.load(std::memory_order_acquire)) {
+ return impl_;
+ }
+
+ impl_ =
+ new flags_internal::Flag<T>(name_, filename_,
+ {flags_internal::FlagHelpMsg(help_gen_),
+ flags_internal::FlagHelpKind::kGenFunc},
+ default_value_gen_);
+ inited_.store(true, std::memory_order_release);
+ }
+
+ return impl_;
+ }
+
+ // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API.
+ // See https://abseil.io/docs/cpp/guides/flags
+ bool IsRetired() const { return GetImpl()->IsRetired(); }
+ bool IsAbseilFlag() const { return GetImpl()->IsAbseilFlag(); }
+ absl::string_view Name() const { return GetImpl()->Name(); }
+ std::string Help() const { return GetImpl()->Help(); }
+ bool IsModified() const { return GetImpl()->IsModified(); }
+ bool IsSpecifiedOnCommandLine() const {
+ return GetImpl()->IsSpecifiedOnCommandLine();
+ }
+ absl::string_view Typename() const { return GetImpl()->Typename(); }
+ std::string Filename() const { return GetImpl()->Filename(); }
+ std::string DefaultValue() const { return GetImpl()->DefaultValue(); }
+ std::string CurrentValue() const { return GetImpl()->CurrentValue(); }
+ template <typename U>
+ inline bool IsOfType() const {
+ return GetImpl()->template IsOfType<U>();
+ }
+ T Get() const { return GetImpl()->Get(); }
+ bool AtomicGet(T* v) const { return GetImpl()->AtomicGet(v); }
+ void Set(const T& v) { GetImpl()->Set(v); }
+ void SetCallback(const flags_internal::FlagCallbackFunc mutation_callback) {
+ GetImpl()->SetCallback(mutation_callback);
+ }
+ void InvokeCallback() { GetImpl()->InvokeCallback(); }
+
+ // The data members are logically private, but they need to be public for
+ // this to be an aggregate type.
+ const char* name_;
+ const char* filename_;
+ const flags_internal::HelpGenFunc help_gen_;
+ const flags_internal::FlagDfltGenFunc default_value_gen_;
+
+ mutable std::atomic<bool> inited_;
+ mutable flags_internal::Flag<T>* impl_;
+};
+#endif
// GetFlag()
//
@@ -84,23 +184,10 @@ using Flag = flags_internal::Flag<T>;
// // FLAGS_firstname is a Flag of type `std::string`
// std::string first_name = absl::GetFlag(FLAGS_firstname);
template <typename T>
-T GetFlag(const absl::Flag<T>& flag) {
-#define ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE(BIT) \
- static_assert( \
- !std::is_same<T, BIT>::value, \
- "Do not specify explicit template parameters to absl::GetFlag");
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE)
-#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE
-
+ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag<T>& flag) {
return flag.Get();
}
-// Overload for `GetFlag()` for types that support lock-free reads.
-#define ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT(T) \
- extern T GetFlag(const absl::Flag<T>& flag);
-ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT)
-#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT
-
// SetFlag()
//
// Sets the value of an `absl::Flag` to the value `v`. Do not construct an
@@ -122,7 +209,7 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
flag->Set(value);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
@@ -186,13 +273,23 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
#if ABSL_FLAGS_STRIP_NAMES
#define ABSL_FLAG_IMPL_FLAGNAME(txt) ""
#define ABSL_FLAG_IMPL_FILENAME() ""
+#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, false>(&flag)
#else
+#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
+ absl::flags_internal::FlagRegistrar<T, false>(flag.GetImpl())
+#endif
+#else
#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt
#define ABSL_FLAG_IMPL_FILENAME() __FILE__
+#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
absl::flags_internal::FlagRegistrar<T, true>(&flag)
+#else
+#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \
+ absl::flags_internal::FlagRegistrar<T, true>(flag.GetImpl())
+#endif
#endif
// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP
@@ -203,9 +300,19 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
#define ABSL_FLAG_IMPL_FLAGHELP(txt) txt
#endif
-#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \
- static std::string AbslFlagsWrapHelp##name() { \
- return ABSL_FLAG_IMPL_FLAGHELP(txt); \
+// AbslFlagHelpGenFor##name is used to encapsulate both immediate (method Const)
+// and lazy (method NonConst) evaluation of help message expression. We choose
+// between the two via the call to HelpArg in absl::Flag instantiation below.
+// If help message expression is constexpr evaluable compiler will optimize
+// away this whole struct.
+#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \
+ struct AbslFlagHelpGenFor##name { \
+ template <typename T = void> \
+ static constexpr const char* Const() { \
+ return absl::flags_internal::HelpConstexprWrap( \
+ ABSL_FLAG_IMPL_FLAGHELP(txt)); \
+ } \
+ static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \
}
#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
@@ -218,17 +325,30 @@ void SetFlag(absl::Flag<T>* flag, const V& v) {
// Note: Name of registrar object is not arbitrary. It is used to "grab"
// global name for FLAGS_no<flag_name> symbol, thus preventing the possibility
// of defining two flags with names foo and nofoo.
+#if !defined(_MSC_VER) || defined(__clang__)
#define ABSL_FLAG_IMPL(Type, name, default_value, help) \
namespace absl /* block flags in namespaces */ {} \
ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
- ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \
- ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name( \
- ABSL_FLAG_IMPL_FLAGNAME(#name), &AbslFlagsWrapHelp##name, \
- ABSL_FLAG_IMPL_FILENAME(), \
- &absl::flags_internal::FlagMarshallingOps<Type>, \
- &AbslFlagsInitFlag##name); \
+ ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \
+ ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \
+ ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \
+ absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0), \
+ &AbslFlagsInitFlag##name}; \
extern bool FLAGS_no##name; \
bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name)
+#else
+// MSVC version uses aggregate initialization. We also do not try to
+// optimize away help wrapper.
+#define ABSL_FLAG_IMPL(Type, name, default_value, help) \
+ namespace absl /* block flags in namespaces */ {} \
+ ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \
+ ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \
+ ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \
+ ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \
+ &AbslFlagHelpGenFor##name::NonConst, &AbslFlagsInitFlag##name}; \
+ extern bool FLAGS_no##name; \
+ bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name)
+#endif
// ABSL_RETIRED_FLAG
//
diff --git a/absl/flags/flag_benchmark.cc b/absl/flags/flag_benchmark.cc
new file mode 100644
index 00000000..87f73170
--- /dev/null
+++ b/absl/flags/flag_benchmark.cc
@@ -0,0 +1,111 @@
+//
+// Copyright 2020 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/flags/flag.h"
+#include "absl/time/time.h"
+#include "absl/types/optional.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+using String = std::string;
+using VectorOfStrings = std::vector<std::string>;
+using AbslDuration = absl::Duration;
+
+// We do not want to take over marshalling for the types absl::optional<int>,
+// absl::optional<std::string> which we do not own. Instead we introduce unique
+// "aliases" to these types, which we do.
+using AbslOptionalInt = absl::optional<int>;
+struct OptionalInt : AbslOptionalInt {
+ using AbslOptionalInt::AbslOptionalInt;
+};
+// Next two functions represent Abseil Flags marshalling for OptionalInt.
+bool AbslParseFlag(absl::string_view src, OptionalInt* flag,
+ std::string* error) {
+ int val;
+ if (src.empty())
+ flag->reset();
+ else if (!absl::ParseFlag(src, &val, error))
+ return false;
+ *flag = val;
+ return true;
+}
+std::string AbslUnparseFlag(const OptionalInt& flag) {
+ return !flag ? "" : absl::UnparseFlag(*flag);
+}
+
+using AbslOptionalString = absl::optional<std::string>;
+struct OptionalString : AbslOptionalString {
+ using AbslOptionalString::AbslOptionalString;
+};
+// Next two functions represent Abseil Flags marshalling for OptionalString.
+bool AbslParseFlag(absl::string_view src, OptionalString* flag,
+ std::string* error) {
+ std::string val;
+ if (src.empty())
+ flag->reset();
+ else if (!absl::ParseFlag(src, &val, error))
+ return false;
+ *flag = val;
+ return true;
+}
+std::string AbslUnparseFlag(const OptionalString& flag) {
+ return !flag ? "" : absl::UnparseFlag(*flag);
+}
+
+struct UDT {
+ UDT() = default;
+ UDT(const UDT&) {}
+ UDT& operator=(const UDT&) { return *this; }
+};
+// Next two functions represent Abseil Flags marshalling for UDT.
+bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
+std::string AbslUnparseFlag(const UDT&) { return ""; }
+
+} // namespace
+
+#define BENCHMARKED_TYPES(A) \
+ A(bool) \
+ A(int16_t) \
+ A(uint16_t) \
+ A(int32_t) \
+ A(uint32_t) \
+ A(int64_t) \
+ A(uint64_t) \
+ A(double) \
+ A(float) \
+ A(String) \
+ A(VectorOfStrings) \
+ A(OptionalInt) \
+ A(OptionalString) \
+ A(AbslDuration) \
+ A(UDT)
+
+#define FLAG_DEF(T) ABSL_FLAG(T, T##_flag, {}, "");
+
+BENCHMARKED_TYPES(FLAG_DEF)
+
+namespace {
+
+#define BM_GetFlag(T) \
+ void BM_GetFlag_##T(benchmark::State& state) { \
+ for (auto _ : state) { \
+ benchmark::DoNotOptimize(absl::GetFlag(FLAGS_##T##_flag)); \
+ } \
+ } \
+ BENCHMARK(BM_GetFlag_##T);
+
+BENCHMARKED_TYPES(BM_GetFlag)
+
+} // namespace
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc
index 1bcd7e96..4984d284 100644
--- a/absl/flags/flag_test.cc
+++ b/absl/flags/flag_test.cc
@@ -15,11 +15,25 @@
#include "absl/flags/flag.h"
+#include <stdint.h>
+
+#include <cmath>
+#include <string>
+#include <vector>
+
#include "gtest/gtest.h"
+#include "absl/base/attributes.h"
+#include "absl/flags/config.h"
+#include "absl/flags/declare.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/flag.h"
+#include "absl/flags/internal/registry.h"
+#include "absl/flags/usage_config.h"
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag);
ABSL_DECLARE_FLAG(std::vector<std::string>, mistyped_string_flag);
@@ -28,7 +42,7 @@ namespace {
namespace flags = absl::flags_internal;
-std::string TestHelpMsg() { return "help"; }
+std::string TestHelpMsg() { return "dynamic help"; }
template <typename T>
void* TestMakeDflt() {
return new T{};
@@ -37,20 +51,21 @@ void TestCallback() {}
template <typename T>
bool TestConstructionFor() {
- constexpr flags::Flag<T> f1("f1", &TestHelpMsg, "file",
- &absl::flags_internal::FlagMarshallingOps<T>,
- &TestMakeDflt<T>);
+ constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"),
+ flags::FlagHelpKind::kLiteral};
+ constexpr flags::Flag<T> f1("f1", "file", help_arg, &TestMakeDflt<T>);
EXPECT_EQ(f1.Name(), "f1");
- EXPECT_EQ(f1.Help(), "help");
+ EXPECT_EQ(f1.Help(), "literal help");
EXPECT_EQ(f1.Filename(), "file");
ABSL_CONST_INIT static flags::Flag<T> f2(
- "f2", &TestHelpMsg, "file", &absl::flags_internal::FlagMarshallingOps<T>,
+ "f2", "file",
+ {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc},
&TestMakeDflt<T>);
flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback);
EXPECT_EQ(f2.Name(), "f2");
- EXPECT_EQ(f2.Help(), "help");
+ EXPECT_EQ(f2.Help(), "dynamic help");
EXPECT_EQ(f2.Filename(), "file");
return true;
@@ -63,7 +78,27 @@ struct UDT {
bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
std::string AbslUnparseFlag(const UDT&) { return ""; }
-TEST(FlagTest, TestConstruction) {
+class FlagTest : public testing::Test {
+ protected:
+ static void SetUpTestSuite() {
+ // Install a function to normalize filenames before this test is run.
+ absl::FlagsUsageConfig default_config;
+ default_config.normalize_filename = &FlagTest::NormalizeFileName;
+ absl::SetFlagsUsageConfig(default_config);
+ }
+
+ private:
+ static std::string NormalizeFileName(absl::string_view fname) {
+#ifdef _WIN32
+ std::string normalized(fname);
+ std::replace(normalized.begin(), normalized.end(), '\\', '/');
+ fname = normalized;
+#endif
+ return std::string(fname);
+ }
+};
+
+TEST_F(FlagTest, TestConstruction) {
TestConstructionFor<bool>();
TestConstructionFor<int16_t>();
TestConstructionFor<uint16_t>();
@@ -98,7 +133,7 @@ namespace {
#if !ABSL_FLAGS_STRIP_NAMES
-TEST(FlagTest, TestFlagDeclaration) {
+TEST_F(FlagTest, TestFlagDeclaration) {
// test that we can access flag objects.
EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01");
EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02");
@@ -133,69 +168,69 @@ ABSL_FLAG(std::string, test_flag_11, "", "test flag 11");
namespace {
#if !ABSL_FLAGS_STRIP_NAMES
-TEST(FlagTest, TestFlagDefinition) {
+TEST_F(FlagTest, TestFlagDefinition) {
absl::string_view expected_file_name = "absl/flags/flag_test.cc";
EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01");
EXPECT_EQ(FLAGS_test_flag_01.Help(), "test flag 01");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_01.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_01.Filename(), expected_file_name))
+ << FLAGS_test_flag_01.Filename();
EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02");
EXPECT_EQ(FLAGS_test_flag_02.Help(), "test flag 02");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_02.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_02.Filename(), expected_file_name))
+ << FLAGS_test_flag_02.Filename();
EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03");
EXPECT_EQ(FLAGS_test_flag_03.Help(), "test flag 03");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_03.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_03.Filename(), expected_file_name))
+ << FLAGS_test_flag_03.Filename();
EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04");
EXPECT_EQ(FLAGS_test_flag_04.Help(), "test flag 04");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_04.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_04.Filename(), expected_file_name))
+ << FLAGS_test_flag_04.Filename();
EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05");
EXPECT_EQ(FLAGS_test_flag_05.Help(), "test flag 05");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_05.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_05.Filename(), expected_file_name))
+ << FLAGS_test_flag_05.Filename();
EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06");
EXPECT_EQ(FLAGS_test_flag_06.Help(), "test flag 06");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_06.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_06.Filename(), expected_file_name))
+ << FLAGS_test_flag_06.Filename();
EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07");
EXPECT_EQ(FLAGS_test_flag_07.Help(), "test flag 07");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_07.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_07.Filename(), expected_file_name))
+ << FLAGS_test_flag_07.Filename();
EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08");
EXPECT_EQ(FLAGS_test_flag_08.Help(), "test flag 08");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_08.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_08.Filename(), expected_file_name))
+ << FLAGS_test_flag_08.Filename();
EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09");
EXPECT_EQ(FLAGS_test_flag_09.Help(), "test flag 09");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_09.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_09.Filename(), expected_file_name))
+ << FLAGS_test_flag_09.Filename();
EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10");
EXPECT_EQ(FLAGS_test_flag_10.Help(), "test flag 10");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_10.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_10.Filename(), expected_file_name))
+ << FLAGS_test_flag_10.Filename();
EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11");
EXPECT_EQ(FLAGS_test_flag_11.Help(), "test flag 11");
- EXPECT_TRUE(
- absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name));
+ EXPECT_TRUE(absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name))
+ << FLAGS_test_flag_11.Filename();
}
#endif // !ABSL_FLAGS_STRIP_NAMES
// --------------------------------------------------------------------
-TEST(FlagTest, TestDefault) {
+TEST_F(FlagTest, TestDefault) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34);
@@ -211,7 +246,7 @@ TEST(FlagTest, TestDefault) {
// --------------------------------------------------------------------
-TEST(FlagTest, TestGetSet) {
+TEST_F(FlagTest, TestGetSet) {
absl::SetFlag(&FLAGS_test_flag_01, false);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), false);
@@ -258,7 +293,7 @@ ABSL_FLAG(std::string, test_flag_13, absl::StrCat("AAA", "BBB"),
namespace {
-TEST(FlagTest, TestNonConstexprDefault) {
+TEST_F(FlagTest, TestNonConstexprDefault) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), 1);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), "AAABBB");
}
@@ -272,7 +307,7 @@ ABSL_FLAG(bool, test_flag_14, true, absl::StrCat("test ", "flag ", "14"));
namespace {
#if !ABSL_FLAGS_STRIP_HELP
-TEST(FlagTest, TestNonConstexprHelp) {
+TEST_F(FlagTest, TestNonConstexprHelp) {
EXPECT_EQ(FLAGS_test_flag_14.Help(), "test flag 14");
}
#endif //! ABSL_FLAGS_STRIP_HELP
@@ -296,7 +331,7 @@ namespace {
void TestFlagCB() { cb_test_value = absl::GetFlag(FLAGS_test_flag_with_cb); }
// Tests side-effects of callback invocation.
-TEST(FlagTest, CallbackInvocation) {
+TEST_F(FlagTest, CallbackInvocation) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_cb), 100);
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_lambda_cb), 200);
EXPECT_EQ(cb_test_value, 300);
@@ -343,7 +378,7 @@ ABSL_FLAG(CustomUDT, test_flag_15, CustomUDT(), "test flag 15");
namespace {
-TEST(FlagTest, TestCustomUDT) {
+TEST_F(FlagTest, TestCustomUDT) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(1, 1));
absl::SetFlag(&FLAGS_test_flag_15, CustomUDT(2, 3));
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(2, 3));
@@ -351,19 +386,20 @@ TEST(FlagTest, TestCustomUDT) {
// MSVC produces link error on the type mismatch.
// Linux does not have build errors and validations work as expected.
-#if 0 // !defined(_WIN32) && GTEST_HAS_DEATH_TEST
+#if !defined(_WIN32) && GTEST_HAS_DEATH_TEST
+
+using FlagDeathTest = FlagTest;
-TEST(Flagtest, TestTypeMismatchValidations) {
- // For builtin types, GetFlag() only does validation in debug mode.
+TEST_F(FlagDeathTest, TestTypeMismatchValidations) {
EXPECT_DEBUG_DEATH(
- absl::GetFlag(FLAGS_mistyped_int_flag),
+ static_cast<void>(absl::GetFlag(FLAGS_mistyped_int_flag)),
"Flag 'mistyped_int_flag' is defined as one type and declared "
"as another");
- EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 0),
+ EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 1),
"Flag 'mistyped_int_flag' is defined as one type and declared "
"as another");
- EXPECT_DEATH(absl::GetFlag(FLAGS_mistyped_string_flag),
+ EXPECT_DEATH(static_cast<void>(absl::GetFlag(FLAGS_mistyped_string_flag)),
"Flag 'mistyped_string_flag' is defined as one type and "
"declared as another");
EXPECT_DEATH(
@@ -409,7 +445,7 @@ ABSL_FLAG(ConversionTestVal, test_flag_16,
namespace {
-TEST(FlagTest, CanSetViaImplicitConversion) {
+TEST_F(FlagTest, CanSetViaImplicitConversion) {
EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 10);
absl::SetFlag(&FLAGS_test_flag_16,
ConversionTestVal::ViaImplicitConv::kEleven);
@@ -447,7 +483,7 @@ ABSL_FLAG(NonDfltConstructible, ndc_flag2, 0,
namespace {
-TEST(FlagTest, TestNonDefaultConstructibleType) {
+TEST_F(FlagTest, TestNonDefaultConstructibleType) {
EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, '1' + 100);
EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 0);
@@ -468,7 +504,7 @@ ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr"));
namespace {
-TEST(FlagTest, TestRetiredFlagRegistration) {
+TEST_F(FlagTest, TestRetiredFlagRegistration) {
bool is_bool = false;
EXPECT_TRUE(flags::IsRetiredFlag("old_bool_flag", &is_bool));
EXPECT_TRUE(is_bool);
diff --git a/absl/flags/flag_test_defs.cc b/absl/flags/flag_test_defs.cc
index 3366c580..49f91dee 100644
--- a/absl/flags/flag_test_defs.cc
+++ b/absl/flags/flag_test_defs.cc
@@ -20,3 +20,5 @@
ABSL_FLAG(int, mistyped_int_flag, 0, "");
ABSL_FLAG(std::string, mistyped_string_flag, "", "");
+ABSL_RETIRED_FLAG(bool, old_bool_flag, true,
+ "repetition of retired flag definition");
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc
deleted file mode 100644
index f964165e..00000000
--- a/absl/flags/internal/commandlineflag.cc
+++ /dev/null
@@ -1,496 +0,0 @@
-//
-// Copyright 2019 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "absl/flags/internal/commandlineflag.h"
-
-#include <cassert>
-
-#include "absl/base/internal/raw_logging.h"
-#include "absl/base/optimization.h"
-#include "absl/flags/config.h"
-#include "absl/flags/usage_config.h"
-#include "absl/strings/str_cat.h"
-#include "absl/synchronization/mutex.h"
-
-namespace absl {
-inline namespace lts_2019_08_08 {
-namespace flags_internal {
-
-// The help message indicating that the commandline flag has been
-// 'stripped'. It will not show up when doing "-help" and its
-// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1
-// before including absl/flags/flag.h
-
-// This is used by this file, and also in commandlineflags_reporting.cc
-const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
-
-namespace {
-
-// Currently we only validate flag values for user-defined flag types.
-bool ShouldValidateFlagValue(const CommandLineFlag& flag) {
-#define DONT_VALIDATE(T) \
- if (flag.IsOfType<T>()) return false;
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE)
- DONT_VALIDATE(std::string)
- DONT_VALIDATE(std::vector<std::string>)
-#undef DONT_VALIDATE
-
- return true;
-}
-
-} // namespace
-
-absl::Mutex* InitFlag(CommandLineFlag* flag) {
- ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit);
- absl::Mutex* mu;
-
- {
- absl::MutexLock lock(&init_lock);
-
- if (flag->locks == nullptr) { // Must initialize Mutexes for this flag.
- flag->locks = new flags_internal::CommandLineFlagLocks;
- }
-
- mu = &flag->locks->primary_mu;
- }
-
- {
- absl::MutexLock lock(mu);
-
- if (!flag->retired && flag->def == nullptr) {
- // Need to initialize def and cur fields.
- flag->def = (*flag->make_init_value)();
- flag->cur = Clone(flag->op, flag->def);
- UpdateCopy(flag);
- flag->inited.store(true, std::memory_order_release);
- flag->InvokeCallback();
- }
- }
-
- flag->inited.store(true, std::memory_order_release);
- return mu;
-}
-
-// Ensure that the lazily initialized fields of *flag have been initialized,
-// and return &flag->locks->primary_mu.
-absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const
- LOCK_RETURNED(locks->primary_mu) {
- if (!this->inited.load(std::memory_order_acquire)) {
- return InitFlag(const_cast<CommandLineFlag*>(this));
- }
-
- // All fields initialized; this->locks is therefore safe to read.
- return &this->locks->primary_mu;
-}
-
-void CommandLineFlag::Destroy() const {
- // Values are heap allocated for retired and Abseil Flags.
- if (IsRetired() || IsAbseilFlag()) {
- if (this->cur) Delete(this->op, this->cur);
- if (this->def) Delete(this->op, this->def);
- }
-
- delete this->locks;
-}
-
-bool CommandLineFlag::IsModified() const {
- absl::MutexLock l(InitFlagIfNecessary());
- return modified;
-}
-
-void CommandLineFlag::SetModified(bool is_modified) {
- absl::MutexLock l(InitFlagIfNecessary());
- modified = is_modified;
-}
-
-bool CommandLineFlag::IsSpecifiedOnCommandLine() const {
- absl::MutexLock l(InitFlagIfNecessary());
- return on_command_line;
-}
-
-absl::string_view CommandLineFlag::Typename() const {
- // We do not store/report type in Abseil Flags, so that user do not rely on in
- // at runtime
- if (IsAbseilFlag() || IsRetired()) return "";
-
-#define HANDLE_V1_BUILTIN_TYPE(t) \
- if (IsOfType<t>()) { \
- return #t; \
- }
-
- HANDLE_V1_BUILTIN_TYPE(bool);
- HANDLE_V1_BUILTIN_TYPE(int32_t);
- HANDLE_V1_BUILTIN_TYPE(int64_t);
- HANDLE_V1_BUILTIN_TYPE(uint64_t);
- HANDLE_V1_BUILTIN_TYPE(double);
-#undef HANDLE_V1_BUILTIN_TYPE
-
- if (IsOfType<std::string>()) {
- return "string";
- }
-
- return "";
-}
-
-std::string CommandLineFlag::Filename() const {
- return flags_internal::GetUsageConfig().normalize_filename(this->filename);
-}
-
-std::string CommandLineFlag::DefaultValue() const {
- absl::MutexLock l(InitFlagIfNecessary());
-
- return Unparse(this->marshalling_op, this->def);
-}
-
-std::string CommandLineFlag::CurrentValue() const {
- absl::MutexLock l(InitFlagIfNecessary());
-
- return Unparse(this->marshalling_op, this->cur);
-}
-
-bool CommandLineFlag::HasValidatorFn() const {
- absl::MutexLock l(InitFlagIfNecessary());
-
- return this->validator != nullptr;
-}
-
-bool CommandLineFlag::SetValidatorFn(FlagValidator fn) {
- absl::MutexLock l(InitFlagIfNecessary());
-
- // ok to register the same function over and over again
- if (fn == this->validator) return true;
-
- // Can't set validator to a different function, unless reset first.
- if (fn != nullptr && this->validator != nullptr) {
- ABSL_INTERNAL_LOG(
- WARNING, absl::StrCat("Ignoring SetValidatorFn() for flag '", Name(),
- "': validate-fn already registered"));
-
- return false;
- }
-
- this->validator = fn;
- return true;
-}
-
-bool CommandLineFlag::InvokeValidator(const void* value) const
- EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
- if (!this->validator) {
- return true;
- }
-
- (void)value;
-
- ABSL_INTERNAL_LOG(
- FATAL,
- absl::StrCat("Flag '", Name(),
- "' of encapsulated type should not have a validator"));
-
- return false;
-}
-
-void CommandLineFlag::SetCallback(
- const flags_internal::FlagCallback mutation_callback) {
- absl::MutexLock l(InitFlagIfNecessary());
-
- callback = mutation_callback;
-
- InvokeCallback();
-}
-
-// If the flag has a mutation callback this function invokes it. While the
-// callback is being invoked the primary flag's mutex is unlocked and it is
-// re-locked back after call to callback is completed. Callback invocation is
-// guarded by flag's secondary mutex instead which prevents concurrent callback
-// invocation. Note that it is possible for other thread to grab the primary
-// lock and update flag's value at any time during the callback invocation.
-// This is by design. Callback can get a value of the flag if necessary, but it
-// might be different from the value initiated the callback and it also can be
-// different by the time the callback invocation is completed.
-// Requires that *primary_lock be held in exclusive mode; it may be released
-// and reacquired by the implementation.
-void CommandLineFlag::InvokeCallback()
- EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) {
- if (!this->callback) return;
-
- // The callback lock is guaranteed initialized, because *locks->primary_mu
- // exists.
- absl::Mutex* callback_mu = &this->locks->callback_mu;
-
- // When executing the callback we need the primary flag's mutex to be unlocked
- // so that callback can retrieve the flag's value.
- this->locks->primary_mu.Unlock();
-
- {
- absl::MutexLock lock(callback_mu);
- this->callback();
- }
-
- this->locks->primary_mu.Lock();
-}
-
-// Attempts to parse supplied `value` string using parsing routine in the `flag`
-// argument. If parsing is successful, it will try to validate that the parsed
-// value is valid for the specified 'flag'. Finally this function stores the
-// parsed value in 'dst' assuming it is a pointer to the flag's value type. In
-// case if any error is encountered in either step, the error message is stored
-// in 'err'
-bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value,
- std::string* err)
- EXCLUSIVE_LOCKS_REQUIRED(flag->locks->primary_mu) {
- void* tentative_value = Clone(flag->op, flag->def);
- std::string parse_err;
- if (!Parse(flag->marshalling_op, value, tentative_value, &parse_err)) {
- auto type_name = flag->Typename();
- absl::string_view err_sep = parse_err.empty() ? "" : "; ";
- absl::string_view typename_sep = type_name.empty() ? "" : " ";
- *err = absl::StrCat("Illegal value '", value, "' specified for",
- typename_sep, type_name, " flag '", flag->Name(), "'",
- err_sep, parse_err);
- Delete(flag->op, tentative_value);
- return false;
- }
-
- if (!flag->InvokeValidator(tentative_value)) {
- *err = absl::StrCat("Failed validation of new value '",
- Unparse(flag->marshalling_op, tentative_value),
- "' for flag '", flag->Name(), "'");
- Delete(flag->op, tentative_value);
- return false;
- }
-
- flag->counter++;
- Copy(flag->op, tentative_value, dst);
- Delete(flag->op, tentative_value);
- return true;
-}
-
-// Sets the value of the flag based on specified string `value`. If the flag
-// was successfully set to new value, it returns true. Otherwise, sets `err`
-// to indicate the error, leaves the flag unchanged, and returns false. There
-// are three ways to set the flag's value:
-// * Update the current flag value
-// * Update the flag's default value
-// * Update the current flag value if it was never set before
-// The mode is selected based on 'set_mode' parameter.
-bool CommandLineFlag::SetFromString(absl::string_view value,
- FlagSettingMode set_mode,
- ValueSource source, std::string* err) {
- if (IsRetired()) return false;
-
- absl::MutexLock l(InitFlagIfNecessary());
-
- // Direct-access flags can be modified without going through the
- // flag API. Detect such changes and update the flag->modified bit.
- if (!IsAbseilFlag()) {
- if (!this->modified && ChangedDirectly(this, this->cur, this->def)) {
- this->modified = true;
- }
- }
-
- switch (set_mode) {
- case SET_FLAGS_VALUE: {
- // set or modify the flag's value
- if (!TryParseLocked(this, this->cur, value, err)) return false;
- this->modified = true;
- UpdateCopy(this);
- InvokeCallback();
-
- if (source == kCommandLine) {
- this->on_command_line = true;
- }
- break;
- }
- case SET_FLAG_IF_DEFAULT: {
- // set the flag's value, but only if it hasn't been set by someone else
- if (!this->modified) {
- if (!TryParseLocked(this, this->cur, value, err)) return false;
- this->modified = true;
- UpdateCopy(this);
- InvokeCallback();
- } else {
- // TODO(rogeeff): review and fix this semantic. Currently we do not fail
- // in this case if flag is modified. This is misleading since the flag's
- // value is not updated even though we return true.
- // *err = absl::StrCat(this->Name(), " is already set to ",
- // CurrentValue(), "\n");
- // return false;
- return true;
- }
- break;
- }
- case SET_FLAGS_DEFAULT: {
- // modify the flag's default-value
- if (!TryParseLocked(this, this->def, value, err)) return false;
-
- if (!this->modified) {
- // Need to set both defvalue *and* current, in this case
- Copy(this->op, this->def, this->cur);
- UpdateCopy(this);
- InvokeCallback();
- }
- break;
- }
- default: {
- // unknown set_mode
- assert(false);
- return false;
- }
- }
-
- return true;
-}
-
-void CommandLineFlag::StoreAtomic(size_t size) {
- int64_t t = 0;
- assert(size <= sizeof(int64_t));
- memcpy(&t, this->cur, size);
- this->atomic.store(t, std::memory_order_release);
-}
-
-void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const {
- std::string v = DefaultValue();
-
- absl::MutexLock lock(InitFlagIfNecessary());
-
- void* dst = Clone(this->op, this->def);
- std::string error;
- if (!flags_internal::Parse(this->marshalling_op, v, dst, &error)) {
- ABSL_INTERNAL_LOG(
- FATAL,
- absl::StrCat("Flag ", Name(), " (from ", Filename(),
- "): std::string form of default value '", v,
- "' could not be parsed; error=", error));
- }
-
- // We do not compare dst to def since parsing/unparsing may make
- // small changes, e.g., precision loss for floating point types.
- Delete(this->op, dst);
-}
-
-bool CommandLineFlag::ValidateDefaultValue() const {
- absl::MutexLock lock(InitFlagIfNecessary());
- return InvokeValidator(this->def);
-}
-
-bool CommandLineFlag::ValidateInputValue(absl::string_view value) const {
- absl::MutexLock l(InitFlagIfNecessary()); // protect default value access
-
- void* obj = Clone(this->op, this->def);
- std::string ignored_error;
- const bool result =
- flags_internal::Parse(this->marshalling_op, value, obj, &ignored_error) &&
- InvokeValidator(obj);
- Delete(this->op, obj);
- return result;
-}
-
-const int64_t CommandLineFlag::kAtomicInit;
-
-void CommandLineFlag::Read(void* dst,
- const flags_internal::FlagOpFn dst_op) const {
- absl::ReaderMutexLock l(InitFlagIfNecessary());
-
- // `dst_op` is the unmarshaling operation corresponding to the declaration
- // visibile at the call site. `op` is the Flag's defined unmarshalling
- // operation. They must match for this operation to be well-defined.
- if (ABSL_PREDICT_FALSE(dst_op != op)) {
- ABSL_INTERNAL_LOG(
- ERROR,
- absl::StrCat("Flag '", name,
- "' is defined as one type and declared as another"));
- }
- CopyConstruct(op, cur, dst);
-}
-
-void CommandLineFlag::Write(const void* src,
- const flags_internal::FlagOpFn src_op) {
- absl::MutexLock l(InitFlagIfNecessary());
-
- // `src_op` is the marshalling operation corresponding to the declaration
- // visible at the call site. `op` is the Flag's defined marshalling operation.
- // They must match for this operation to be well-defined.
- if (ABSL_PREDICT_FALSE(src_op != op)) {
- ABSL_INTERNAL_LOG(
- ERROR,
- absl::StrCat("Flag '", name,
- "' is defined as one type and declared as another"));
- }
-
- if (ShouldValidateFlagValue(*this)) {
- void* obj = Clone(op, src);
- std::string ignored_error;
- std::string src_as_str = Unparse(marshalling_op, src);
- if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) ||
- !InvokeValidator(obj)) {
- ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", name,
- "' to invalid value ", src_as_str));
- }
- Delete(op, obj);
- }
-
- modified = true;
- counter++;
- Copy(op, src, cur);
-
- UpdateCopy(this);
- InvokeCallback();
-}
-
-std::string HelpText::GetHelpText() const {
- if (help_function_) return help_function_();
- if (help_message_) return help_message_;
-
- return {};
-}
-
-// Update any copy of the flag value that is stored in an atomic word.
-// In addition if flag has a mutation callback this function invokes it.
-void UpdateCopy(CommandLineFlag* flag) {
-#define STORE_ATOMIC(T) \
- else if (flag->IsOfType<T>()) { \
- flag->StoreAtomic(sizeof(T)); \
- }
-
- if (false) {
- }
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC)
-#undef STORE_ATOMIC
-}
-
-// Return true iff flag value was changed via direct-access.
-bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) {
- if (!flag->IsAbseilFlag()) {
-// Need to compare values for direct-access flags.
-#define CHANGED_FOR_TYPE(T) \
- if (flag->IsOfType<T>()) { \
- return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \
- }
-
- CHANGED_FOR_TYPE(bool);
- CHANGED_FOR_TYPE(int32_t);
- CHANGED_FOR_TYPE(int64_t);
- CHANGED_FOR_TYPE(uint64_t);
- CHANGED_FOR_TYPE(double);
- CHANGED_FOR_TYPE(std::string);
-#undef CHANGED_FOR_TYPE
- }
-
- return false;
-}
-
-} // namespace flags_internal
-} // inline namespace lts_2019_08_08
-} // namespace absl
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h
index 382553d2..6363c661 100644
--- a/absl/flags/internal/commandlineflag.h
+++ b/absl/flags/internal/commandlineflag.h
@@ -16,30 +16,41 @@
#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
-#include <atomic>
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+#include <string>
+#include <typeinfo>
+
+#include "absl/base/config.h"
#include "absl/base/macros.h"
+#include "absl/flags/config.h"
#include "absl/flags/marshalling.h"
-#include "absl/synchronization/mutex.h"
+#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// Type-specific operations, eg., parsing, copying, etc. are provided
-// by function specific to that type with a signature matching FlagOpFn.
-enum FlagOp {
- kDelete,
- kClone,
- kCopy,
- kCopyConstruct,
- kSizeof,
- kParse,
- kUnparse
-};
-using FlagOpFn = void* (*)(FlagOp, const void*, void*);
-using FlagMarshallingOpFn = void* (*)(FlagOp, const void*, void*, void*);
+// An alias for flag static type id. Values of type identify the flag value type
+// simialarly to typeid(T), but without relying on RTTI being available. In most
+// cases this id is enough to uniquely identify the flag's value type. In a few
+// cases we'll have to resort to using actual RTTI implementation if it is
+// available.
+using FlagStaticTypeId = void* (*)();
+
+// Address of this function template is used in current implementation as a flag
+// static type id.
+template <typename T>
+void* FlagStaticTypeIdGen() {
+#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+ return const_cast<std::type_info*>(&typeid(T));
+#else
+ return nullptr;
+#endif
+}
// Options that control SetCommandLineOptionWithMode.
enum FlagSettingMode {
@@ -62,215 +73,95 @@ enum ValueSource {
kProgrammaticChange,
};
-// Signature for the help generation function used as an argument for the
-// absl::Flag constructor.
-using HelpGenFunc = std::string (*)();
-
-// Signature for the function generating the initial flag value based (usually
-// based on default value supplied in flag's definition)
-using InitialValGenFunc = void* (*)();
-
-struct CommandLineFlagInfo;
-
-// Signature for the mutation callback used by watched Flags
-// The callback is noexcept.
-// TODO(rogeeff): add noexcept after C++17 support is added.
-using FlagCallback = void (*)();
-
-using FlagValidator = bool (*)();
-
-extern const char kStrippedFlagHelp[];
-
-// The per-type function
-template <typename T>
-void* FlagOps(FlagOp op, const void* v1, void* v2) {
- switch (op) {
- case kDelete:
- delete static_cast<const T*>(v1);
- return nullptr;
- case kClone:
- return new T(*static_cast<const T*>(v1));
- case kCopy:
- *static_cast<T*>(v2) = *static_cast<const T*>(v1);
- return nullptr;
- case kCopyConstruct:
- new (v2) T(*static_cast<const T*>(v1));
- return nullptr;
- case kSizeof:
- return reinterpret_cast<void*>(sizeof(T));
- default:
- return nullptr;
- }
-}
-
-template <typename T>
-void* FlagMarshallingOps(FlagOp op, const void* v1, void* v2, void* v3) {
- switch (op) {
- case kParse: {
- // initialize the temporary instance of type T based on current value in
- // destination (which is going to be flag's default value).
- T temp(*static_cast<T*>(v2));
- if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp,
- static_cast<std::string*>(v3))) {
- return nullptr;
- }
- *static_cast<T*>(v2) = std::move(temp);
- return v2;
- }
- case kUnparse:
- *static_cast<std::string*>(v2) =
- absl::UnparseFlag<T>(*static_cast<const T*>(v1));
- return nullptr;
- default:
- return nullptr;
- }
-}
-
-// Functions that invoke flag-type-specific operations.
-inline void Delete(FlagOpFn op, const void* obj) {
- op(flags_internal::kDelete, obj, nullptr);
-}
-
-inline void* Clone(FlagOpFn op, const void* obj) {
- return op(flags_internal::kClone, obj, nullptr);
-}
-
-inline void Copy(FlagOpFn op, const void* src, void* dst) {
- op(flags_internal::kCopy, src, dst);
-}
-
-inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
- op(flags_internal::kCopyConstruct, src, dst);
-}
-
-inline bool Parse(FlagMarshallingOpFn op, absl::string_view text, void* dst,
- std::string* error) {
- return op(flags_internal::kParse, &text, dst, error) != nullptr;
-}
-
-inline std::string Unparse(FlagMarshallingOpFn op, const void* val) {
- std::string result;
- op(flags_internal::kUnparse, val, &result, nullptr);
- return result;
-}
-
-inline size_t Sizeof(FlagOpFn op) {
- // This sequence of casts reverses the sequence from base::internal::FlagOps()
- return static_cast<size_t>(reinterpret_cast<intptr_t>(
- op(flags_internal::kSizeof, nullptr, nullptr)));
-}
-
-// The following struct contains the locks in a CommandLineFlag struct.
-// They are in a separate struct that is lazily allocated to avoid problems
-// with static initialization and to avoid multiple allocations.
-struct CommandLineFlagLocks {
- absl::Mutex primary_mu; // protects several fields in CommandLineFlag
- absl::Mutex callback_mu; // used to serialize callbacks
-};
-
-// Holds either a pointer to help text or a function which produces it. This is
-// needed for supporting both static initialization of Flags while supporting
-// the legacy registration framework. We can't use absl::variant<const char*,
-// const char*(*)()> since anybody passing 0 or nullptr in to a CommandLineFlag
-// would find an ambiguity.
-class HelpText {
+// Handle to FlagState objects. Specific flag state objects will restore state
+// of a flag produced this flag state from method CommandLineFlag::SaveState().
+class FlagStateInterface {
public:
- static constexpr HelpText FromFunctionPointer(const HelpGenFunc fn) {
- return HelpText(fn, nullptr);
- }
- static constexpr HelpText FromStaticCString(const char* msg) {
- return HelpText(nullptr, msg);
- }
-
- std::string GetHelpText() const;
-
- HelpText() = delete;
- HelpText(const HelpText&) = default;
- HelpText(HelpText&&) = default;
+ virtual ~FlagStateInterface() {}
- private:
- explicit constexpr HelpText(const HelpGenFunc fn, const char* msg)
- : help_function_(fn), help_message_(msg) {}
-
- HelpGenFunc help_function_;
- const char* help_message_;
+ // Restores the flag originated this object to the saved state.
+ virtual void Restore() const = 0;
};
// Holds all information for a flag.
-struct CommandLineFlag {
- constexpr CommandLineFlag(
- const char* name_arg, HelpText help_text, const char* filename_arg,
- const flags_internal::FlagOpFn op_arg,
- const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
- const flags_internal::InitialValGenFunc initial_value_gen,
- const bool retired_arg, void* def_arg, void* cur_arg)
- : name(name_arg),
- help(help_text),
- filename(filename_arg),
- op(op_arg),
- marshalling_op(marshalling_op_arg),
- make_init_value(initial_value_gen),
- retired(retired_arg),
- inited(false),
- modified(false),
- on_command_line(false),
- validator(nullptr),
- callback(nullptr),
- def(def_arg),
- cur(cur_arg),
- counter(0),
- atomic(kAtomicInit),
- locks(nullptr) {}
-
- // Revert the init routine.
- void Destroy() const;
+class CommandLineFlag {
+ public:
+ constexpr CommandLineFlag() = default;
// Not copyable/assignable.
CommandLineFlag(const CommandLineFlag&) = delete;
CommandLineFlag& operator=(const CommandLineFlag&) = delete;
- absl::string_view Name() const { return name; }
- std::string Help() const { return help.GetHelpText(); }
- bool IsRetired() const { return this->retired; }
- bool IsModified() const;
- void SetModified(bool is_modified);
- bool IsSpecifiedOnCommandLine() const;
- // Returns true iff this is a handle to an Abseil Flag.
- bool IsAbseilFlag() const {
- // Set to null for V1 flags
- return this->make_init_value != nullptr;
- }
-
- absl::string_view Typename() const;
- std::string Filename() const;
- std::string DefaultValue() const;
- std::string CurrentValue() const;
-
- bool HasValidatorFn() const;
- bool SetValidatorFn(FlagValidator fn);
- bool InvokeValidator(const void* value) const;
+ // Non-polymorphic access methods.
// Return true iff flag has type T.
template <typename T>
inline bool IsOfType() const {
- return this->op == &flags_internal::FlagOps<T>;
+ return TypeId() == &flags_internal::FlagStaticTypeIdGen<T>;
}
// Attempts to retrieve the flag value. Returns value on success,
// absl::nullopt otherwise.
template <typename T>
absl::optional<T> Get() const {
- if (IsRetired() || flags_internal::FlagOps<T> != this->op)
+ if (IsRetired() || !IsOfType<T>()) {
return absl::nullopt;
+ }
- T res;
- Read(&res, flags_internal::FlagOps<T>);
-
- return res;
+ // Implementation notes:
+ //
+ // We are wrapping a union around the value of `T` to serve three purposes:
+ //
+ // 1. `U.value` has correct size and alignment for a value of type `T`
+ // 2. The `U.value` constructor is not invoked since U's constructor does
+ // not do it explicitly.
+ // 3. The `U.value` destructor is invoked since U's destructor does it
+ // explicitly. This makes `U` a kind of RAII wrapper around non default
+ // constructible value of T, which is destructed when we leave the
+ // scope. We do need to destroy U.value, which is constructed by
+ // CommandLineFlag::Read even though we left it in a moved-from state
+ // after std::move.
+ //
+ // All of this serves to avoid requiring `T` being default constructible.
+ union U {
+ T value;
+ U() {}
+ ~U() { value.~T(); }
+ };
+ U u;
+
+ Read(&u.value);
+ return std::move(u.value);
}
- void SetCallback(const flags_internal::FlagCallback mutation_callback);
- void InvokeCallback();
+ // Polymorphic access methods
+
+ // Returns name of this flag.
+ virtual absl::string_view Name() const = 0;
+ // Returns name of the file where this flag is defined.
+ virtual std::string Filename() const = 0;
+ // Returns name of the flag's value type for some built-in types or empty
+ // std::string.
+ virtual absl::string_view Typename() const = 0;
+ // Returns help message associated with this flag.
+ virtual std::string Help() const = 0;
+ // Returns true iff this object corresponds to retired flag.
+ virtual bool IsRetired() const { return false; }
+ // Returns true iff this is a handle to an Abseil Flag.
+ virtual bool IsAbseilFlag() const { return true; }
+ // Returns id of the flag's value type.
+ virtual FlagStaticTypeId TypeId() const = 0;
+ virtual bool IsModified() const = 0;
+ virtual bool IsSpecifiedOnCommandLine() const = 0;
+ virtual std::string DefaultValue() const = 0;
+ virtual std::string CurrentValue() const = 0;
+
+ // Interfaces to operate on validators.
+ virtual bool ValidateInputValue(absl::string_view value) const = 0;
+
+ // Interface to save flag to some persistent state. Returns current flag state
+ // or nullptr if flag does not support saving and restoring a state.
+ virtual std::unique_ptr<FlagStateInterface> SaveState() = 0;
// Sets the value of the flag based on specified std::string `value`. If the flag
// was successfully set to new value, it returns true. Otherwise, sets `error`
@@ -280,106 +171,43 @@ struct CommandLineFlag {
// * Update the flag's default value
// * Update the current flag value if it was never set before
// The mode is selected based on `set_mode` parameter.
- bool SetFromString(absl::string_view value,
- flags_internal::FlagSettingMode set_mode,
- flags_internal::ValueSource source, std::string* error);
-
- void StoreAtomic(size_t size);
+ virtual bool SetFromString(absl::string_view value,
+ flags_internal::FlagSettingMode set_mode,
+ flags_internal::ValueSource source,
+ std::string* error) = 0;
- void CheckDefaultValueParsingRoundtrip() const;
- // Invoke the flag validators for old flags.
- // TODO(rogeeff): implement proper validators for Abseil Flags
- bool ValidateDefaultValue() const;
- bool ValidateInputValue(absl::string_view value) const;
-
- // Constant configuration for a particular flag.
- private:
- const char* const name;
- const HelpText help;
- const char* const filename;
+ // Checks that flags default value can be converted to std::string and back to the
+ // flag's value type.
+ virtual void CheckDefaultValueParsingRoundtrip() const = 0;
protected:
- const FlagOpFn op; // Type-specific handler
- const FlagMarshallingOpFn marshalling_op; // Marshalling ops handler
- const InitialValGenFunc make_init_value; // Makes initial value for the flag
- const bool retired; // Is the flag retired?
- std::atomic<bool> inited; // fields have been lazily initialized
+ ~CommandLineFlag() = default;
- // Mutable state (guarded by locks->primary_mu).
- bool modified; // Has flag value been modified?
- bool on_command_line; // Specified on command line.
- FlagValidator validator; // Validator function, or nullptr
- FlagCallback callback; // Mutation callback, or nullptr
- void* def; // Lazily initialized pointer to default value
- void* cur; // Lazily initialized pointer to current value
- int64_t counter; // Mutation counter
-
- // For some types, a copy of the current value is kept in an atomically
- // accessible field.
- static const int64_t kAtomicInit = 0xababababababababll;
- std::atomic<int64_t> atomic;
-
- // Lazily initialized mutexes for this flag value. We cannot inline a
- // SpinLock or Mutex here because those have non-constexpr constructors and
- // so would prevent constant initialization of this type.
- // TODO(rogeeff): fix it once Mutex has constexpr constructor
- struct CommandLineFlagLocks* locks; // locks, laziliy allocated.
-
- // Ensure that the lazily initialized fields of *flag have been initialized,
- // and return the lock which should be locked when flag's state is mutated.
- absl::Mutex* InitFlagIfNecessary() const;
-
- // copy construct new value of flag's type in a memory referenced by dst
- // based on current flag's value
- void Read(void* dst, const flags_internal::FlagOpFn dst_op) const;
- // updates flag's value to *src (locked)
- void Write(const void* src, const flags_internal::FlagOpFn src_op);
-
- friend class FlagRegistry;
- friend class FlagPtrMap;
- friend class FlagSaverImpl;
- friend void FillCommandLineFlagInfo(CommandLineFlag* flag,
- CommandLineFlagInfo* result);
- friend bool TryParseLocked(CommandLineFlag* flag, void* dst,
- absl::string_view value, std::string* err);
- friend absl::Mutex* InitFlag(CommandLineFlag* flag);
+ private:
+ // Copy-construct a new value of the flag's type in a memory referenced by
+ // the dst based on the current flag's value.
+ virtual void Read(void* dst) const = 0;
};
-// Update any copy of the flag value that is stored in an atomic word.
-// In addition if flag has a mutation callback this function invokes it. While
-// callback is being invoked the primary flag's mutex is unlocked and it is
-// re-locked back after call to callback is completed. Callback invocation is
-// guarded by flag's secondary mutex instead which prevents concurrent callback
-// invocation. Note that it is possible for other thread to grab the primary
-// lock and update flag's value at any time during the callback invocation.
-// This is by design. Callback can get a value of the flag if necessary, but it
-// might be different from the value initiated the callback and it also can be
-// different by the time the callback invocation is completed.
-// Requires that *primary_lock be held in exclusive mode; it may be released
-// and reacquired by the implementation.
-void UpdateCopy(CommandLineFlag* flag);
-// Return true iff flag value was changed via direct-access.
-bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b);
-
-// This macro is the "source of truth" for the list of supported flag types we
-// expect to perform lock free operations on. Specifically it generates code,
-// a one argument macro operating on a type, supplied as a macro argument, for
-// each type in the list.
-#define ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(A) \
- A(bool) \
- A(short) \
- A(unsigned short) \
- A(int) \
- A(unsigned int) \
- A(long) \
- A(unsigned long) \
- A(long long) \
- A(unsigned long long) \
- A(double) \
- A(float)
+// This macro is the "source of truth" for the list of supported flag built-in
+// types.
+#define ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(A) \
+ A(bool) \
+ A(short) \
+ A(unsigned short) \
+ A(int) \
+ A(unsigned int) \
+ A(long) \
+ A(unsigned long) \
+ A(long long) \
+ A(unsigned long long) \
+ A(double) \
+ A(float) \
+ A(std::string) \
+ A(std::vector<std::string>)
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_
diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc
index f0d57adb..0e8bc313 100644
--- a/absl/flags/internal/commandlineflag_test.cc
+++ b/absl/flags/internal/commandlineflag_test.cc
@@ -15,12 +15,17 @@
#include "absl/flags/internal/commandlineflag.h"
+#include <memory>
+#include <string>
+
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/flags/usage_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
ABSL_FLAG(int, int_flag, 201, "int_flag help");
ABSL_FLAG(std::string, string_flag, "dflt",
@@ -33,10 +38,26 @@ namespace flags = absl::flags_internal;
class CommandLineFlagTest : public testing::Test {
protected:
+ static void SetUpTestSuite() {
+ // Install a function to normalize filenames before this test is run.
+ absl::FlagsUsageConfig default_config;
+ default_config.normalize_filename = &CommandLineFlagTest::NormalizeFileName;
+ absl::SetFlagsUsageConfig(default_config);
+ }
+
void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); }
void TearDown() override { flag_saver_.reset(); }
private:
+ static std::string NormalizeFileName(absl::string_view fname) {
+#ifdef _WIN32
+ std::string normalized(fname);
+ std::replace(normalized.begin(), normalized.end(), '\\', '/');
+ fname = normalized;
+#endif
+ return std::string(fname);
+ }
+
std::unique_ptr<flags::FlagSaver> flag_saver_;
};
@@ -49,9 +70,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
EXPECT_EQ(flag_01->Typename(), "");
EXPECT_TRUE(!flag_01->IsRetired());
EXPECT_TRUE(flag_01->IsOfType<int>());
- EXPECT_TRUE(absl::EndsWith(
- flag_01->Filename(),
- "absl/flags/internal/commandlineflag_test.cc"));
+ EXPECT_TRUE(
+ absl::EndsWith(flag_01->Filename(),
+ "absl/flags/internal/commandlineflag_test.cc"))
+ << flag_01->Filename();
auto* flag_02 = flags::FindCommandLineFlag("string_flag");
@@ -61,9 +83,10 @@ TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) {
EXPECT_EQ(flag_02->Typename(), "");
EXPECT_TRUE(!flag_02->IsRetired());
EXPECT_TRUE(flag_02->IsOfType<std::string>());
- EXPECT_TRUE(absl::EndsWith(
- flag_02->Filename(),
- "absl/flags/internal/commandlineflag_test.cc"));
+ EXPECT_TRUE(
+ absl::EndsWith(flag_02->Filename(),
+ "absl/flags/internal/commandlineflag_test.cc"))
+ << flag_02->Filename();
auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag");
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc
new file mode 100644
index 00000000..5a921e28
--- /dev/null
+++ b/absl/flags/internal/flag.cc
@@ -0,0 +1,382 @@
+//
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/flags/internal/flag.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/base/attributes.h"
+#include "absl/base/config.h"
+#include "absl/base/const_init.h"
+#include "absl/base/optimization.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/usage_config.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/synchronization/mutex.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace flags_internal {
+
+// The help message indicating that the commandline flag has been
+// 'stripped'. It will not show up when doing "-help" and its
+// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1
+// before including absl/flags/flag.h
+const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
+
+namespace {
+
+// Currently we only validate flag values for user-defined flag types.
+bool ShouldValidateFlagValue(FlagStaticTypeId flag_type_id) {
+#define DONT_VALIDATE(T) \
+ if (flag_type_id == &FlagStaticTypeIdGen<T>) return false;
+ ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(DONT_VALIDATE)
+#undef DONT_VALIDATE
+
+ return true;
+}
+
+// RAII helper used to temporarily unlock and relock `absl::Mutex`.
+// This is used when we need to ensure that locks are released while
+// invoking user supplied callbacks and then reacquired, since callbacks may
+// need to acquire these locks themselves.
+class MutexRelock {
+ public:
+ explicit MutexRelock(absl::Mutex* mu) : mu_(mu) { mu_->Unlock(); }
+ ~MutexRelock() { mu_->Lock(); }
+
+ MutexRelock(const MutexRelock&) = delete;
+ MutexRelock& operator=(const MutexRelock&) = delete;
+
+ private:
+ absl::Mutex* mu_;
+};
+
+} // namespace
+
+void FlagImpl::Init() {
+ new (&data_guard_) absl::Mutex;
+
+ absl::MutexLock lock(reinterpret_cast<absl::Mutex*>(&data_guard_));
+
+ value_.dynamic = MakeInitValue().release();
+ StoreAtomic();
+}
+
+// Ensures that the lazily initialized data is initialized,
+// and returns pointer to the mutex guarding flags data.
+absl::Mutex* FlagImpl::DataGuard() const {
+ absl::call_once(const_cast<FlagImpl*>(this)->init_control_, &FlagImpl::Init,
+ const_cast<FlagImpl*>(this));
+
+ // data_guard_ is initialized.
+ return reinterpret_cast<absl::Mutex*>(&data_guard_);
+}
+
+void FlagImpl::AssertValidType(FlagStaticTypeId type_id) const {
+ FlagStaticTypeId this_type_id = flags_internal::StaticTypeId(op_);
+
+ // `type_id` is the type id corresponding to the declaration visibile at the
+ // call site. `this_type_id` is the type id corresponding to the type stored
+ // during flag definition. They must match for this operation to be
+ // well-defined.
+ if (ABSL_PREDICT_TRUE(type_id == this_type_id)) return;
+
+ void* lhs_runtime_type_id = type_id();
+ void* rhs_runtime_type_id = this_type_id();
+
+ if (lhs_runtime_type_id == rhs_runtime_type_id) return;
+
+#if defined(ABSL_FLAGS_INTERNAL_HAS_RTTI)
+ if (*reinterpret_cast<std::type_info*>(lhs_runtime_type_id) ==
+ *reinterpret_cast<std::type_info*>(rhs_runtime_type_id))
+ return;
+#endif
+
+ ABSL_INTERNAL_LOG(
+ FATAL, absl::StrCat("Flag '", Name(),
+ "' is defined as one type and declared as another"));
+}
+
+std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
+ void* res = nullptr;
+ if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
+ res = flags_internal::Clone(op_, default_value_.dynamic_value);
+ } else {
+ res = (*default_value_.gen_func)();
+ }
+ return {res, DynValueDeleter{op_}};
+}
+
+void FlagImpl::StoreValue(const void* src) {
+ flags_internal::Copy(op_, src, value_.dynamic);
+ StoreAtomic();
+ modified_ = true;
+ ++counter_;
+ InvokeCallback();
+}
+
+absl::string_view FlagImpl::Name() const { return name_; }
+
+std::string FlagImpl::Filename() const {
+ return flags_internal::GetUsageConfig().normalize_filename(filename_);
+}
+
+std::string FlagImpl::Help() const {
+ return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal
+ : help_.gen_func();
+}
+
+bool FlagImpl::IsModified() const {
+ absl::MutexLock l(DataGuard());
+ return modified_;
+}
+
+bool FlagImpl::IsSpecifiedOnCommandLine() const {
+ absl::MutexLock l(DataGuard());
+ return on_command_line_;
+}
+
+std::string FlagImpl::DefaultValue() const {
+ absl::MutexLock l(DataGuard());
+
+ auto obj = MakeInitValue();
+ return flags_internal::Unparse(op_, obj.get());
+}
+
+std::string FlagImpl::CurrentValue() const {
+ absl::MutexLock l(DataGuard());
+
+ return flags_internal::Unparse(op_, value_.dynamic);
+}
+
+void FlagImpl::SetCallback(const FlagCallbackFunc mutation_callback) {
+ absl::MutexLock l(DataGuard());
+
+ if (callback_ == nullptr) {
+ callback_ = new FlagCallback;
+ }
+ callback_->func = mutation_callback;
+
+ InvokeCallback();
+}
+
+void FlagImpl::InvokeCallback() const {
+ if (!callback_) return;
+
+ // Make a copy of the C-style function pointer that we are about to invoke
+ // before we release the lock guarding it.
+ FlagCallbackFunc cb = callback_->func;
+
+ // If the flag has a mutation callback this function invokes it. While the
+ // callback is being invoked the primary flag's mutex is unlocked and it is
+ // re-locked back after call to callback is completed. Callback invocation is
+ // guarded by flag's secondary mutex instead which prevents concurrent
+ // callback invocation. Note that it is possible for other thread to grab the
+ // primary lock and update flag's value at any time during the callback
+ // invocation. This is by design. Callback can get a value of the flag if
+ // necessary, but it might be different from the value initiated the callback
+ // and it also can be different by the time the callback invocation is
+ // completed. Requires that *primary_lock be held in exclusive mode; it may be
+ // released and reacquired by the implementation.
+ MutexRelock relock(DataGuard());
+ absl::MutexLock lock(&callback_->guard);
+ cb();
+}
+
+bool FlagImpl::RestoreState(const void* value, bool modified,
+ bool on_command_line, int64_t counter) {
+ {
+ absl::MutexLock l(DataGuard());
+
+ if (counter_ == counter) return false;
+ }
+
+ Write(value);
+
+ {
+ absl::MutexLock l(DataGuard());
+
+ modified_ = modified;
+ on_command_line_ = on_command_line;
+ }
+
+ return true;
+}
+
+// Attempts to parse supplied `value` string using parsing routine in the `flag`
+// argument. If parsing successful, this function replaces the dst with newly
+// parsed value. In case if any error is encountered in either step, the error
+// message is stored in 'err'
+std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
+ absl::string_view value, std::string* err) const {
+ std::unique_ptr<void, DynValueDeleter> tentative_value = MakeInitValue();
+
+ std::string parse_err;
+ if (!flags_internal::Parse(op_, value, tentative_value.get(), &parse_err)) {
+ absl::string_view err_sep = parse_err.empty() ? "" : "; ";
+ *err = absl::StrCat("Illegal value '", value, "' specified for flag '",
+ Name(), "'", err_sep, parse_err);
+ return nullptr;
+ }
+
+ return tentative_value;
+}
+
+void FlagImpl::Read(void* dst) const {
+ absl::ReaderMutexLock l(DataGuard());
+
+ flags_internal::CopyConstruct(op_, value_.dynamic, dst);
+}
+
+void FlagImpl::StoreAtomic() {
+ size_t data_size = flags_internal::Sizeof(op_);
+
+ if (data_size <= sizeof(int64_t)) {
+ int64_t t = 0;
+ std::memcpy(&t, value_.dynamic, data_size);
+ value_.atomics.small_atomic.store(t, std::memory_order_release);
+ }
+#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
+ else if (data_size <= sizeof(FlagsInternalTwoWordsType)) {
+ FlagsInternalTwoWordsType t{0, 0};
+ std::memcpy(&t, value_.dynamic, data_size);
+ value_.atomics.big_atomic.store(t, std::memory_order_release);
+ }
+#endif
+}
+
+void FlagImpl::Write(const void* src) {
+ absl::MutexLock l(DataGuard());
+
+ if (ShouldValidateFlagValue(flags_internal::StaticTypeId(op_))) {
+ std::unique_ptr<void, DynValueDeleter> obj{flags_internal::Clone(op_, src),
+ DynValueDeleter{op_}};
+ std::string ignored_error;
+ std::string src_as_str = flags_internal::Unparse(op_, src);
+ if (!flags_internal::Parse(op_, src_as_str, obj.get(), &ignored_error)) {
+ ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", Name(),
+ "' to invalid value ", src_as_str));
+ }
+ }
+
+ StoreValue(src);
+}
+
+// Sets the value of the flag based on specified string `value`. If the flag
+// was successfully set to new value, it returns true. Otherwise, sets `err`
+// to indicate the error, leaves the flag unchanged, and returns false. There
+// are three ways to set the flag's value:
+// * Update the current flag value
+// * Update the flag's default value
+// * Update the current flag value if it was never set before
+// The mode is selected based on 'set_mode' parameter.
+bool FlagImpl::SetFromString(absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* err) {
+ absl::MutexLock l(DataGuard());
+
+ switch (set_mode) {
+ case SET_FLAGS_VALUE: {
+ // set or modify the flag's value
+ auto tentative_value = TryParse(value, err);
+ if (!tentative_value) return false;
+
+ StoreValue(tentative_value.get());
+
+ if (source == kCommandLine) {
+ on_command_line_ = true;
+ }
+ break;
+ }
+ case SET_FLAG_IF_DEFAULT: {
+ // set the flag's value, but only if it hasn't been set by someone else
+ if (modified_) {
+ // TODO(rogeeff): review and fix this semantic. Currently we do not fail
+ // in this case if flag is modified. This is misleading since the flag's
+ // value is not updated even though we return true.
+ // *err = absl::StrCat(Name(), " is already set to ",
+ // CurrentValue(), "\n");
+ // return false;
+ return true;
+ }
+ auto tentative_value = TryParse(value, err);
+ if (!tentative_value) return false;
+
+ StoreValue(tentative_value.get());
+ break;
+ }
+ case SET_FLAGS_DEFAULT: {
+ auto tentative_value = TryParse(value, err);
+ if (!tentative_value) return false;
+
+ if (DefaultKind() == FlagDefaultKind::kDynamicValue) {
+ void* old_value = default_value_.dynamic_value;
+ default_value_.dynamic_value = tentative_value.release();
+ tentative_value.reset(old_value);
+ } else {
+ default_value_.dynamic_value = tentative_value.release();
+ def_kind_ = static_cast<uint8_t>(FlagDefaultKind::kDynamicValue);
+ }
+
+ if (!modified_) {
+ // Need to set both default value *and* current, in this case
+ StoreValue(default_value_.dynamic_value);
+ modified_ = false;
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+void FlagImpl::CheckDefaultValueParsingRoundtrip() const {
+ std::string v = DefaultValue();
+
+ absl::MutexLock lock(DataGuard());
+
+ auto dst = MakeInitValue();
+ std::string error;
+ if (!flags_internal::Parse(op_, v, dst.get(), &error)) {
+ ABSL_INTERNAL_LOG(
+ FATAL,
+ absl::StrCat("Flag ", Name(), " (from ", Filename(),
+ "): std::string form of default value '", v,
+ "' could not be parsed; error=", error));
+ }
+
+ // We do not compare dst to def since parsing/unparsing may make
+ // small changes, e.g., precision loss for floating point types.
+}
+
+bool FlagImpl::ValidateInputValue(absl::string_view value) const {
+ absl::MutexLock l(DataGuard());
+
+ auto obj = MakeInitValue();
+ std::string ignored_error;
+ return flags_internal::Parse(op_, value, obj.get(), &ignored_error);
+}
+
+} // namespace flags_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h
index 9a5d9b4b..35a148cf 100644
--- a/absl/flags/internal/flag.h
+++ b/absl/flags/internal/flag.h
@@ -16,45 +16,547 @@
#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_
#define ABSL_FLAGS_INTERNAL_FLAG_H_
+#include <stdint.h>
+
+#include <atomic>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include "absl/base/call_once.h"
+#include "absl/base/config.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/flags/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/memory/memory.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// This is "unspecified" implementation of absl::Flag<T> type.
template <typename T>
-class Flag : public flags_internal::CommandLineFlag {
+class Flag;
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag value type operations, eg., parsing, copying, etc. are provided
+// by function specific to that type with a signature matching FlagOpFn.
+
+enum class FlagOp {
+ kDelete,
+ kClone,
+ kCopy,
+ kCopyConstruct,
+ kSizeof,
+ kStaticTypeId,
+ kParse,
+ kUnparse,
+};
+using FlagOpFn = void* (*)(FlagOp, const void*, void*, void*);
+
+// Flag value specific operations routine.
+template <typename T>
+void* FlagOps(FlagOp op, const void* v1, void* v2, void* v3) {
+ switch (op) {
+ case FlagOp::kDelete:
+ delete static_cast<const T*>(v1);
+ return nullptr;
+ case FlagOp::kClone:
+ return new T(*static_cast<const T*>(v1));
+ case FlagOp::kCopy:
+ *static_cast<T*>(v2) = *static_cast<const T*>(v1);
+ return nullptr;
+ case FlagOp::kCopyConstruct:
+ new (v2) T(*static_cast<const T*>(v1));
+ return nullptr;
+ case FlagOp::kSizeof:
+ return reinterpret_cast<void*>(sizeof(T));
+ case FlagOp::kStaticTypeId:
+ return reinterpret_cast<void*>(&FlagStaticTypeIdGen<T>);
+ case FlagOp::kParse: {
+ // Initialize the temporary instance of type T based on current value in
+ // destination (which is going to be flag's default value).
+ T temp(*static_cast<T*>(v2));
+ if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp,
+ static_cast<std::string*>(v3))) {
+ return nullptr;
+ }
+ *static_cast<T*>(v2) = std::move(temp);
+ return v2;
+ }
+ case FlagOp::kUnparse:
+ *static_cast<std::string*>(v2) =
+ absl::UnparseFlag<T>(*static_cast<const T*>(v1));
+ return nullptr;
+ default:
+ return nullptr;
+ }
+}
+
+// Deletes memory interpreting obj as flag value type pointer.
+inline void Delete(FlagOpFn op, const void* obj) {
+ op(FlagOp::kDelete, obj, nullptr, nullptr);
+}
+// Makes a copy of flag value pointed by obj.
+inline void* Clone(FlagOpFn op, const void* obj) {
+ return op(FlagOp::kClone, obj, nullptr, nullptr);
+}
+// Copies src to dst interpreting as flag value type pointers.
+inline void Copy(FlagOpFn op, const void* src, void* dst) {
+ op(FlagOp::kCopy, src, dst, nullptr);
+}
+// Construct a copy of flag value in a location pointed by dst
+// based on src - pointer to the flag's value.
+inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) {
+ op(FlagOp::kCopyConstruct, src, dst, nullptr);
+}
+// Returns true if parsing of input text is successfull.
+inline bool Parse(FlagOpFn op, absl::string_view text, void* dst,
+ std::string* error) {
+ return op(FlagOp::kParse, &text, dst, error) != nullptr;
+}
+// Returns string representing supplied value.
+inline std::string Unparse(FlagOpFn op, const void* val) {
+ std::string result;
+ op(FlagOp::kUnparse, val, &result, nullptr);
+ return result;
+}
+// Returns size of flag value type.
+inline size_t Sizeof(FlagOpFn op) {
+ // This sequence of casts reverses the sequence from
+ // `flags_internal::FlagOps()`
+ return static_cast<size_t>(reinterpret_cast<intptr_t>(
+ op(FlagOp::kSizeof, nullptr, nullptr, nullptr)));
+}
+// Returns static type id coresponding to the value type.
+inline FlagStaticTypeId StaticTypeId(FlagOpFn op) {
+ return reinterpret_cast<FlagStaticTypeId>(
+ op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Persistent state of the flag data.
+
+template <typename T>
+class FlagState : public flags_internal::FlagStateInterface {
+ public:
+ FlagState(Flag<T>* flag, T&& cur, bool modified, bool on_command_line,
+ int64_t counter)
+ : flag_(flag),
+ cur_value_(std::move(cur)),
+ modified_(modified),
+ on_command_line_(on_command_line),
+ counter_(counter) {}
+
+ ~FlagState() override = default;
+
+ private:
+ friend class Flag<T>;
+
+ // Restores the flag to the saved state.
+ void Restore() const override;
+
+ // Flag and saved flag data.
+ Flag<T>* flag_;
+ T cur_value_;
+ bool modified_;
+ bool on_command_line_;
+ int64_t counter_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag help auxiliary structs.
+
+// This is help argument for absl::Flag encapsulating the string literal pointer
+// or pointer to function generating it as well as enum descriminating two
+// cases.
+using HelpGenFunc = std::string (*)();
+
+union FlagHelpMsg {
+ constexpr explicit FlagHelpMsg(const char* help_msg) : literal(help_msg) {}
+ constexpr explicit FlagHelpMsg(HelpGenFunc help_gen) : gen_func(help_gen) {}
+
+ const char* literal;
+ HelpGenFunc gen_func;
+};
+
+enum class FlagHelpKind : uint8_t { kLiteral = 0, kGenFunc = 1 };
+
+struct FlagHelpArg {
+ FlagHelpMsg source;
+ FlagHelpKind kind;
+};
+
+extern const char kStrippedFlagHelp[];
+
+// HelpConstexprWrap is used by struct AbslFlagHelpGenFor##name generated by
+// ABSL_FLAG macro. It is only used to silence the compiler in the case where
+// help message expression is not constexpr and does not have type const char*.
+// If help message expression is indeed constexpr const char* HelpConstexprWrap
+// is just a trivial identity function.
+template <typename T>
+const char* HelpConstexprWrap(const T&) {
+ return nullptr;
+}
+constexpr const char* HelpConstexprWrap(const char* p) { return p; }
+constexpr const char* HelpConstexprWrap(char* p) { return p; }
+
+// These two HelpArg overloads allows us to select at compile time one of two
+// way to pass Help argument to absl::Flag. We'll be passing
+// AbslFlagHelpGenFor##name as T and integer 0 as a single argument to prefer
+// first overload if possible. If T::Const is evaluatable on constexpr
+// context (see non template int parameter below) we'll choose first overload.
+// In this case the help message expression is immediately evaluated and is used
+// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG.
+// Otherwise SFINAE kicks in and first overload is dropped from the
+// consideration, in which case the second overload will be used. The second
+// overload does not attempt to evaluate the help message expression
+// immediately and instead delays the evaluation by returing the function
+// pointer (&T::NonConst) genering the help message when necessary. This is
+// evaluatable in constexpr context, but the cost is an extra function being
+// generated in the ABSL_FLAG code.
+template <typename T, int = (T::Const(), 1)>
+constexpr FlagHelpArg HelpArg(int) {
+ return {FlagHelpMsg(T::Const()), FlagHelpKind::kLiteral};
+}
+
+template <typename T>
+constexpr FlagHelpArg HelpArg(char) {
+ return {FlagHelpMsg(&T::NonConst), FlagHelpKind::kGenFunc};
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag default value auxiliary structs.
+
+// Signature for the function generating the initial flag value (usually
+// based on default value supplied in flag's definition)
+using FlagDfltGenFunc = void* (*)();
+
+union FlagDefaultSrc {
+ constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg)
+ : gen_func(gen_func_arg) {}
+
+ void* dynamic_value;
+ FlagDfltGenFunc gen_func;
+};
+
+enum class FlagDefaultKind : uint8_t { kDynamicValue = 0, kGenFunc = 1 };
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag current value auxiliary structs.
+
+// The minimum atomic size we believe to generate lock free code, i.e. all
+// trivially copyable types not bigger this size generate lock free code.
+static constexpr int kMinLockFreeAtomicSize = 8;
+
+// The same as kMinLockFreeAtomicSize but maximum atomic size. As double words
+// might use two registers, we want to dispatch the logic for them.
+#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
+static constexpr int kMaxLockFreeAtomicSize = 16;
+#else
+static constexpr int kMaxLockFreeAtomicSize = 8;
+#endif
+
+// We can use atomic in cases when it fits in the register, trivially copyable
+// in order to make memcpy operations.
+template <typename T>
+struct IsAtomicFlagTypeTrait {
+ static constexpr bool value =
+ (sizeof(T) <= kMaxLockFreeAtomicSize &&
+ type_traits_internal::is_trivially_copyable<T>::value);
+};
+
+// Clang does not always produce cmpxchg16b instruction when alignment of a 16
+// bytes type is not 16.
+struct alignas(16) FlagsInternalTwoWordsType {
+ int64_t first;
+ int64_t second;
+};
+
+constexpr bool operator==(const FlagsInternalTwoWordsType& that,
+ const FlagsInternalTwoWordsType& other) {
+ return that.first == other.first && that.second == other.second;
+}
+constexpr bool operator!=(const FlagsInternalTwoWordsType& that,
+ const FlagsInternalTwoWordsType& other) {
+ return !(that == other);
+}
+
+constexpr int64_t SmallAtomicInit() { return 0xababababababababll; }
+
+template <typename T, typename S = void>
+struct BestAtomicType {
+ using type = int64_t;
+ static constexpr int64_t AtomicInit() { return SmallAtomicInit(); }
+};
+
+template <typename T>
+struct BestAtomicType<
+ T, typename std::enable_if<(kMinLockFreeAtomicSize < sizeof(T) &&
+ sizeof(T) <= kMaxLockFreeAtomicSize),
+ void>::type> {
+ using type = FlagsInternalTwoWordsType;
+ static constexpr FlagsInternalTwoWordsType AtomicInit() {
+ return {SmallAtomicInit(), SmallAtomicInit()};
+ }
+};
+
+struct FlagValue {
+ // Heap allocated value.
+ void* dynamic = nullptr;
+ // For some types, a copy of the current value is kept in an atomically
+ // accessible field.
+ union Atomics {
+ // Using small atomic for small types.
+ std::atomic<int64_t> small_atomic;
+ template <typename T,
+ typename K = typename std::enable_if<
+ (sizeof(T) <= kMinLockFreeAtomicSize), void>::type>
+ int64_t load() const {
+ return small_atomic.load(std::memory_order_acquire);
+ }
+
+#if defined(ABSL_FLAGS_INTERNAL_ATOMIC_DOUBLE_WORD)
+ // Using big atomics for big types.
+ std::atomic<FlagsInternalTwoWordsType> big_atomic;
+ template <typename T, typename K = typename std::enable_if<
+ (kMinLockFreeAtomicSize < sizeof(T) &&
+ sizeof(T) <= kMaxLockFreeAtomicSize),
+ void>::type>
+ FlagsInternalTwoWordsType load() const {
+ return big_atomic.load(std::memory_order_acquire);
+ }
+ constexpr Atomics()
+ : big_atomic{FlagsInternalTwoWordsType{SmallAtomicInit(),
+ SmallAtomicInit()}} {}
+#else
+ constexpr Atomics() : small_atomic{SmallAtomicInit()} {}
+#endif
+ };
+ Atomics atomics{};
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag callback auxiliary structs.
+
+// Signature for the mutation callback used by watched Flags
+// The callback is noexcept.
+// TODO(rogeeff): add noexcept after C++17 support is added.
+using FlagCallbackFunc = void (*)();
+
+struct FlagCallback {
+ FlagCallbackFunc func;
+ absl::Mutex guard; // Guard for concurrent callback invocations.
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Flag implementation, which does not depend on flag value type.
+// The class encapsulates the Flag's data and access to it.
+
+struct DynValueDeleter {
+ explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {}
+ void operator()(void* ptr) const {
+ if (op != nullptr) Delete(op, ptr);
+ }
+
+ FlagOpFn op;
+};
+
+class FlagImpl {
public:
- constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen,
- const char* filename,
- const flags_internal::FlagMarshallingOpFn marshalling_op_arg,
- const flags_internal::InitialValGenFunc initial_value_gen)
- : flags_internal::CommandLineFlag(
- name, flags_internal::HelpText::FromFunctionPointer(help_gen),
- filename, &flags_internal::FlagOps<T>, marshalling_op_arg,
- initial_value_gen,
- /*retired_arg=*/false, /*def_arg=*/nullptr,
- /*cur_arg=*/nullptr) {}
+ constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op,
+ FlagHelpArg help, FlagDfltGenFunc default_value_gen)
+ : name_(name),
+ filename_(filename),
+ op_(op),
+ help_(help.source),
+ help_source_kind_(static_cast<uint8_t>(help.kind)),
+ def_kind_(static_cast<uint8_t>(FlagDefaultKind::kGenFunc)),
+ modified_(false),
+ on_command_line_(false),
+ counter_(0),
+ callback_(nullptr),
+ default_value_(default_value_gen),
+ data_guard_{} {}
+
+ // Constant access methods
+ absl::string_view Name() const;
+ std::string Filename() const;
+ std::string Help() const;
+ bool IsModified() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard());
+ void Read(void* dst) const ABSL_LOCKS_EXCLUDED(*DataGuard());
+
+ template <typename T, typename std::enable_if<
+ !IsAtomicFlagTypeTrait<T>::value, int>::type = 0>
+ void Get(T* dst) const {
+ AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
+ Read(dst);
+ }
+ // Overload for `GetFlag()` for types that support lock-free reads.
+ template <typename T, typename std::enable_if<IsAtomicFlagTypeTrait<T>::value,
+ int>::type = 0>
+ void Get(T* dst) const {
+ // For flags of types which can be accessed "atomically" we want to avoid
+ // slowing down flag value access due to type validation. That's why
+ // this validation is hidden behind !NDEBUG
+#ifndef NDEBUG
+ AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
+#endif
+ using U = flags_internal::BestAtomicType<T>;
+ typename U::type r = value_.atomics.template load<T>();
+ if (r != U::AtomicInit()) {
+ std::memcpy(static_cast<void*>(dst), &r, sizeof(T));
+ } else {
+ Read(dst);
+ }
+ }
+ template <typename T>
+ void Set(const T& src) {
+ AssertValidType(&flags_internal::FlagStaticTypeIdGen<T>);
+ Write(&src);
+ }
+
+ // Mutating access methods
+ void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool SetFromString(absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* err)
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+ // If possible, updates copy of the Flag's value that is stored in an
+ // atomic word.
+ void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
+ // Interfaces to operate on callbacks.
+ void SetCallback(const FlagCallbackFunc mutation_callback)
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+ void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
+ // Interfaces to save/restore mutable flag data
+ template <typename T>
+ std::unique_ptr<FlagStateInterface> SaveState(Flag<T>* flag) const
+ ABSL_LOCKS_EXCLUDED(*DataGuard()) {
+ T&& cur_value = flag->Get();
+ absl::MutexLock l(DataGuard());
+
+ return absl::make_unique<FlagState<T>>(
+ flag, std::move(cur_value), modified_, on_command_line_, counter_);
+ }
+ bool RestoreState(const void* value, bool modified, bool on_command_line,
+ int64_t counter) ABSL_LOCKS_EXCLUDED(*DataGuard());
+
+ // Value validation interfaces.
+ void CheckDefaultValueParsingRoundtrip() const
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+ bool ValidateInputValue(absl::string_view value) const
+ ABSL_LOCKS_EXCLUDED(*DataGuard());
+
+ private:
+ // Ensures that `data_guard_` is initialized and returns it.
+ absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_);
+ // Returns heap allocated value of type T initialized with default value.
+ std::unique_ptr<void, DynValueDeleter> MakeInitValue() const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+ // Flag initialization called via absl::call_once.
+ void Init();
+ // Attempts to parse supplied `value` std::string. If parsing is successful,
+ // returns new value. Otherwise returns nullptr.
+ std::unique_ptr<void, DynValueDeleter> TryParse(absl::string_view value,
+ std::string* err) const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+ // Stores the flag value based on the pointer to the source.
+ void StoreValue(const void* src) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
+
+ FlagHelpKind HelpSourceKind() const {
+ return static_cast<FlagHelpKind>(help_source_kind_);
+ }
+ FlagDefaultKind DefaultKind() const
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()) {
+ return static_cast<FlagDefaultKind>(def_kind_);
+ }
+ // Used in read/write operations to validate source/target has correct type.
+ // For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to
+ // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed
+ // int. To do that we pass the "assumed" type id (which is deduced from type
+ // int) as an argument `op`, which is in turn is validated against the type id
+ // stored in flag object by flag definition statement.
+ void AssertValidType(FlagStaticTypeId type_id) const;
+
+ // Immutable flag's state.
+
+ // Flags name passed to ABSL_FLAG as second arg.
+ const char* const name_;
+ // The file name where ABSL_FLAG resides.
+ const char* const filename_;
+ // Type-specific operations "vtable".
+ const FlagOpFn op_;
+ // Help message literal or function to generate it.
+ const FlagHelpMsg help_;
+ // Indicates if help message was supplied as literal or generator func.
+ const uint8_t help_source_kind_ : 1;
+
+ // ------------------------------------------------------------------------
+ // The bytes containing the const bitfields must not be shared with bytes
+ // containing the mutable bitfields.
+ // ------------------------------------------------------------------------
+
+ // Unique tag for absl::call_once call to initialize this flag.
+ //
+ // The placement of this variable between the immutable and mutable bitfields
+ // is important as prevents them from occupying the same byte. If you remove
+ // this variable, make sure to maintain this property.
+ absl::once_flag init_control_;
+
+ // Mutable flag's state (guarded by `data_guard_`).
+
+ // If def_kind_ == kDynamicValue, default_value_ holds a dynamically allocated
+ // value.
+ uint8_t def_kind_ : 1 ABSL_GUARDED_BY(*DataGuard());
+ // Has this flag's value been modified?
+ bool modified_ : 1 ABSL_GUARDED_BY(*DataGuard());
+ // Has this flag been specified on command line.
+ bool on_command_line_ : 1 ABSL_GUARDED_BY(*DataGuard());
+
+ // Mutation counter
+ int64_t counter_ ABSL_GUARDED_BY(*DataGuard());
+ // Optional flag's callback and absl::Mutex to guard the invocations.
+ FlagCallback* callback_ ABSL_GUARDED_BY(*DataGuard());
+ // Either a pointer to the function generating the default value based on the
+ // value specified in ABSL_FLAG or pointer to the dynamically set default
+ // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish
+ // these two cases.
+ FlagDefaultSrc default_value_ ABSL_GUARDED_BY(*DataGuard());
+ // Current Flag Value
+ FlagValue value_;
+
+ // This is reserved space for an absl::Mutex to guard flag data. It will be
+ // initialized in FlagImpl::Init via placement new.
+ // We can't use "absl::Mutex data_guard_", since this class is not literal.
+ // We do not want to use "absl::Mutex* data_guard_", since this would require
+ // heap allocation during initialization, which is both slows program startup
+ // and can fail. Using reserved space + placement new allows us to avoid both
+ // problems.
+ alignas(absl::Mutex) mutable char data_guard_[sizeof(absl::Mutex)];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// The Flag object parameterized by the flag's value type. This class implements
+// flag reflection handle interface.
+
+template <typename T>
+class Flag final : public flags_internal::CommandLineFlag {
+ public:
+ constexpr Flag(const char* name, const char* filename, const FlagHelpArg help,
+ const FlagDfltGenFunc default_value_gen)
+ : impl_(name, filename, &FlagOps<T>, help, default_value_gen) {}
T Get() const {
- // Implementation notes:
- //
- // We are wrapping a union around the value of `T` to serve three purposes:
- //
- // 1. `U.value` has correct size and alignment for a value of type `T`
- // 2. The `U.value` constructor is not invoked since U's constructor does
- // not
- // do it explicitly.
- // 3. The `U.value` destructor is invoked since U's destructor does it
- // explicitly. This makes `U` a kind of RAII wrapper around non default
- // constructible value of T, which is destructed when we leave the
- // scope. We do need to destroy U.value, which is constructed by
- // CommandLineFlag::Read even though we left it in a moved-from state
- // after std::move.
- //
- // All of this serves to avoid requiring `T` being default constructible.
+ // See implementation notes in CommandLineFlag::Get().
union U {
T value;
U() {}
@@ -62,23 +564,69 @@ class Flag : public flags_internal::CommandLineFlag {
};
U u;
- this->Read(&u.value, &flags_internal::FlagOps<T>);
+ impl_.Get(&u.value);
return std::move(u.value);
}
+ void Set(const T& v) { impl_.Set(v); }
+ void SetCallback(const FlagCallbackFunc mutation_callback) {
+ impl_.SetCallback(mutation_callback);
+ }
- bool AtomicGet(T* v) const {
- const int64_t r = this->atomic.load(std::memory_order_acquire);
- if (r != flags_internal::CommandLineFlag::kAtomicInit) {
- memcpy(v, &r, sizeof(T));
- return true;
- }
+ // CommandLineFlag interface
+ absl::string_view Name() const override { return impl_.Name(); }
+ std::string Filename() const override { return impl_.Filename(); }
+ absl::string_view Typename() const override { return ""; }
+ std::string Help() const override { return impl_.Help(); }
+ bool IsModified() const override { return impl_.IsModified(); }
+ bool IsSpecifiedOnCommandLine() const override {
+ return impl_.IsSpecifiedOnCommandLine();
+ }
+ std::string DefaultValue() const override { return impl_.DefaultValue(); }
+ std::string CurrentValue() const override { return impl_.CurrentValue(); }
+ bool ValidateInputValue(absl::string_view value) const override {
+ return impl_.ValidateInputValue(value);
+ }
+
+ // Interfaces to save and restore flags to/from persistent state.
+ // Returns current flag state or nullptr if flag does not support
+ // saving and restoring a state.
+ std::unique_ptr<FlagStateInterface> SaveState() override {
+ return impl_.SaveState(this);
+ }
- return false;
+ // Restores the flag state to the supplied state object. If there is
+ // nothing to restore returns false. Otherwise returns true.
+ bool RestoreState(const FlagState<T>& flag_state) {
+ return impl_.RestoreState(&flag_state.cur_value_, flag_state.modified_,
+ flag_state.on_command_line_, flag_state.counter_);
+ }
+ bool SetFromString(absl::string_view value, FlagSettingMode set_mode,
+ ValueSource source, std::string* error) override {
+ return impl_.SetFromString(value, set_mode, source, error);
}
+ void CheckDefaultValueParsingRoundtrip() const override {
+ impl_.CheckDefaultValueParsingRoundtrip();
+ }
+
+ private:
+ friend class FlagState<T>;
+
+ void Read(void* dst) const override { impl_.Read(dst); }
+ FlagStaticTypeId TypeId() const override { return &FlagStaticTypeIdGen<T>; }
- void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps<T>); }
+ // Flag's data
+ FlagImpl impl_;
};
+template <typename T>
+inline void FlagState<T>::Restore() const {
+ if (flag_->RestoreState(*this)) {
+ ABSL_INTERNAL_LOG(INFO,
+ absl::StrCat("Restore saved value of ", flag_->Name(),
+ " to: ", flag_->CurrentValue()));
+ }
+}
+
// This class facilitates Flag object registration and tail expression-based
// flag definition, for example:
// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
@@ -89,7 +637,7 @@ class FlagRegistrar {
if (do_register) flags_internal::RegisterCommandLineFlag(flag_);
}
- FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && {
+ FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && {
flag_->SetCallback(cb);
return *this;
}
@@ -117,7 +665,7 @@ T* MakeFromDefaultValue(EmptyBraces) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_FLAG_H_
diff --git a/absl/flags/internal/parse.h b/absl/flags/internal/parse.h
index 6c79dc87..03e8a07b 100644
--- a/absl/flags/internal/parse.h
+++ b/absl/flags/internal/parse.h
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
+#include "absl/base/config.h"
#include "absl/flags/declare.h"
ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile);
@@ -27,7 +28,7 @@ ABSL_DECLARE_FLAG(std::vector<std::string>, tryfromenv);
ABSL_DECLARE_FLAG(std::vector<std::string>, undefok);
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs };
@@ -44,7 +45,7 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[],
OnUndefinedFlag on_undef_flag);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_PARSE_H_
diff --git a/absl/flags/internal/path_util.h b/absl/flags/internal/path_util.h
index 623a7bc9..365c8305 100644
--- a/absl/flags/internal/path_util.h
+++ b/absl/flags/internal/path_util.h
@@ -16,11 +16,12 @@
#ifndef ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
#define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
+#include "absl/base/config.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// A portable interface that returns the basename of the filename passed as an
@@ -56,7 +57,7 @@ inline absl::string_view Package(absl::string_view filename) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_PATH_UTIL_H_
diff --git a/absl/flags/internal/program_name.cc b/absl/flags/internal/program_name.cc
index 28b8ed38..51d698da 100644
--- a/absl/flags/internal/program_name.cc
+++ b/absl/flags/internal/program_name.cc
@@ -17,16 +17,21 @@
#include <string>
+#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/internal/path_util.h"
+#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
ABSL_CONST_INIT static absl::Mutex program_name_guard(absl::kConstInit);
ABSL_CONST_INIT static std::string* program_name
- GUARDED_BY(program_name_guard) = nullptr;
+ ABSL_GUARDED_BY(program_name_guard) = nullptr;
std::string ProgramInvocationName() {
absl::MutexLock l(&program_name_guard);
@@ -51,5 +56,5 @@ void SetProgramInvocationName(absl::string_view prog_name_str) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/program_name.h b/absl/flags/internal/program_name.h
index a2f0ca10..b99b94fe 100644
--- a/absl/flags/internal/program_name.h
+++ b/absl/flags/internal/program_name.h
@@ -18,13 +18,14 @@
#include <string>
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Program name
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// Returns program invocation name or "UNKNOWN" if `SetProgramInvocationName()`
@@ -43,7 +44,7 @@ std::string ShortProgramInvocationName();
void SetProgramInvocationName(absl::string_view prog_name_str);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_
diff --git a/absl/flags/internal/program_name_test.cc b/absl/flags/internal/program_name_test.cc
index ed69218b..269142f2 100644
--- a/absl/flags/internal/program_name_test.cc
+++ b/absl/flags/internal/program_name_test.cc
@@ -15,8 +15,11 @@
#include "absl/flags/internal/program_name.h"
+#include <string>
+
#include "gtest/gtest.h"
#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
namespace {
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc
index e39264e5..e434a859 100644
--- a/absl/flags/internal/registry.cc
+++ b/absl/flags/internal/registry.cc
@@ -15,9 +15,20 @@
#include "absl/flags/internal/registry.h"
-#include "absl/base/dynamic_annotations.h"
+#include <assert.h>
+#include <stdlib.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
-#include "absl/flags/config.h"
+#include "absl/base/thread_annotations.h"
+#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/usage_config.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
@@ -30,20 +41,8 @@
// set it.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-namespace {
-
-void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
- flag->Destroy();
-
- // CommandLineFlag handle object is heap allocated for non Abseil Flags.
- if (!flag->IsAbseilFlag()) {
- delete flag;
- }
-}
-
-} // namespace
// --------------------------------------------------------------------
// FlagRegistry
@@ -58,17 +57,13 @@ void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS {
class FlagRegistry {
public:
FlagRegistry() = default;
- ~FlagRegistry() {
- for (auto& p : flags_) {
- DestroyFlag(p.second);
- }
- }
+ ~FlagRegistry() = default;
// Store a flag in this registry. Takes ownership of *flag.
void RegisterFlag(CommandLineFlag* flag);
- void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
- void Unlock() UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
+ void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); }
+ void Unlock() ABSL_UNLOCK_FUNCTION(lock_) { lock_.Unlock(); }
// Returns the flag object for the specified name, or nullptr if not found.
// Will emit a warning if a 'retired' flag is specified.
@@ -114,6 +109,7 @@ class FlagRegistryLock {
FlagRegistry* const fr_;
};
+void DestroyRetiredFlag(CommandLineFlag* flag);
} // namespace
void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
@@ -131,7 +127,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
(flag->IsRetired() ? old_flag->Filename() : flag->Filename()),
"'."),
true);
- } else if (flag->op != old_flag->op) {
+ } else if (flag->TypeId() != old_flag->TypeId()) {
flags_internal::ReportUsageError(
absl::StrCat("Flag '", flag->Name(),
"' was defined more than once but with "
@@ -141,8 +137,8 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
flag->Typename(), "', respectively."),
true);
} else if (old_flag->IsRetired()) {
- // Retired definitions are idempotent. Just keep the old one.
- DestroyFlag(flag);
+ // Retired flag can just be deleted.
+ DestroyRetiredFlag(flag);
return;
} else if (old_flag->Filename() != flag->Filename()) {
flags_internal::ReportUsageError(
@@ -201,112 +197,34 @@ CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) {
class FlagSaverImpl {
public:
- // Constructs an empty FlagSaverImpl object.
- FlagSaverImpl() {}
- ~FlagSaverImpl() {
- // reclaim memory from each of our CommandLineFlags
- for (const SavedFlag& src : backup_registry_) {
- Delete(src.op, src.current);
- Delete(src.op, src.default_value);
- }
- }
+ FlagSaverImpl() = default;
+ FlagSaverImpl(const FlagSaverImpl&) = delete;
+ void operator=(const FlagSaverImpl&) = delete;
// Saves the flag states from the flag registry into this object.
// It's an error to call this more than once.
- // Must be called when the registry mutex is not held.
void SaveFromRegistry() {
assert(backup_registry_.empty()); // call only once!
- SavedFlag saved;
flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) {
- if (flag->IsRetired()) return;
-
- saved.name = flag->Name();
- saved.op = flag->op;
- saved.marshalling_op = flag->marshalling_op;
- {
- absl::MutexLock l(flag->InitFlagIfNecessary());
- saved.validator = flag->validator;
- saved.modified = flag->modified;
- saved.on_command_line = flag->on_command_line;
- saved.current = Clone(saved.op, flag->cur);
- saved.default_value = Clone(saved.op, flag->def);
- saved.counter = flag->counter;
+ if (auto flag_state = flag->SaveState()) {
+ backup_registry_.emplace_back(std::move(flag_state));
}
- backup_registry_.push_back(saved);
});
}
- // Restores the saved flag states into the flag registry. We
- // assume no flags were added or deleted from the registry since
- // the SaveFromRegistry; if they were, that's trouble! Must be
- // called when the registry mutex is not held.
+ // Restores the saved flag states into the flag registry.
void RestoreToRegistry() {
- FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry();
- FlagRegistryLock frl(global_registry);
- for (const SavedFlag& src : backup_registry_) {
- CommandLineFlag* flag = global_registry->FindFlagLocked(src.name);
- // If null, flag got deleted from registry.
- if (!flag) continue;
-
- bool restored = false;
- {
- absl::MutexLock l(flag->InitFlagIfNecessary());
- flag->validator = src.validator;
- flag->modified = src.modified;
- flag->on_command_line = src.on_command_line;
- if (flag->counter != src.counter ||
- ChangedDirectly(flag, src.default_value, flag->def)) {
- restored = true;
- Copy(src.op, src.default_value, flag->def);
- }
- if (flag->counter != src.counter ||
- ChangedDirectly(flag, src.current, flag->cur)) {
- restored = true;
- Copy(src.op, src.current, flag->cur);
- UpdateCopy(flag);
- flag->InvokeCallback();
- }
- }
-
- if (restored) {
- flag->counter++;
-
- // Revalidate the flag because the validator might store state based
- // on the flag's value, which just changed due to the restore.
- // Failing validation is ignored because it's assumed that the flag
- // was valid previously and there's little that can be done about it
- // here, anyway.
- flag->ValidateInputValue(flag->CurrentValue());
-
- ABSL_INTERNAL_LOG(
- INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ",
- Unparse(src.marshalling_op, src.current)));
- }
+ for (const auto& flag_state : backup_registry_) {
+ flag_state->Restore();
}
}
private:
- struct SavedFlag {
- absl::string_view name;
- FlagOpFn op;
- FlagMarshallingOpFn marshalling_op;
- int64_t counter;
- bool modified;
- bool on_command_line;
- bool (*validator)();
- const void* current; // nullptr after restore
- const void* default_value; // nullptr after restore
- };
-
- std::vector<SavedFlag> backup_registry_;
-
- FlagSaverImpl(const FlagSaverImpl&); // no copying!
- void operator=(const FlagSaverImpl&);
+ std::vector<std::unique_ptr<flags_internal::FlagStateInterface>>
+ backup_registry_;
};
-FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) {
- impl_->SaveFromRegistry();
-}
+FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); }
void FlagSaver::Ignore() {
delete impl_;
@@ -321,44 +239,6 @@ FlagSaver::~FlagSaver() {
}
// --------------------------------------------------------------------
-// GetAllFlags()
-// The main way the FlagRegistry class exposes its data. This
-// returns, as strings, all the info about all the flags in
-// the main registry, sorted first by filename they are defined
-// in, and then by flagname.
-// --------------------------------------------------------------------
-
-struct FilenameFlagnameLess {
- bool operator()(const CommandLineFlagInfo& a,
- const CommandLineFlagInfo& b) const {
- int cmp = absl::string_view(a.filename).compare(b.filename);
- if (cmp != 0) return cmp < 0;
- return a.name < b.name;
- }
-};
-
-void FillCommandLineFlagInfo(CommandLineFlag* flag,
- CommandLineFlagInfo* result) {
- result->name = std::string(flag->Name());
- result->type = std::string(flag->Typename());
- result->description = flag->Help();
- result->filename = flag->Filename();
-
- if (!flag->IsAbseilFlag()) {
- if (!flag->IsModified() && ChangedDirectly(flag, flag->cur, flag->def)) {
- flag->modified = true;
- }
- }
-
- result->current_value = flag->CurrentValue();
- result->default_value = flag->DefaultValue();
- result->is_default = !flag->IsModified();
- result->has_validator_fn = flag->HasValidatorFn();
- absl::MutexLock l(flag->InitFlagIfNecessary());
- result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur;
-}
-
-// --------------------------------------------------------------------
CommandLineFlag* FindCommandLineFlag(absl::string_view name) {
if (name.empty()) return nullptr;
@@ -393,21 +273,6 @@ void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) {
// --------------------------------------------------------------------
-void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT) {
- flags_internal::ForEachFlag([&](CommandLineFlag* flag) {
- if (flag->IsRetired()) return;
-
- CommandLineFlagInfo fi;
- FillCommandLineFlagInfo(flag, &fi);
- OUTPUT->push_back(fi);
- });
-
- // Now sort the flags, first by filename they occur in, then alphabetically
- std::sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameLess());
-}
-
-// --------------------------------------------------------------------
-
bool RegisterCommandLineFlag(CommandLineFlag* flag) {
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
return true;
@@ -415,14 +280,55 @@ bool RegisterCommandLineFlag(CommandLineFlag* flag) {
// --------------------------------------------------------------------
-bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
- const char* name) {
- auto* flag = new CommandLineFlag(
- name,
- /*help_text=*/absl::flags_internal::HelpText::FromStaticCString(nullptr),
- /*filename_arg=*/"RETIRED", ops, marshalling_ops,
- /*initial_value_gen=*/nullptr,
- /*retired_arg=*/true, nullptr, nullptr);
+namespace {
+
+class RetiredFlagObj final : public flags_internal::CommandLineFlag {
+ public:
+ constexpr RetiredFlagObj(const char* name, FlagStaticTypeId type_id)
+ : name_(name), type_id_(type_id) {}
+
+ private:
+ absl::string_view Name() const override { return name_; }
+ std::string Filename() const override { return "RETIRED"; }
+ absl::string_view Typename() const override { return ""; }
+ FlagStaticTypeId TypeId() const override { return type_id_; }
+ std::string Help() const override { return ""; }
+ bool IsRetired() const override { return true; }
+ bool IsModified() const override { return false; }
+ bool IsSpecifiedOnCommandLine() const override { return false; }
+ std::string DefaultValue() const override { return ""; }
+ std::string CurrentValue() const override { return ""; }
+
+ // Any input is valid
+ bool ValidateInputValue(absl::string_view) const override { return true; }
+
+ std::unique_ptr<flags_internal::FlagStateInterface> SaveState() override {
+ return nullptr;
+ }
+
+ bool SetFromString(absl::string_view, flags_internal::FlagSettingMode,
+ flags_internal::ValueSource, std::string*) override {
+ return false;
+ }
+
+ void CheckDefaultValueParsingRoundtrip() const override {}
+
+ void Read(void*) const override {}
+
+ // Data members
+ const char* const name_;
+ const FlagStaticTypeId type_id_;
+};
+
+void DestroyRetiredFlag(flags_internal::CommandLineFlag* flag) {
+ assert(flag->IsRetired());
+ delete static_cast<RetiredFlagObj*>(flag);
+}
+
+} // namespace
+
+bool Retire(const char* name, FlagStaticTypeId type_id) {
+ auto* flag = new flags_internal::RetiredFlagObj(name, type_id);
FlagRegistry::GlobalRegistry()->RegisterFlag(flag);
return true;
}
@@ -441,5 +347,5 @@ bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h
index d851d245..69ff889f 100644
--- a/absl/flags/internal/registry.h
+++ b/absl/flags/internal/registry.h
@@ -20,43 +20,18 @@
#include <map>
#include <string>
+#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/flags/internal/commandlineflag.h"
+#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Global flags registry API.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
-// CommandLineFlagInfo holds all information for a flag.
-struct CommandLineFlagInfo {
- std::string name; // the name of the flag
- std::string type; // DO NOT use. Use flag->IsOfType<T>() instead.
- std::string description; // the "help text" associated with the flag
- std::string current_value; // the current value, as a std::string
- std::string default_value; // the default value, as a std::string
- std::string filename; // 'cleaned' version of filename holding the flag
- bool has_validator_fn; // true if RegisterFlagValidator called on this flag
-
- bool is_default; // true if the flag has the default value and
- // has not been set explicitly from the cmdline
- // or via SetCommandLineOption.
-
- // nullptr for ABSL_FLAG. A pointer to the flag's current value
- // otherwise. E.g., for DEFINE_int32(foo, ...), flag_ptr will be
- // &FLAGS_foo.
- const void* flag_ptr;
-};
-
-//-----------------------------------------------------------------------------
-
-void FillCommandLineFlagInfo(CommandLineFlag* flag,
- CommandLineFlagInfo* result);
-
-//-----------------------------------------------------------------------------
-
CommandLineFlag* FindCommandLineFlag(absl::string_view name);
CommandLineFlag* FindRetiredFlag(absl::string_view name);
@@ -69,11 +44,6 @@ void ForEachFlag(std::function<void(CommandLineFlag*)> visitor);
//-----------------------------------------------------------------------------
-// Store the list of all flags in *OUTPUT, sorted by file.
-void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT);
-
-//-----------------------------------------------------------------------------
-
bool RegisterCommandLineFlag(CommandLineFlag*);
//-----------------------------------------------------------------------------
@@ -107,29 +77,14 @@ bool RegisterCommandLineFlag(CommandLineFlag*);
// 4: Remove the old_lib 'retired' registration.
// 5: Eventually delete the graveyard registration entirely.
//
-// Returns bool to enable use in namespace-scope initializers.
-// For example:
-//
-// static const bool dummy = base::RetiredFlag<int32_t>("myflag");
-//
-// Or to declare several at once:
-//
-// static bool dummies[] = {
-// base::RetiredFlag<std::string>("some_string_flag"),
-// base::RetiredFlag<double>("some_double_flag"),
-// base::RetiredFlag<int32_t>("some_int32_flag")
-// };
// Retire flag with name "name" and type indicated by ops.
-bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops,
- const char* name);
+bool Retire(const char* name, FlagStaticTypeId type_id);
// Registered a retired flag with name 'flag_name' and type 'T'.
template <typename T>
inline bool RetiredFlag(const char* flag_name) {
- return flags_internal::Retire(flags_internal::FlagOps<T>,
- flags_internal::FlagMarshallingOps<T>,
- flag_name);
+ return flags_internal::Retire(flag_name, &FlagStaticTypeIdGen<T>);
}
// If the flag is retired, returns true and indicates in |*type_is_bool|
@@ -163,7 +118,7 @@ class FlagSaver {
};
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_REGISTRY_H_
diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc
index 6b91b261..490bc4eb 100644
--- a/absl/flags/internal/type_erased.cc
+++ b/absl/flags/internal/type_erased.cc
@@ -15,13 +15,19 @@
#include "absl/flags/internal/type_erased.h"
+#include <assert.h>
+
+#include <string>
+
+#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
-#include "absl/flags/config.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/registry.h"
#include "absl/flags/usage_config.h"
-#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
bool GetCommandLineOption(absl::string_view name, std::string* value) {
@@ -37,30 +43,6 @@ bool GetCommandLineOption(absl::string_view name, std::string* value) {
return true;
}
-bool GetCommandLineFlagInfo(absl::string_view name,
- CommandLineFlagInfo* OUTPUT) {
- if (name.empty()) return false;
-
- CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name);
- if (flag == nullptr || flag->IsRetired()) {
- return false;
- }
-
- assert(OUTPUT);
- FillCommandLineFlagInfo(flag, OUTPUT);
- return true;
-}
-
-CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name) {
- CommandLineFlagInfo info;
- if (!GetCommandLineFlagInfo(name, &info)) {
- ABSL_INTERNAL_LOG(FATAL, absl::StrCat("Flag '", name, "' does not exist"));
- }
- return info;
-}
-
-// --------------------------------------------------------------------
-
bool SetCommandLineOption(absl::string_view name, absl::string_view value) {
return SetCommandLineOptionWithMode(name, value,
flags_internal::SET_FLAGS_VALUE);
@@ -104,5 +86,5 @@ bool SpecifiedOnCommandLine(absl::string_view name) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/type_erased.h b/absl/flags/internal/type_erased.h
index c9de6de9..188429c7 100644
--- a/absl/flags/internal/type_erased.h
+++ b/absl/flags/internal/type_erased.h
@@ -18,14 +18,16 @@
#include <string>
+#include "absl/base/config.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/flags/internal/registry.h"
+#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Registry interfaces operating on type erased handles.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// If a flag named "name" exists, store its current value in *OUTPUT
@@ -33,17 +35,6 @@ namespace flags_internal {
// Thread-safe.
bool GetCommandLineOption(absl::string_view name, std::string* value);
-// If a flag named "name" exists, store its information in *OUTPUT
-// and return true. Else return false without changing *OUTPUT.
-// Thread-safe.
-bool GetCommandLineFlagInfo(absl::string_view name,
- CommandLineFlagInfo* OUTPUT);
-
-// Returns the CommandLineFlagInfo of the flagname. exit() with an
-// error code if name not found.
-// Thread-safe.
-CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name);
-
// Set the value of the flag named "name" to value. If successful,
// returns true. If not successful (e.g., the flag was not found or
// the value is not a valid value), returns false.
@@ -93,7 +84,7 @@ inline bool GetByName(absl::string_view name, T* dst) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_
diff --git a/absl/flags/internal/type_erased_test.cc b/absl/flags/internal/type_erased_test.cc
index ac749a60..4ce59810 100644
--- a/absl/flags/internal/type_erased_test.cc
+++ b/absl/flags/internal/type_erased_test.cc
@@ -15,12 +15,15 @@
#include "absl/flags/internal/type_erased.h"
-#include <cmath>
+#include <memory>
+#include <string>
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/registry.h"
+#include "absl/flags/marshalling.h"
#include "absl/memory/memory.h"
-#include "absl/strings/str_cat.h"
ABSL_FLAG(int, int_flag, 1, "int_flag help");
ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help");
@@ -116,6 +119,13 @@ TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_DEFAULT) {
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101",
flags::SET_FLAGS_DEFAULT));
+ // Set it again to ensure that resetting logic is covered.
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "102",
+ flags::SET_FLAGS_DEFAULT));
+
+ EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "103",
+ flags::SET_FLAGS_DEFAULT));
+
EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh",
flags::SET_FLAGS_DEFAULT));
EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh");
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc
index a9a8a21e..ff907161 100644
--- a/absl/flags/internal/usage.cc
+++ b/absl/flags/internal/usage.cc
@@ -15,18 +15,24 @@
#include "absl/flags/internal/usage.h"
+#include <functional>
#include <map>
+#include <ostream>
#include <string>
+#include <utility>
+#include <vector>
+#include "absl/base/config.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/flag.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
+#include "absl/flags/internal/registry.h"
#include "absl/flags/usage_config.h"
-#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
-#include "absl/synchronization/mutex.h"
ABSL_FLAG(bool, help, false,
"show help on important flags for this binary [tip: all flags can "
@@ -44,10 +50,31 @@ ABSL_FLAG(std::string, helpmatch, "",
"show help on modules whose name contains the specified substr");
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
+absl::string_view TypenameForHelp(const flags_internal::CommandLineFlag& flag) {
+ // Only report names of v1 built-in types
+#define HANDLE_V1_BUILTIN_TYPE(t) \
+ if (flag.IsOfType<t>()) { \
+ return #t; \
+ }
+
+ HANDLE_V1_BUILTIN_TYPE(bool);
+ HANDLE_V1_BUILTIN_TYPE(int32_t);
+ HANDLE_V1_BUILTIN_TYPE(int64_t);
+ HANDLE_V1_BUILTIN_TYPE(uint64_t);
+ HANDLE_V1_BUILTIN_TYPE(double);
+#undef HANDLE_V1_BUILTIN_TYPE
+
+ if (flag.IsOfType<std::string>()) {
+ return "string";
+ }
+
+ return "";
+}
+
// This class is used to emit an XML element with `tag` and `text`.
// It adds opening and closing tags and escapes special characters in the text.
// For example:
@@ -180,14 +207,14 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag,
FlagHelpPrettyPrinter printer(80, out); // Max line length is 80.
// Flag name.
- printer.Write(absl::StrCat("-", flag.Name()));
+ printer.Write(absl::StrCat("--", flag.Name()));
// Flag help.
printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true);
// Flag data type (for V1 flags only).
if (!flag.IsAbseilFlag() && !flag.IsRetired()) {
- printer.Write(absl::StrCat("type: ", flag.Typename(), ";"));
+ printer.Write(absl::StrCat("type: ", TypenameForHelp(flag), ";"));
}
// The listed default value will be the actual default from the flag
@@ -224,6 +251,9 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb,
} else {
// XML schema is not a part of our public API for now.
out << "<?xml version=\"1.0\"?>\n"
+ << "<!-- This output should be used with care. We do not report type "
+ "names for flags with user defined types -->\n"
+ << "<!-- Prefer flag only_check_args for validating flag inputs -->\n"
// The document.
<< "<AllFlags>\n"
// The program name and usage.
@@ -381,5 +411,5 @@ int HandleUsageFlags(std::ostream& out,
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h
index f3794afe..6b080fd1 100644
--- a/absl/flags/internal/usage.h
+++ b/absl/flags/internal/usage.h
@@ -19,6 +19,7 @@
#include <iosfwd>
#include <string>
+#include "absl/base/config.h"
#include "absl/flags/declare.h"
#include "absl/flags/internal/commandlineflag.h"
#include "absl/strings/string_view.h"
@@ -27,7 +28,7 @@
// Usage reporting interfaces
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// The format to report the help messages in.
@@ -65,7 +66,7 @@ int HandleUsageFlags(std::ostream& out,
absl::string_view program_usage_message);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
ABSL_DECLARE_FLAG(bool, help);
diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc
index 781e1d2b..e1e57e55 100644
--- a/absl/flags/internal/usage_test.cc
+++ b/absl/flags/internal/usage_test.cc
@@ -15,17 +15,23 @@
#include "absl/flags/internal/usage.h"
+#include <stdint.h>
+
#include <sstream>
+#include <string>
#include "gtest/gtest.h"
+#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/parse.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
-#include "absl/flags/parse.h"
+#include "absl/flags/internal/registry.h"
#include "absl/flags/usage.h"
#include "absl/flags/usage_config.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
ABSL_FLAG(int, usage_reporting_test_flag_01, 101,
"usage_reporting_test_flag_01 help message");
@@ -67,9 +73,9 @@ static std::string NormalizeFileName(absl::string_view fname) {
fname = normalized;
#endif
- auto absl_pos = fname.find("/absl/");
+ auto absl_pos = fname.rfind("absl/");
if (absl_pos != absl::string_view::npos) {
- fname = fname.substr(absl_pos + 1);
+ fname = fname.substr(absl_pos);
}
return std::string(fname);
}
@@ -111,7 +117,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ R"( --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
)");
}
@@ -123,7 +129,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ R"( --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
)");
}
@@ -135,7 +141,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ R"( --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
)");
}
@@ -147,7 +153,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ R"( --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
)");
}
@@ -159,7 +165,7 @@ TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) {
flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable);
EXPECT_EQ(
test_buf.str(),
- R"( -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ R"( --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
)");
}
@@ -171,17 +177,17 @@ TEST_F(UsageReportingTest, TestFlagsHelpHRF) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -247,17 +253,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpshort) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -276,17 +282,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_help) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -307,17 +313,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helppackage) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -372,17 +378,17 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
R"(usage_test: Custom usage message
Flags from absl/flags/internal/usage_test.cc:
- -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
+ --usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message);
default: 101;
- -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
+ --usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message);
default: false;
- -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
+ --usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message);
default: 1.03;
- -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
+ --usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message);
default: 1000000000000004;
- -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
+ --usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message);
default: UDT{};
- -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
+ --usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message.
Some more help.
Even more long long long long long long long long long long long long help
@@ -395,7 +401,7 @@ TEST_F(UsageReportingTest, TestUsageFlag_helpon) {
} // namespace
int main(int argc, char* argv[]) {
- absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
+ (void)absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc
flags::SetProgramInvocationName("usage_test");
absl::SetProgramUsageMessage(kTestUsageMessage);
::testing::InitGoogleTest(&argc, argv);
diff --git a/absl/flags/marshalling.cc b/absl/flags/marshalling.cc
index 71b01d77..6f2ddda8 100644
--- a/absl/flags/marshalling.cc
+++ b/absl/flags/marshalling.cc
@@ -15,18 +15,28 @@
#include "absl/flags/marshalling.h"
+#include <stddef.h>
+
+#include <cmath>
#include <limits>
+#include <string>
+#include <type_traits>
+#include <vector>
+#include "absl/base/config.h"
+#include "absl/base/log_severity.h"
#include "absl/base/macros.h"
+#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
+#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// --------------------------------------------------------------------
@@ -187,5 +197,44 @@ std::string AbslUnparseFlag(const std::vector<std::string>& v) {
}
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+
+bool AbslParseFlag(absl::string_view text, absl::LogSeverity* dst,
+ std::string* err) {
+ text = absl::StripAsciiWhitespace(text);
+ if (text.empty()) {
+ *err = "no value provided";
+ return false;
+ }
+ if (text.front() == 'k' || text.front() == 'K') text.remove_prefix(1);
+ if (absl::EqualsIgnoreCase(text, "info")) {
+ *dst = absl::LogSeverity::kInfo;
+ return true;
+ }
+ if (absl::EqualsIgnoreCase(text, "warning")) {
+ *dst = absl::LogSeverity::kWarning;
+ return true;
+ }
+ if (absl::EqualsIgnoreCase(text, "error")) {
+ *dst = absl::LogSeverity::kError;
+ return true;
+ }
+ if (absl::EqualsIgnoreCase(text, "fatal")) {
+ *dst = absl::LogSeverity::kFatal;
+ return true;
+ }
+ std::underlying_type<absl::LogSeverity>::type numeric_value;
+ if (absl::ParseFlag(text, &numeric_value, err)) {
+ *dst = static_cast<absl::LogSeverity>(numeric_value);
+ return true;
+ }
+ *err = "only integers and absl::LogSeverity enumerators are accepted";
+ return false;
+}
+
+std::string AbslUnparseFlag(absl::LogSeverity v) {
+ if (v == absl::NormalizeLogSeverity(v)) return absl::LogSeverityName(v);
+ return absl::UnparseFlag(static_cast<int>(v));
+}
+
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h
index 5598d444..0b503354 100644
--- a/absl/flags/marshalling.h
+++ b/absl/flags/marshalling.h
@@ -33,15 +33,16 @@
// * `double`
// * `std::string`
// * `std::vector<std::string>`
+// * `absl::LogSeverity` (provided natively for layering reasons)
//
// Note that support for integral types is implemented using overloads for
// variable-width fundamental types (`short`, `int`, `long`, etc.). However,
// you should prefer the fixed-width integral types (`int32_t`, `uint64_t`,
// etc.) we've noted above within flag definitions.
-
//
// In addition, several Abseil libraries provide their own custom support for
-// Abseil flags.
+// Abseil flags. Documentation for these formats is provided in the type's
+// `AbslParseFlag()` definition.
//
// The Abseil time library provides the following support for civil time values:
//
@@ -164,10 +165,11 @@
#include <string>
#include <vector>
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
// Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types.
@@ -179,8 +181,8 @@ bool AbslParseFlag(absl::string_view, unsigned int*, std::string*); // NOLINT
bool AbslParseFlag(absl::string_view, long*, std::string*); // NOLINT
bool AbslParseFlag(absl::string_view, unsigned long*, std::string*); // NOLINT
bool AbslParseFlag(absl::string_view, long long*, std::string*); // NOLINT
-bool AbslParseFlag(absl::string_view, unsigned long long*,
- std::string*); // NOLINT
+bool AbslParseFlag(absl::string_view, unsigned long long*, // NOLINT
+ std::string*);
bool AbslParseFlag(absl::string_view, float*, std::string*);
bool AbslParseFlag(absl::string_view, double*, std::string*);
bool AbslParseFlag(absl::string_view, std::string*, std::string*);
@@ -249,7 +251,14 @@ inline std::string UnparseFlag(const T& v) {
return flags_internal::Unparse(v);
}
-} // inline namespace lts_2019_08_08
+// Overloads for `absl::LogSeverity` can't (easily) appear alongside that type's
+// definition because it is layered below flags. See proper documentation in
+// base/log_severity.h.
+enum class LogSeverity : int;
+bool AbslParseFlag(absl::string_view, absl::LogSeverity*, std::string*);
+std::string AbslUnparseFlag(absl::LogSeverity);
+
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_MARSHALLING_H_
diff --git a/absl/flags/marshalling_test.cc b/absl/flags/marshalling_test.cc
index 37cd1940..4a64ce11 100644
--- a/absl/flags/marshalling_test.cc
+++ b/absl/flags/marshalling_test.cc
@@ -15,7 +15,12 @@
#include "absl/flags/marshalling.h"
+#include <stdint.h>
+
#include <cmath>
+#include <limits>
+#include <string>
+#include <vector>
#include "gtest/gtest.h"
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index fd80a0c8..812e4981 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -17,43 +17,58 @@
#include <stdlib.h>
+#include <algorithm>
#include <fstream>
#include <iostream>
+#include <iterator>
+#include <string>
#include <tuple>
+#include <utility>
+#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
+#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/config.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/commandlineflag.h"
+#include "absl/flags/internal/flag.h"
+#include "absl/flags/internal/parse.h"
#include "absl/flags/internal/program_name.h"
#include "absl/flags/internal/registry.h"
#include "absl/flags/internal/usage.h"
#include "absl/flags/usage.h"
#include "absl/flags/usage_config.h"
+#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/synchronization/mutex.h"
// --------------------------------------------------------------------
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
ABSL_CONST_INIT absl::Mutex processing_checks_guard(absl::kConstInit);
ABSL_CONST_INIT bool flagfile_needs_processing
- GUARDED_BY(processing_checks_guard) = false;
+ ABSL_GUARDED_BY(processing_checks_guard) = false;
ABSL_CONST_INIT bool fromenv_needs_processing
- GUARDED_BY(processing_checks_guard) = false;
+ ABSL_GUARDED_BY(processing_checks_guard) = false;
ABSL_CONST_INIT bool tryfromenv_needs_processing
- GUARDED_BY(processing_checks_guard) = false;
+ ABSL_GUARDED_BY(processing_checks_guard) = false;
} // namespace
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
ABSL_FLAG(std::vector<std::string>, flagfile, {},
@@ -111,7 +126,7 @@ ABSL_FLAG(std::vector<std::string>, undefok, {},
"with that name");
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
@@ -280,9 +295,7 @@ void CheckDefaultValuesParsingRoundtrip() {
#define IGNORE_TYPE(T) \
if (flag->IsOfType<T>()) return;
- ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(IGNORE_TYPE)
- IGNORE_TYPE(std::string)
- IGNORE_TYPE(std::vector<std::string>)
+ ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(IGNORE_TYPE)
#undef IGNORE_TYPE
flag->CheckDefaultValueParsingRoundtrip();
@@ -525,7 +538,7 @@ std::tuple<bool, absl::string_view> DeduceFlagValue(const CommandLineFlag& flag,
// --my_string_var --foo=bar
// We look for a flag of std::string type, whose value begins with a
// dash and corresponds to known flag or standalone --.
- if (value[0] == '-' && flag.IsOfType<std::string>()) {
+ if (!value.empty() && value[0] == '-' && flag.IsOfType<std::string>()) {
auto maybe_flag_name = std::get<0>(SplitNameAndValue(value.substr(1)));
if (maybe_flag_name.empty() ||
@@ -751,5 +764,5 @@ std::vector<char*> ParseCommandLine(int argc, char* argv[]) {
flags_internal::OnUndefinedFlag::kAbortIfUndefined);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/parse.h b/absl/flags/parse.h
index 469bd506..f37b0602 100644
--- a/absl/flags/parse.h
+++ b/absl/flags/parse.h
@@ -26,10 +26,11 @@
#include <string>
#include <vector>
+#include "absl/base/config.h"
#include "absl/flags/internal/parse.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// ParseCommandLine()
//
@@ -54,7 +55,7 @@ inline namespace lts_2019_08_08 {
// help messages and then exits the program.
std::vector<char*> ParseCommandLine(int argc, char* argv[]);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_PARSE_H_
diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc
index 447a3bc7..6f49377a 100644
--- a/absl/flags/parse_test.cc
+++ b/absl/flags/parse_test.cc
@@ -15,14 +15,22 @@
#include "absl/flags/parse.h"
+#include <stdlib.h>
+
#include <fstream>
+#include <string>
+#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/scoped_set_env.h"
+#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
+#include "absl/flags/internal/parse.h"
+#include "absl/flags/internal/registry.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "absl/types/span.h"
@@ -92,8 +100,11 @@ const std::string& GetTestTempDir() {
auto len = GetTempPathA(MAX_PATH, temp_path_buffer);
if (len < MAX_PATH && len != 0) {
- std::string temp_dir_name = absl::StrCat(
- temp_path_buffer, "\\parse_test.", GetCurrentProcessId());
+ std::string temp_dir_name = temp_path_buffer;
+ if (!absl::EndsWith(temp_dir_name, "\\")) {
+ temp_dir_name.push_back('\\');
+ }
+ absl::StrAppend(&temp_dir_name, "parse_test.", GetCurrentProcessId());
if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) {
*res = temp_dir_name;
}
@@ -104,11 +115,11 @@ const std::string& GetTestTempDir() {
*res = unique_name;
}
#endif
+ }
- if (res->empty()) {
- ABSL_INTERNAL_LOG(FATAL,
- "Failed to make temporary directory for data files");
- }
+ if (res->empty()) {
+ ABSL_INTERNAL_LOG(FATAL,
+ "Failed to make temporary directory for data files");
}
#ifdef _WIN32
diff --git a/absl/flags/usage.cc b/absl/flags/usage.cc
index 12c346b7..452f6675 100644
--- a/absl/flags/usage.cc
+++ b/absl/flags/usage.cc
@@ -14,18 +14,25 @@
// limitations under the License.
#include "absl/flags/usage.h"
+#include <stdlib.h>
+
#include <string>
+#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/internal/usage.h"
+#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
ABSL_CONST_INIT absl::Mutex usage_message_guard(absl::kConstInit);
ABSL_CONST_INIT std::string* program_usage_message
- GUARDED_BY(usage_message_guard) = nullptr;
+ ABSL_GUARDED_BY(usage_message_guard) = nullptr;
} // namespace
} // namespace flags_internal
@@ -54,5 +61,5 @@ absl::string_view ProgramUsageMessage() {
: "Warning: SetProgramUsageMessage() never called";
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/usage.h b/absl/flags/usage.h
index c232a7d0..ad12ab7a 100644
--- a/absl/flags/usage.h
+++ b/absl/flags/usage.h
@@ -16,13 +16,14 @@
#ifndef ABSL_FLAGS_USAGE_H_
#define ABSL_FLAGS_USAGE_H_
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
// --------------------------------------------------------------------
// Usage reporting interfaces
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Sets the "usage" message to be used by help reporting routines.
// For example:
@@ -36,7 +37,7 @@ void SetProgramUsageMessage(absl::string_view new_usage_message);
// Returns the usage message set by SetProgramUsageMessage().
absl::string_view ProgramUsageMessage();
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_FLAGS_USAGE_H_
diff --git a/absl/flags/usage_config.cc b/absl/flags/usage_config.cc
index a538acd5..2d837ec5 100644
--- a/absl/flags/usage_config.cc
+++ b/absl/flags/usage_config.cc
@@ -16,12 +16,16 @@
#include "absl/flags/usage_config.h"
#include <iostream>
-#include <memory>
+#include <string>
#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/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
-#include "absl/strings/str_cat.h"
+#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/synchronization/mutex.h"
@@ -34,7 +38,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalReportFatalUsageError(absl::string_view) {}
} // extern "C"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
namespace {
@@ -95,7 +99,7 @@ std::string NormalizeFilename(absl::string_view filename) {
ABSL_CONST_INIT absl::Mutex custom_usage_config_guard(absl::kConstInit);
ABSL_CONST_INIT FlagsUsageConfig* custom_usage_config
- GUARDED_BY(custom_usage_config_guard) = nullptr;
+ ABSL_GUARDED_BY(custom_usage_config_guard) = nullptr;
} // namespace
@@ -150,5 +154,5 @@ void SetFlagsUsageConfig(FlagsUsageConfig usage_config) {
flags_internal::custom_usage_config = new FlagsUsageConfig(usage_config);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/flags/usage_config.h b/absl/flags/usage_config.h
index c6d34e4a..0ed7e1b4 100644
--- a/absl/flags/usage_config.h
+++ b/absl/flags/usage_config.h
@@ -27,6 +27,7 @@
#include <functional>
#include <string>
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
// -----------------------------------------------------------------------------
@@ -54,7 +55,7 @@
// Shows help on modules whose name contains the specified substring
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace flags_internal {
using FlagKindFilter = std::function<bool (absl::string_view)>;
@@ -102,7 +103,7 @@ struct FlagsUsageConfig {
// normalize_filename("/my_company/some_long_path/src/project/file.cc")
// might produce
// "project/file.cc".
- std::function<std::string (absl::string_view)> normalize_filename;
+ std::function<std::string(absl::string_view)> normalize_filename;
};
// SetFlagsUsageConfig()
@@ -119,7 +120,7 @@ FlagsUsageConfig GetUsageConfig();
void ReportUsageError(absl::string_view msg, bool is_fatal);
} // namespace flags_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
extern "C" {
diff --git a/absl/flags/usage_config_test.cc b/absl/flags/usage_config_test.cc
index 3bde13af..70eca30b 100644
--- a/absl/flags/usage_config_test.cc
+++ b/absl/flags/usage_config_test.cc
@@ -15,10 +15,13 @@
#include "absl/flags/usage_config.h"
+#include <string>
+
#include "gtest/gtest.h"
#include "absl/flags/internal/path_util.h"
#include "absl/flags/internal/program_name.h"
#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
namespace {
diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel
new file mode 100644
index 00000000..432546ce
--- /dev/null
+++ b/absl/functional/BUILD.bazel
@@ -0,0 +1,93 @@
+#
+# 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.
+#
+
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
+load(
+ "//absl:copts/configure_copts.bzl",
+ "ABSL_DEFAULT_COPTS",
+ "ABSL_DEFAULT_LINKOPTS",
+ "ABSL_TEST_COPTS",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+cc_library(
+ name = "bind_front",
+ srcs = ["internal/front_binder.h"],
+ hdrs = ["bind_front.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:base_internal",
+ "//absl/container:compressed_tuple",
+ "//absl/meta:type_traits",
+ "//absl/utility",
+ ],
+)
+
+cc_test(
+ name = "bind_front_test",
+ srcs = ["bind_front_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":bind_front",
+ "//absl/memory",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "function_ref",
+ srcs = ["internal/function_ref.h"],
+ hdrs = ["function_ref.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:base_internal",
+ "//absl/meta:type_traits",
+ ],
+)
+
+cc_test(
+ name = "function_ref_test",
+ size = "small",
+ srcs = ["function_ref_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":function_ref",
+ "//absl/container:test_instance_tracker",
+ "//absl/memory",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "function_ref_benchmark",
+ srcs = [
+ "function_ref_benchmark.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ tags = ["benchmark"],
+ visibility = ["//visibility:private"],
+ deps = [
+ ":function_ref",
+ "//absl/base:core_headers",
+ "@com_github_google_benchmark//:benchmark_main",
+ ],
+)
diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt
new file mode 100644
index 00000000..cda914f2
--- /dev/null
+++ b/absl/functional/CMakeLists.txt
@@ -0,0 +1,72 @@
+#
+# 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.
+#
+
+absl_cc_library(
+ NAME
+ bind_front
+ SRCS
+ "internal/front_binder.h"
+ HDRS
+ "bind_front.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base_internal
+ absl::compressed_tuple
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ bind_front_test
+ SRCS
+ "bind_front_test.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::bind_front
+ absl::memory
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ function_ref
+ SRCS
+ "internal/function_ref.h"
+ HDRS
+ "function_ref.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::base_internal
+ absl::meta
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ function_ref_test
+ SRCS
+ "function_ref_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::function_ref
+ absl::memory
+ absl::test_instance_tracker
+ gmock_main
+)
diff --git a/absl/functional/bind_front.h b/absl/functional/bind_front.h
new file mode 100644
index 00000000..5b47970e
--- /dev/null
+++ b/absl/functional/bind_front.h
@@ -0,0 +1,184 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: bind_front.h
+// -----------------------------------------------------------------------------
+//
+// `absl::bind_front()` returns a functor by binding a number of arguments to
+// the front of a provided (usually more generic) functor. Unlike `std::bind`,
+// it does not require the use of argument placeholders. The simpler syntax of
+// `absl::bind_front()` allows you to avoid known misuses with `std::bind()`.
+//
+// `absl::bind_front()` is meant as a drop-in replacement for C++20's upcoming
+// `std::bind_front()`, which similarly resolves these issues with
+// `std::bind()`. Both `bind_front()` alternatives, unlike `std::bind()`, allow
+// partial function application. (See
+// https://en.wikipedia.org/wiki/Partial_application).
+
+#ifndef ABSL_FUNCTIONAL_BIND_FRONT_H_
+#define ABSL_FUNCTIONAL_BIND_FRONT_H_
+
+#include "absl/functional/internal/front_binder.h"
+#include "absl/utility/utility.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// bind_front()
+//
+// Binds the first N arguments of an invocable object and stores them by value.
+//
+// Like `std::bind()`, `absl::bind_front()` is implicitly convertible to
+// `std::function`. In particular, it may be used as a simpler replacement for
+// `std::bind()` in most cases, as it does not require placeholders to be
+// specified. More importantly, it provides more reliable correctness guarantees
+// than `std::bind()`; while `std::bind()` will silently ignore passing more
+// parameters than expected, for example, `absl::bind_front()` will report such
+// mis-uses as errors.
+//
+// absl::bind_front(a...) can be seen as storing the results of
+// std::make_tuple(a...).
+//
+// Example: Binding a free function.
+//
+// int Minus(int a, int b) { return a - b; }
+//
+// assert(absl::bind_front(Minus)(3, 2) == 3 - 2);
+// assert(absl::bind_front(Minus, 3)(2) == 3 - 2);
+// assert(absl::bind_front(Minus, 3, 2)() == 3 - 2);
+//
+// Example: Binding a member function.
+//
+// struct Math {
+// int Double(int a) const { return 2 * a; }
+// };
+//
+// Math math;
+//
+// assert(absl::bind_front(&Math::Double)(&math, 3) == 2 * 3);
+// // Stores a pointer to math inside the functor.
+// assert(absl::bind_front(&Math::Double, &math)(3) == 2 * 3);
+// // Stores a copy of math inside the functor.
+// assert(absl::bind_front(&Math::Double, math)(3) == 2 * 3);
+// // Stores std::unique_ptr<Math> inside the functor.
+// assert(absl::bind_front(&Math::Double,
+// std::unique_ptr<Math>(new Math))(3) == 2 * 3);
+//
+// Example: Using `absl::bind_front()`, instead of `std::bind()`, with
+// `std::function`.
+//
+// class FileReader {
+// public:
+// void ReadFileAsync(const std::string& filename, std::string* content,
+// const std::function<void()>& done) {
+// // Calls Executor::Schedule(std::function<void()>).
+// Executor::DefaultExecutor()->Schedule(
+// absl::bind_front(&FileReader::BlockingRead, this,
+// filename, content, done));
+// }
+//
+// private:
+// void BlockingRead(const std::string& filename, std::string* content,
+// const std::function<void()>& done) {
+// CHECK_OK(file::GetContents(filename, content, {}));
+// done();
+// }
+// };
+//
+// `absl::bind_front()` stores bound arguments explicitly using the type passed
+// rather than implicitly based on the type accepted by its functor.
+//
+// Example: Binding arguments explicitly.
+//
+// void LogStringView(absl::string_view sv) {
+// LOG(INFO) << sv;
+// }
+//
+// Executor* e = Executor::DefaultExecutor();
+// std::string s = "hello";
+// absl::string_view sv = s;
+//
+// // absl::bind_front(LogStringView, arg) makes a copy of arg and stores it.
+// e->Schedule(absl::bind_front(LogStringView, sv)); // ERROR: dangling
+// // string_view.
+//
+// e->Schedule(absl::bind_front(LogStringView, s)); // OK: stores a copy of
+// // s.
+//
+// To store some of the arguments passed to `absl::bind_front()` by reference,
+// use std::ref()` and `std::cref()`.
+//
+// Example: Storing some of the bound arguments by reference.
+//
+// class Service {
+// public:
+// void Serve(const Request& req, std::function<void()>* done) {
+// // The request protocol buffer won't be deleted until done is called.
+// // It's safe to store a reference to it inside the functor.
+// Executor::DefaultExecutor()->Schedule(
+// absl::bind_front(&Service::BlockingServe, this, std::cref(req),
+// done));
+// }
+//
+// private:
+// void BlockingServe(const Request& req, std::function<void()>* done);
+// };
+//
+// Example: Storing bound arguments by reference.
+//
+// void Print(const std::string& a, const std::string& b) {
+// std::cerr << a << b;
+// }
+//
+// std::string hi = "Hello, ";
+// std::vector<std::string> names = {"Chuk", "Gek"};
+// // Doesn't copy hi.
+// for_each(names.begin(), names.end(),
+// absl::bind_front(Print, std::ref(hi)));
+//
+// // DO NOT DO THIS: the functor may outlive "hi", resulting in
+// // dangling references.
+// foo->DoInFuture(absl::bind_front(Print, std::ref(hi), "Guest")); // BAD!
+// auto f = absl::bind_front(Print, std::ref(hi), "Guest"); // BAD!
+//
+// Example: Storing reference-like types.
+//
+// void Print(absl::string_view a, const std::string& b) {
+// std::cerr << a << b;
+// }
+//
+// std::string hi = "Hello, ";
+// // Copies "hi".
+// absl::bind_front(Print, hi)("Chuk");
+//
+// // Compile error: std::reference_wrapper<const string> is not implicitly
+// // convertible to string_view.
+// // absl::bind_front(Print, std::cref(hi))("Chuk");
+//
+// // Doesn't copy "hi".
+// absl::bind_front(Print, absl::string_view(hi))("Chuk");
+//
+template <class F, class... BoundArgs>
+constexpr functional_internal::bind_front_t<F, BoundArgs...> bind_front(
+ F&& func, BoundArgs&&... args) {
+ return functional_internal::bind_front_t<F, BoundArgs...>(
+ absl::in_place, absl::forward<F>(func),
+ absl::forward<BoundArgs>(args)...);
+}
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_FUNCTIONAL_BIND_FRONT_H_
diff --git a/absl/functional/bind_front_test.cc b/absl/functional/bind_front_test.cc
new file mode 100644
index 00000000..4801a81c
--- /dev/null
+++ b/absl/functional/bind_front_test.cc
@@ -0,0 +1,231 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/functional/bind_front.h"
+
+#include <stddef.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
+
+namespace {
+
+char CharAt(const char* s, size_t index) { return s[index]; }
+
+TEST(BindTest, Basics) {
+ EXPECT_EQ('C', absl::bind_front(CharAt)("ABC", 2));
+ EXPECT_EQ('C', absl::bind_front(CharAt, "ABC")(2));
+ EXPECT_EQ('C', absl::bind_front(CharAt, "ABC", 2)());
+}
+
+TEST(BindTest, Lambda) {
+ auto lambda = [](int x, int y, int z) { return x + y + z; };
+ EXPECT_EQ(6, absl::bind_front(lambda)(1, 2, 3));
+ EXPECT_EQ(6, absl::bind_front(lambda, 1)(2, 3));
+ EXPECT_EQ(6, absl::bind_front(lambda, 1, 2)(3));
+ EXPECT_EQ(6, absl::bind_front(lambda, 1, 2, 3)());
+}
+
+struct Functor {
+ std::string operator()() & { return "&"; }
+ std::string operator()() const& { return "const&"; }
+ std::string operator()() && { return "&&"; }
+ std::string operator()() const&& { return "const&&"; }
+};
+
+TEST(BindTest, PerfectForwardingOfBoundArgs) {
+ auto f = absl::bind_front(Functor());
+ const auto& cf = f;
+ EXPECT_EQ("&", f());
+ EXPECT_EQ("const&", cf());
+ EXPECT_EQ("&&", std::move(f)());
+ EXPECT_EQ("const&&", std::move(cf)());
+}
+
+struct ArgDescribe {
+ std::string operator()(int&) const { return "&"; } // NOLINT
+ std::string operator()(const int&) const { return "const&"; } // NOLINT
+ std::string operator()(int&&) const { return "&&"; }
+ std::string operator()(const int&&) const { return "const&&"; }
+};
+
+TEST(BindTest, PerfectForwardingOfFreeArgs) {
+ ArgDescribe f;
+ int i;
+ EXPECT_EQ("&", absl::bind_front(f)(static_cast<int&>(i)));
+ EXPECT_EQ("const&", absl::bind_front(f)(static_cast<const int&>(i)));
+ EXPECT_EQ("&&", absl::bind_front(f)(static_cast<int&&>(i)));
+ EXPECT_EQ("const&&", absl::bind_front(f)(static_cast<const int&&>(i)));
+}
+
+struct NonCopyableFunctor {
+ NonCopyableFunctor() = default;
+ NonCopyableFunctor(const NonCopyableFunctor&) = delete;
+ NonCopyableFunctor& operator=(const NonCopyableFunctor&) = delete;
+ const NonCopyableFunctor* operator()() const { return this; }
+};
+
+TEST(BindTest, RefToFunctor) {
+ // It won't copy/move the functor and use the original object.
+ NonCopyableFunctor ncf;
+ auto bound_ncf = absl::bind_front(std::ref(ncf));
+ auto bound_ncf_copy = bound_ncf;
+ EXPECT_EQ(&ncf, bound_ncf_copy());
+}
+
+struct Struct {
+ std::string value;
+};
+
+TEST(BindTest, StoreByCopy) {
+ Struct s = {"hello"};
+ auto f = absl::bind_front(&Struct::value, s);
+ auto g = f;
+ EXPECT_EQ("hello", f());
+ EXPECT_EQ("hello", g());
+ EXPECT_NE(&s.value, &f());
+ EXPECT_NE(&s.value, &g());
+ EXPECT_NE(&g(), &f());
+}
+
+struct NonCopyable {
+ explicit NonCopyable(const std::string& s) : value(s) {}
+ NonCopyable(const NonCopyable&) = delete;
+ NonCopyable& operator=(const NonCopyable&) = delete;
+
+ std::string value;
+};
+
+const std::string& GetNonCopyableValue(const NonCopyable& n) { return n.value; }
+
+TEST(BindTest, StoreByRef) {
+ NonCopyable s("hello");
+ auto f = absl::bind_front(&GetNonCopyableValue, std::ref(s));
+ EXPECT_EQ("hello", f());
+ EXPECT_EQ(&s.value, &f());
+ auto g = std::move(f); // NOLINT
+ EXPECT_EQ("hello", g());
+ EXPECT_EQ(&s.value, &g());
+ s.value = "goodbye";
+ EXPECT_EQ("goodbye", g());
+}
+
+TEST(BindTest, StoreByCRef) {
+ NonCopyable s("hello");
+ auto f = absl::bind_front(&GetNonCopyableValue, std::cref(s));
+ EXPECT_EQ("hello", f());
+ EXPECT_EQ(&s.value, &f());
+ auto g = std::move(f); // NOLINT
+ EXPECT_EQ("hello", g());
+ EXPECT_EQ(&s.value, &g());
+ s.value = "goodbye";
+ EXPECT_EQ("goodbye", g());
+}
+
+const std::string& GetNonCopyableValueByWrapper(
+ std::reference_wrapper<NonCopyable> n) {
+ return n.get().value;
+}
+
+TEST(BindTest, StoreByRefInvokeByWrapper) {
+ NonCopyable s("hello");
+ auto f = absl::bind_front(GetNonCopyableValueByWrapper, std::ref(s));
+ EXPECT_EQ("hello", f());
+ EXPECT_EQ(&s.value, &f());
+ auto g = std::move(f);
+ EXPECT_EQ("hello", g());
+ EXPECT_EQ(&s.value, &g());
+ s.value = "goodbye";
+ EXPECT_EQ("goodbye", g());
+}
+
+TEST(BindTest, StoreByPointer) {
+ NonCopyable s("hello");
+ auto f = absl::bind_front(&NonCopyable::value, &s);
+ EXPECT_EQ("hello", f());
+ EXPECT_EQ(&s.value, &f());
+ auto g = std::move(f);
+ EXPECT_EQ("hello", g());
+ EXPECT_EQ(&s.value, &g());
+}
+
+int Sink(std::unique_ptr<int> p) {
+ return *p;
+}
+
+std::unique_ptr<int> Factory(int n) { return absl::make_unique<int>(n); }
+
+TEST(BindTest, NonCopyableArg) {
+ EXPECT_EQ(42, absl::bind_front(Sink)(absl::make_unique<int>(42)));
+ EXPECT_EQ(42, absl::bind_front(Sink, absl::make_unique<int>(42))());
+}
+
+TEST(BindTest, NonCopyableResult) {
+ EXPECT_THAT(absl::bind_front(Factory)(42), ::testing::Pointee(42));
+ EXPECT_THAT(absl::bind_front(Factory, 42)(), ::testing::Pointee(42));
+}
+
+// is_copy_constructible<FalseCopyable<unique_ptr<T>> is true but an attempt to
+// instantiate the copy constructor leads to a compile error. This is similar
+// to how standard containers behave.
+template <class T>
+struct FalseCopyable {
+ FalseCopyable() {}
+ FalseCopyable(const FalseCopyable& other) : m(other.m) {}
+ FalseCopyable(FalseCopyable&& other) : m(std::move(other.m)) {}
+ T m;
+};
+
+int GetMember(FalseCopyable<std::unique_ptr<int>> x) { return *x.m; }
+
+TEST(BindTest, WrappedMoveOnly) {
+ FalseCopyable<std::unique_ptr<int>> x;
+ x.m = absl::make_unique<int>(42);
+ auto f = absl::bind_front(&GetMember, std::move(x));
+ EXPECT_EQ(42, std::move(f)());
+}
+
+int Plus(int a, int b) { return a + b; }
+
+TEST(BindTest, ConstExpr) {
+ constexpr auto f = absl::bind_front(CharAt);
+ EXPECT_EQ(f("ABC", 1), 'B');
+ static constexpr int five = 5;
+ constexpr auto plus5 = absl::bind_front(Plus, five);
+ EXPECT_EQ(plus5(1), 6);
+
+ // There seems to be a bug in MSVC dealing constexpr construction of
+ // char[]. Notice 'plus5' above; 'int' works just fine.
+#if !(defined(_MSC_VER) && _MSC_VER < 1910)
+ static constexpr char data[] = "DEF";
+ constexpr auto g = absl::bind_front(CharAt, data);
+ EXPECT_EQ(g(1), 'E');
+#endif
+}
+
+struct ManglingCall {
+ int operator()(int, double, std::string) const { return 0; }
+};
+
+TEST(BindTest, Mangling) {
+ // We just want to generate a particular instantiation to see its mangling.
+ absl::bind_front(ManglingCall{}, 1, 3.3)("A");
+}
+
+} // namespace
diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h
new file mode 100644
index 00000000..370acc55
--- /dev/null
+++ b/absl/functional/function_ref.h
@@ -0,0 +1,139 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: function_ref.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines the `absl::FunctionRef` type for holding a
+// non-owning reference to an object of any invocable type. This function
+// reference is typically most useful as a type-erased argument type for
+// accepting function types that neither take ownership nor copy the type; using
+// the reference type in this case avoids a copy and an allocation. Best
+// practices of other non-owning reference-like objects (such as
+// `absl::string_view`) apply here.
+//
+// An `absl::FunctionRef` is similar in usage to a `std::function` but has the
+// following differences:
+//
+// * It doesn't own the underlying object.
+// * It doesn't have a null or empty state.
+// * It never performs deep copies or allocations.
+// * It's much faster and cheaper to construct.
+// * It's trivially copyable and destructable.
+//
+// Generally, `absl::FunctionRef` should not be used as a return value, data
+// member, or to initialize a `std::function`. Such usages will often lead to
+// problematic lifetime issues. Once you convert something to an
+// `absl::FunctionRef` you cannot make a deep copy later.
+//
+// This class is suitable for use wherever a "const std::function<>&"
+// would be used without making a copy. ForEach functions and other versions of
+// the visitor pattern are a good example of when this class should be used.
+//
+// This class is trivial to copy and should be passed by value.
+#ifndef ABSL_FUNCTIONAL_FUNCTION_REF_H_
+#define ABSL_FUNCTIONAL_FUNCTION_REF_H_
+
+#include <cassert>
+#include <functional>
+#include <type_traits>
+
+#include "absl/functional/internal/function_ref.h"
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// FunctionRef
+//
+// Dummy class declaration to allow the partial specialization based on function
+// types below.
+template <typename T>
+class FunctionRef;
+
+// FunctionRef
+//
+// An `absl::FunctionRef` is a lightweight wrapper to any invokable object with
+// a compatible signature. Generally, an `absl::FunctionRef` should only be used
+// as an argument type and should be preferred as an argument over a const
+// reference to a `std::function`.
+//
+// Example:
+//
+// // The following function takes a function callback by const reference
+// bool Visitor(const std::function<void(my_proto&,
+// absl::string_view)>& callback);
+//
+// // Assuming that the function is not stored or otherwise copied, it can be
+// // replaced by an `absl::FunctionRef`:
+// bool Visitor(absl::FunctionRef<void(my_proto&, absl::string_view)>
+// callback);
+//
+// Note: the assignment operator within an `absl::FunctionRef` is intentionally
+// deleted to prevent misuse; because the `absl::FunctionRef` does not own the
+// underlying type, assignment likely indicates misuse.
+template <typename R, typename... Args>
+class FunctionRef<R(Args...)> {
+ private:
+ // Used to disable constructors for objects that are not compatible with the
+ // signature of this FunctionRef.
+ template <typename F,
+ typename FR = absl::base_internal::InvokeT<F, Args&&...>>
+ using EnableIfCompatible =
+ typename std::enable_if<std::is_void<R>::value ||
+ std::is_convertible<FR, R>::value>::type;
+
+ public:
+ // Constructs a FunctionRef from any invokable type.
+ template <typename F, typename = EnableIfCompatible<const F&>>
+ FunctionRef(const F& f) // NOLINT(runtime/explicit)
+ : invoker_(&absl::functional_internal::InvokeObject<F, R, Args...>) {
+ absl::functional_internal::AssertNonNull(f);
+ ptr_.obj = &f;
+ }
+
+ // Overload for function pointers. This eliminates a level of indirection that
+ // would happen if the above overload was used (it lets us store the pointer
+ // instead of a pointer to a pointer).
+ //
+ // This overload is also used for references to functions, since references to
+ // functions can decay to function pointers implicitly.
+ template <
+ typename F, typename = EnableIfCompatible<F*>,
+ absl::functional_internal::EnableIf<absl::is_function<F>::value> = 0>
+ FunctionRef(F* f) // NOLINT(runtime/explicit)
+ : invoker_(&absl::functional_internal::InvokeFunction<F*, R, Args...>) {
+ assert(f != nullptr);
+ ptr_.fun = reinterpret_cast<decltype(ptr_.fun)>(f);
+ }
+
+ // To help prevent subtle lifetime bugs, FunctionRef is not assignable.
+ // Typically, it should only be used as an argument type.
+ FunctionRef& operator=(const FunctionRef& rhs) = delete;
+
+ // Call the underlying object.
+ R operator()(Args... args) const {
+ return invoker_(ptr_, std::forward<Args>(args)...);
+ }
+
+ private:
+ absl::functional_internal::VoidPtr ptr_;
+ absl::functional_internal::Invoker<R, Args...> invoker_;
+};
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_FUNCTIONAL_FUNCTION_REF_H_
diff --git a/absl/functional/function_ref_benchmark.cc b/absl/functional/function_ref_benchmark.cc
new file mode 100644
index 00000000..045305bf
--- /dev/null
+++ b/absl/functional/function_ref_benchmark.cc
@@ -0,0 +1,142 @@
+// 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/functional/function_ref.h"
+
+#include <memory>
+
+#include "benchmark/benchmark.h"
+#include "absl/base/attributes.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace {
+
+int dummy = 0;
+
+void FreeFunction() { benchmark::DoNotOptimize(dummy); }
+
+struct TrivialFunctor {
+ void operator()() const { benchmark::DoNotOptimize(dummy); }
+};
+
+struct LargeFunctor {
+ void operator()() const { benchmark::DoNotOptimize(this); }
+ std::string a, b, c;
+};
+
+template <typename Function, typename... Args>
+void ABSL_ATTRIBUTE_NOINLINE CallFunction(Function f, Args&&... args) {
+ f(std::forward<Args>(args)...);
+}
+
+template <typename Function, typename Callable, typename... Args>
+void ConstructAndCallFunctionBenchmark(benchmark::State& state,
+ const Callable& c, Args&&... args) {
+ for (auto _ : state) {
+ CallFunction<Function>(c, std::forward<Args>(args)...);
+ }
+}
+
+void BM_TrivialStdFunction(benchmark::State& state) {
+ ConstructAndCallFunctionBenchmark<std::function<void()>>(state,
+ TrivialFunctor{});
+}
+BENCHMARK(BM_TrivialStdFunction);
+
+void BM_TrivialFunctionRef(benchmark::State& state) {
+ ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state,
+ TrivialFunctor{});
+}
+BENCHMARK(BM_TrivialFunctionRef);
+
+void BM_LargeStdFunction(benchmark::State& state) {
+ ConstructAndCallFunctionBenchmark<std::function<void()>>(state,
+ LargeFunctor{});
+}
+BENCHMARK(BM_LargeStdFunction);
+
+void BM_LargeFunctionRef(benchmark::State& state) {
+ ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, LargeFunctor{});
+}
+BENCHMARK(BM_LargeFunctionRef);
+
+void BM_FunPtrStdFunction(benchmark::State& state) {
+ ConstructAndCallFunctionBenchmark<std::function<void()>>(state, FreeFunction);
+}
+BENCHMARK(BM_FunPtrStdFunction);
+
+void BM_FunPtrFunctionRef(benchmark::State& state) {
+ ConstructAndCallFunctionBenchmark<FunctionRef<void()>>(state, FreeFunction);
+}
+BENCHMARK(BM_FunPtrFunctionRef);
+
+// Doesn't include construction or copy overhead in the loop.
+template <typename Function, typename Callable, typename... Args>
+void CallFunctionBenchmark(benchmark::State& state, const Callable& c,
+ Args... args) {
+ Function f = c;
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(&f);
+ f(args...);
+ }
+}
+
+struct FunctorWithTrivialArgs {
+ void operator()(int a, int b, int c) const {
+ benchmark::DoNotOptimize(a);
+ benchmark::DoNotOptimize(b);
+ benchmark::DoNotOptimize(c);
+ }
+};
+
+void BM_TrivialArgsStdFunction(benchmark::State& state) {
+ CallFunctionBenchmark<std::function<void(int, int, int)>>(
+ state, FunctorWithTrivialArgs{}, 1, 2, 3);
+}
+BENCHMARK(BM_TrivialArgsStdFunction);
+
+void BM_TrivialArgsFunctionRef(benchmark::State& state) {
+ CallFunctionBenchmark<FunctionRef<void(int, int, int)>>(
+ state, FunctorWithTrivialArgs{}, 1, 2, 3);
+}
+BENCHMARK(BM_TrivialArgsFunctionRef);
+
+struct FunctorWithNonTrivialArgs {
+ void operator()(std::string a, std::string b, std::string c) const {
+ benchmark::DoNotOptimize(&a);
+ benchmark::DoNotOptimize(&b);
+ benchmark::DoNotOptimize(&c);
+ }
+};
+
+void BM_NonTrivialArgsStdFunction(benchmark::State& state) {
+ std::string a, b, c;
+ CallFunctionBenchmark<
+ std::function<void(std::string, std::string, std::string)>>(
+ state, FunctorWithNonTrivialArgs{}, a, b, c);
+}
+BENCHMARK(BM_NonTrivialArgsStdFunction);
+
+void BM_NonTrivialArgsFunctionRef(benchmark::State& state) {
+ std::string a, b, c;
+ CallFunctionBenchmark<
+ FunctionRef<void(std::string, std::string, std::string)>>(
+ state, FunctorWithNonTrivialArgs{}, a, b, c);
+}
+BENCHMARK(BM_NonTrivialArgsFunctionRef);
+
+} // namespace
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/functional/function_ref_test.cc b/absl/functional/function_ref_test.cc
new file mode 100644
index 00000000..3aa59745
--- /dev/null
+++ b/absl/functional/function_ref_test.cc
@@ -0,0 +1,257 @@
+// 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/functional/function_ref.h"
+
+#include <memory>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/container/internal/test_instance_tracker.h"
+#include "absl/memory/memory.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace {
+
+void RunFun(FunctionRef<void()> f) { f(); }
+
+TEST(FunctionRefTest, Lambda) {
+ bool ran = false;
+ RunFun([&] { ran = true; });
+ EXPECT_TRUE(ran);
+}
+
+int Function() { return 1337; }
+
+TEST(FunctionRefTest, Function1) {
+ FunctionRef<int()> ref(&Function);
+ EXPECT_EQ(1337, ref());
+}
+
+TEST(FunctionRefTest, Function2) {
+ FunctionRef<int()> ref(Function);
+ EXPECT_EQ(1337, ref());
+}
+
+int NoExceptFunction() noexcept { return 1337; }
+
+// TODO(jdennett): Add a test for noexcept member functions.
+TEST(FunctionRefTest, NoExceptFunction) {
+ FunctionRef<int()> ref(NoExceptFunction);
+ EXPECT_EQ(1337, ref());
+}
+
+TEST(FunctionRefTest, ForwardsArgs) {
+ auto l = [](std::unique_ptr<int> i) { return *i; };
+ FunctionRef<int(std::unique_ptr<int>)> ref(l);
+ EXPECT_EQ(42, ref(absl::make_unique<int>(42)));
+}
+
+TEST(FunctionRef, ReturnMoveOnly) {
+ auto l = [] { return absl::make_unique<int>(29); };
+ FunctionRef<std::unique_ptr<int>()> ref(l);
+ EXPECT_EQ(29, *ref());
+}
+
+TEST(FunctionRef, ManyArgs) {
+ auto l = [](int a, int b, int c) { return a + b + c; };
+ FunctionRef<int(int, int, int)> ref(l);
+ EXPECT_EQ(6, ref(1, 2, 3));
+}
+
+TEST(FunctionRef, VoidResultFromNonVoidFunctor) {
+ bool ran = false;
+ auto l = [&]() -> int {
+ ran = true;
+ return 2;
+ };
+ FunctionRef<void()> ref(l);
+ ref();
+ EXPECT_TRUE(ran);
+}
+
+TEST(FunctionRef, CastFromDerived) {
+ struct Base {};
+ struct Derived : public Base {};
+
+ Derived d;
+ auto l1 = [&](Base* b) { EXPECT_EQ(&d, b); };
+ FunctionRef<void(Derived*)> ref1(l1);
+ ref1(&d);
+
+ auto l2 = [&]() -> Derived* { return &d; };
+ FunctionRef<Base*()> ref2(l2);
+ EXPECT_EQ(&d, ref2());
+}
+
+TEST(FunctionRef, VoidResultFromNonVoidFuncton) {
+ FunctionRef<void()> ref(Function);
+ ref();
+}
+
+TEST(FunctionRef, MemberPtr) {
+ struct S {
+ int i;
+ };
+
+ S s{1100111};
+ auto mem_ptr = &S::i;
+ FunctionRef<int(const S& s)> ref(mem_ptr);
+ EXPECT_EQ(1100111, ref(s));
+}
+
+TEST(FunctionRef, MemberFun) {
+ struct S {
+ int i;
+ int get_i() const { return i; }
+ };
+
+ S s{22};
+ auto mem_fun_ptr = &S::get_i;
+ FunctionRef<int(const S& s)> ref(mem_fun_ptr);
+ EXPECT_EQ(22, ref(s));
+}
+
+TEST(FunctionRef, MemberFunRefqualified) {
+ struct S {
+ int i;
+ int get_i() && { return i; }
+ };
+ auto mem_fun_ptr = &S::get_i;
+ S s{22};
+ FunctionRef<int(S && s)> ref(mem_fun_ptr);
+ EXPECT_EQ(22, ref(std::move(s)));
+}
+
+#if !defined(_WIN32) && defined(GTEST_HAS_DEATH_TEST)
+
+TEST(FunctionRef, MemberFunRefqualifiedNull) {
+ struct S {
+ int i;
+ int get_i() && { return i; }
+ };
+ auto mem_fun_ptr = &S::get_i;
+ mem_fun_ptr = nullptr;
+ EXPECT_DEBUG_DEATH({ FunctionRef<int(S && s)> ref(mem_fun_ptr); }, "");
+}
+
+TEST(FunctionRef, NullMemberPtrAssertFails) {
+ struct S {
+ int i;
+ };
+ using MemberPtr = int S::*;
+ MemberPtr mem_ptr = nullptr;
+ EXPECT_DEBUG_DEATH({ FunctionRef<int(const S& s)> ref(mem_ptr); }, "");
+}
+
+#endif // GTEST_HAS_DEATH_TEST
+
+TEST(FunctionRef, CopiesAndMovesPerPassByValue) {
+ absl::test_internal::InstanceTracker tracker;
+ absl::test_internal::CopyableMovableInstance instance(0);
+ auto l = [](absl::test_internal::CopyableMovableInstance) {};
+ FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l);
+ ref(instance);
+ EXPECT_EQ(tracker.copies(), 1);
+ EXPECT_EQ(tracker.moves(), 1);
+}
+
+TEST(FunctionRef, CopiesAndMovesPerPassByRef) {
+ absl::test_internal::InstanceTracker tracker;
+ absl::test_internal::CopyableMovableInstance instance(0);
+ auto l = [](const absl::test_internal::CopyableMovableInstance&) {};
+ FunctionRef<void(const absl::test_internal::CopyableMovableInstance&)> ref(l);
+ ref(instance);
+ EXPECT_EQ(tracker.copies(), 0);
+ EXPECT_EQ(tracker.moves(), 0);
+}
+
+TEST(FunctionRef, CopiesAndMovesPerPassByValueCallByMove) {
+ absl::test_internal::InstanceTracker tracker;
+ absl::test_internal::CopyableMovableInstance instance(0);
+ auto l = [](absl::test_internal::CopyableMovableInstance) {};
+ FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l);
+ ref(std::move(instance));
+ EXPECT_EQ(tracker.copies(), 0);
+ EXPECT_EQ(tracker.moves(), 2);
+}
+
+TEST(FunctionRef, CopiesAndMovesPerPassByValueToRef) {
+ absl::test_internal::InstanceTracker tracker;
+ absl::test_internal::CopyableMovableInstance instance(0);
+ auto l = [](const absl::test_internal::CopyableMovableInstance&) {};
+ FunctionRef<void(absl::test_internal::CopyableMovableInstance)> ref(l);
+ ref(std::move(instance));
+ EXPECT_EQ(tracker.copies(), 0);
+ EXPECT_EQ(tracker.moves(), 1);
+}
+
+TEST(FunctionRef, PassByValueTypes) {
+ using absl::functional_internal::Invoker;
+ using absl::functional_internal::VoidPtr;
+ using absl::test_internal::CopyableMovableInstance;
+ struct Trivial {
+ void* p[2];
+ };
+ struct LargeTrivial {
+ void* p[3];
+ };
+
+ static_assert(std::is_same<Invoker<void, int>, void (*)(VoidPtr, int)>::value,
+ "Scalar types should be passed by value");
+ static_assert(
+ std::is_same<Invoker<void, Trivial>, void (*)(VoidPtr, Trivial)>::value,
+ "Small trivial types should be passed by value");
+ static_assert(std::is_same<Invoker<void, LargeTrivial>,
+ void (*)(VoidPtr, LargeTrivial &&)>::value,
+ "Large trivial types should be passed by rvalue reference");
+ static_assert(
+ std::is_same<Invoker<void, CopyableMovableInstance>,
+ void (*)(VoidPtr, CopyableMovableInstance &&)>::value,
+ "Types with copy/move ctor should be passed by rvalue reference");
+
+ // References are passed as references.
+ static_assert(
+ std::is_same<Invoker<void, int&>, void (*)(VoidPtr, int&)>::value,
+ "Reference types should be preserved");
+ static_assert(
+ std::is_same<Invoker<void, CopyableMovableInstance&>,
+ void (*)(VoidPtr, CopyableMovableInstance&)>::value,
+ "Reference types should be preserved");
+ static_assert(
+ std::is_same<Invoker<void, CopyableMovableInstance&&>,
+ void (*)(VoidPtr, CopyableMovableInstance &&)>::value,
+ "Reference types should be preserved");
+
+ // Make sure the address of an object received by reference is the same as the
+ // addess of the object passed by the caller.
+ {
+ LargeTrivial obj;
+ auto test = [&obj](LargeTrivial& input) { ASSERT_EQ(&input, &obj); };
+ absl::FunctionRef<void(LargeTrivial&)> ref(test);
+ ref(obj);
+ }
+
+ {
+ Trivial obj;
+ auto test = [&obj](Trivial& input) { ASSERT_EQ(&input, &obj); };
+ absl::FunctionRef<void(Trivial&)> ref(test);
+ ref(obj);
+ }
+}
+
+} // namespace
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/functional/internal/front_binder.h b/absl/functional/internal/front_binder.h
new file mode 100644
index 00000000..a4d95da4
--- /dev/null
+++ b/absl/functional/internal/front_binder.h
@@ -0,0 +1,95 @@
+// 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.
+
+// Implementation details for `absl::bind_front()`.
+
+#ifndef ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_
+#define ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_
+
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/internal/invoke.h"
+#include "absl/container/internal/compressed_tuple.h"
+#include "absl/meta/type_traits.h"
+#include "absl/utility/utility.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace functional_internal {
+
+// Invoke the method, expanding the tuple of bound arguments.
+template <class R, class Tuple, size_t... Idx, class... Args>
+R Apply(Tuple&& bound, absl::index_sequence<Idx...>, Args&&... free) {
+ return base_internal::Invoke(
+ absl::forward<Tuple>(bound).template get<Idx>()...,
+ absl::forward<Args>(free)...);
+}
+
+template <class F, class... BoundArgs>
+class FrontBinder {
+ using BoundArgsT = absl::container_internal::CompressedTuple<F, BoundArgs...>;
+ using Idx = absl::make_index_sequence<sizeof...(BoundArgs) + 1>;
+
+ BoundArgsT bound_args_;
+
+ public:
+ template <class... Ts>
+ constexpr explicit FrontBinder(absl::in_place_t, Ts&&... ts)
+ : bound_args_(absl::forward<Ts>(ts)...) {}
+
+ template <class... FreeArgs,
+ class R = base_internal::InvokeT<F&, BoundArgs&..., FreeArgs&&...>>
+ R operator()(FreeArgs&&... free_args) & {
+ return functional_internal::Apply<R>(bound_args_, Idx(),
+ absl::forward<FreeArgs>(free_args)...);
+ }
+
+ template <class... FreeArgs,
+ class R = base_internal::InvokeT<const F&, const BoundArgs&...,
+ FreeArgs&&...>>
+ R operator()(FreeArgs&&... free_args) const& {
+ return functional_internal::Apply<R>(bound_args_, Idx(),
+ absl::forward<FreeArgs>(free_args)...);
+ }
+
+ template <class... FreeArgs, class R = base_internal::InvokeT<
+ F&&, BoundArgs&&..., FreeArgs&&...>>
+ R operator()(FreeArgs&&... free_args) && {
+ // This overload is called when *this is an rvalue. If some of the bound
+ // arguments are stored by value or rvalue reference, we move them.
+ return functional_internal::Apply<R>(absl::move(bound_args_), Idx(),
+ absl::forward<FreeArgs>(free_args)...);
+ }
+
+ template <class... FreeArgs,
+ class R = base_internal::InvokeT<const F&&, const BoundArgs&&...,
+ FreeArgs&&...>>
+ R operator()(FreeArgs&&... free_args) const&& {
+ // This overload is called when *this is an rvalue. If some of the bound
+ // arguments are stored by value or rvalue reference, we move them.
+ return functional_internal::Apply<R>(absl::move(bound_args_), Idx(),
+ absl::forward<FreeArgs>(free_args)...);
+ }
+};
+
+template <class F, class... BoundArgs>
+using bind_front_t = FrontBinder<decay_t<F>, absl::decay_t<BoundArgs>...>;
+
+} // namespace functional_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_FUNCTIONAL_INTERNAL_FRONT_BINDER_H_
diff --git a/absl/functional/internal/function_ref.h b/absl/functional/internal/function_ref.h
new file mode 100644
index 00000000..d1575054
--- /dev/null
+++ b/absl/functional/internal/function_ref.h
@@ -0,0 +1,106 @@
+// 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_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_
+#define ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_
+
+#include <cassert>
+#include <functional>
+#include <type_traits>
+
+#include "absl/base/internal/invoke.h"
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace functional_internal {
+
+// Like a void* that can handle function pointers as well. The standard does not
+// allow function pointers to round-trip through void*, but void(*)() is fine.
+//
+// Note: It's important that this class remains trivial and is the same size as
+// a pointer, since this allows the compiler to perform tail-call optimizations
+// when the underlying function is a callable object with a matching signature.
+union VoidPtr {
+ const void* obj;
+ void (*fun)();
+};
+
+// Chooses the best type for passing T as an argument.
+// Attempt to be close to SystemV AMD64 ABI. Objects with trivial copy ctor are
+// passed by value.
+template <typename T>
+constexpr bool PassByValue() {
+ return !std::is_lvalue_reference<T>::value &&
+ absl::is_trivially_copy_constructible<T>::value &&
+ absl::is_trivially_copy_assignable<
+ typename std::remove_cv<T>::type>::value &&
+ std::is_trivially_destructible<T>::value &&
+ sizeof(T) <= 2 * sizeof(void*);
+}
+
+template <typename T>
+struct ForwardT : std::conditional<PassByValue<T>(), T, T&&> {};
+
+// An Invoker takes a pointer to the type-erased invokable object, followed by
+// the arguments that the invokable object expects.
+//
+// Note: The order of arguments here is an optimization, since member functions
+// have an implicit "this" pointer as their first argument, putting VoidPtr
+// first allows the compiler to perform tail-call optimization in many cases.
+template <typename R, typename... Args>
+using Invoker = R (*)(VoidPtr, typename ForwardT<Args>::type...);
+
+//
+// InvokeObject and InvokeFunction provide static "Invoke" functions that can be
+// used as Invokers for objects or functions respectively.
+//
+// static_cast<R> handles the case the return type is void.
+template <typename Obj, typename R, typename... Args>
+R InvokeObject(VoidPtr ptr, typename ForwardT<Args>::type... args) {
+ auto o = static_cast<const Obj*>(ptr.obj);
+ return static_cast<R>(
+ absl::base_internal::Invoke(*o, std::forward<Args>(args)...));
+}
+
+template <typename Fun, typename R, typename... Args>
+R InvokeFunction(VoidPtr ptr, typename ForwardT<Args>::type... args) {
+ auto f = reinterpret_cast<Fun>(ptr.fun);
+ return static_cast<R>(
+ absl::base_internal::Invoke(f, std::forward<Args>(args)...));
+}
+
+template <typename Sig>
+void AssertNonNull(const std::function<Sig>& f) {
+ assert(f != nullptr);
+ (void)f;
+}
+
+template <typename F>
+void AssertNonNull(const F&) {}
+
+template <typename F, typename C>
+void AssertNonNull(F C::*f) {
+ assert(f != nullptr);
+ (void)f;
+}
+
+template <bool C>
+using EnableIf = typename ::std::enable_if<C, int>::type;
+
+} // namespace functional_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_
diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel
index 8c2daf70..ffe8c294 100644
--- a/absl/hash/BUILD.bazel
+++ b/absl/hash/BUILD.bazel
@@ -1,5 +1,5 @@
#
-# Copyright 2018 The Abseil Authors.
+# 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.
@@ -14,6 +14,7 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@@ -70,9 +71,9 @@ cc_test(
deps = [
":hash",
":hash_testing",
+ ":spy_hash_state",
"//absl/base:core_headers",
"//absl/container:flat_hash_set",
- "//absl/hash:spy_hash_state",
"//absl/meta:type_traits",
"//absl/numeric:int128",
"@com_google_googletest//:gtest_main",
diff --git a/absl/hash/hash.h b/absl/hash/hash.h
index d36f0960..23a65ea8 100644
--- a/absl/hash/hash.h
+++ b/absl/hash/hash.h
@@ -73,7 +73,7 @@
#include "absl/hash/internal/hash.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// `absl::Hash`
@@ -318,7 +318,7 @@ class HashState : public hash_internal::HashStateBase<HashState> {
void (*combine_contiguous_)(void*, const unsigned char*, size_t);
};
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HASH_HASH_H_
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc
index 2a4e2262..f02a537a 100644
--- a/absl/hash/hash_test.cc
+++ b/absl/hash/hash_test.cc
@@ -274,8 +274,8 @@ TEST(HashValueTest, Strings) {
const std::string small = "foo";
const std::string dup = "foofoo";
- const std::string large = "large";
- const std::string huge = std::string(5000, 'a');
+ const std::string large = std::string(2048, 'x'); // multiple of chunk size
+ const std::string huge = std::string(5000, 'a'); // not a multiple
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
std::string(), absl::string_view(),
@@ -300,6 +300,33 @@ TEST(HashValueTest, Strings) {
SpyHash(absl::string_view("ABC")));
}
+TEST(HashValueTest, WString) {
+ EXPECT_TRUE((is_hashable<std::wstring>::value));
+
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
+ std::wstring(), std::wstring(L"ABC"), std::wstring(L"ABC"),
+ std::wstring(L"Some other different string"),
+ std::wstring(L"Iñtërnâtiônàlizætiøn"))));
+}
+
+TEST(HashValueTest, U16String) {
+ EXPECT_TRUE((is_hashable<std::u16string>::value));
+
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
+ std::u16string(), std::u16string(u"ABC"), std::u16string(u"ABC"),
+ std::u16string(u"Some other different string"),
+ std::u16string(u"Iñtërnâtiônàlizætiøn"))));
+}
+
+TEST(HashValueTest, U32String) {
+ EXPECT_TRUE((is_hashable<std::u32string>::value));
+
+ EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
+ std::u32string(), std::u32string(U"ABC"), std::u32string(U"ABC"),
+ std::u32string(U"Some other different string"),
+ std::u32string(U"Iñtërnâtiônàlizætiøn"))));
+}
+
TEST(HashValueTest, StdArray) {
EXPECT_TRUE((is_hashable<std::array<int, 3>>::value));
@@ -378,6 +405,116 @@ struct Private {
}
};
+// Test helper for combine_piecewise_buffer. It holds a string_view to the
+// buffer-to-be-hashed. Its AbslHashValue specialization will split up its
+// contents at the character offsets requested.
+class PiecewiseHashTester {
+ public:
+ // Create a hash view of a buffer to be hashed contiguously.
+ explicit PiecewiseHashTester(absl::string_view buf)
+ : buf_(buf), piecewise_(false), split_locations_() {}
+
+ // Create a hash view of a buffer to be hashed piecewise, with breaks at the
+ // given locations.
+ PiecewiseHashTester(absl::string_view buf, std::set<size_t> split_locations)
+ : buf_(buf),
+ piecewise_(true),
+ split_locations_(std::move(split_locations)) {}
+
+ template <typename H>
+ friend H AbslHashValue(H h, const PiecewiseHashTester& p) {
+ if (!p.piecewise_) {
+ return H::combine_contiguous(std::move(h), p.buf_.data(), p.buf_.size());
+ }
+ absl::hash_internal::PiecewiseCombiner combiner;
+ if (p.split_locations_.empty()) {
+ h = combiner.add_buffer(std::move(h), p.buf_.data(), p.buf_.size());
+ return combiner.finalize(std::move(h));
+ }
+ size_t begin = 0;
+ for (size_t next : p.split_locations_) {
+ absl::string_view chunk = p.buf_.substr(begin, next - begin);
+ h = combiner.add_buffer(std::move(h), chunk.data(), chunk.size());
+ begin = next;
+ }
+ absl::string_view last_chunk = p.buf_.substr(begin);
+ if (!last_chunk.empty()) {
+ h = combiner.add_buffer(std::move(h), last_chunk.data(),
+ last_chunk.size());
+ }
+ return combiner.finalize(std::move(h));
+ }
+
+ private:
+ absl::string_view buf_;
+ bool piecewise_;
+ std::set<size_t> split_locations_;
+};
+
+// Dummy object that hashes as two distinct contiguous buffers, "foo" followed
+// by "bar"
+struct DummyFooBar {
+ template <typename H>
+ friend H AbslHashValue(H h, const DummyFooBar&) {
+ const char* foo = "foo";
+ const char* bar = "bar";
+ h = H::combine_contiguous(std::move(h), foo, 3);
+ h = H::combine_contiguous(std::move(h), bar, 3);
+ return h;
+ }
+};
+
+TEST(HashValueTest, CombinePiecewiseBuffer) {
+ absl::Hash<PiecewiseHashTester> hash;
+
+ // Check that hashing an empty buffer through the piecewise API works.
+ EXPECT_EQ(hash(PiecewiseHashTester("")), hash(PiecewiseHashTester("", {})));
+
+ // Similarly, small buffers should give consistent results
+ EXPECT_EQ(hash(PiecewiseHashTester("foobar")),
+ hash(PiecewiseHashTester("foobar", {})));
+ EXPECT_EQ(hash(PiecewiseHashTester("foobar")),
+ hash(PiecewiseHashTester("foobar", {3})));
+
+ // But hashing "foobar" in pieces gives a different answer than hashing "foo"
+ // contiguously, then "bar" contiguously.
+ EXPECT_NE(hash(PiecewiseHashTester("foobar", {3})),
+ absl::Hash<DummyFooBar>()(DummyFooBar{}));
+
+ // Test hashing a large buffer incrementally, broken up in several different
+ // ways. Arrange for breaks on and near the stride boundaries to look for
+ // off-by-one errors in the implementation.
+ //
+ // This test is run on a buffer that is a multiple of the stride size, and one
+ // that isn't.
+ for (size_t big_buffer_size : {1024 * 2 + 512, 1024 * 3}) {
+ SCOPED_TRACE(big_buffer_size);
+ std::string big_buffer;
+ for (int i = 0; i < big_buffer_size; ++i) {
+ // Arbitrary std::string
+ big_buffer.push_back(32 + (i * (i / 3)) % 64);
+ }
+ auto big_buffer_hash = hash(PiecewiseHashTester(big_buffer));
+
+ const int possible_breaks = 9;
+ size_t breaks[possible_breaks] = {1, 512, 1023, 1024, 1025,
+ 1536, 2047, 2048, 2049};
+ for (unsigned test_mask = 0; test_mask < (1u << possible_breaks);
+ ++test_mask) {
+ SCOPED_TRACE(test_mask);
+ std::set<size_t> break_locations;
+ for (int j = 0; j < possible_breaks; ++j) {
+ if (test_mask & (1u << j)) {
+ break_locations.insert(breaks[j]);
+ }
+ }
+ EXPECT_EQ(
+ hash(PiecewiseHashTester(big_buffer, std::move(break_locations))),
+ big_buffer_hash);
+ }
+ }
+}
+
TEST(HashValueTest, PrivateSanity) {
// Sanity check that Private is working as the tests below expect it to work.
EXPECT_TRUE(is_hashable<Private>::value);
@@ -458,7 +595,10 @@ TEST(IsHashableTest, PoisonHash) {
EXPECT_FALSE(absl::is_copy_assignable<absl::Hash<X>>::value);
EXPECT_FALSE(absl::is_move_assignable<absl::Hash<X>>::value);
EXPECT_FALSE(IsHashCallable<X>::value);
+#if !defined(__GNUC__) || __GNUC__ < 9
+ // This doesn't compile on GCC 9.
EXPECT_FALSE(IsAggregateInitializable<absl::Hash<X>>::value);
+#endif
}
#endif // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_
@@ -544,7 +684,7 @@ H AbslHashValue(H state, CustomHashType<Tags...> t) {
} // namespace
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace hash_internal {
template <InvokeTag... Tags>
struct is_uniquely_represented<
@@ -552,7 +692,7 @@ struct is_uniquely_represented<
typename EnableIfContained<InvokeTag::kUniquelyRepresented, Tags...>::type>
: std::true_type {};
} // namespace hash_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_
diff --git a/absl/hash/hash_testing.h b/absl/hash/hash_testing.h
index 6e39028f..1e1c5741 100644
--- a/absl/hash/hash_testing.h
+++ b/absl/hash/hash_testing.h
@@ -28,7 +28,7 @@
#include "absl/types/variant.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Run the absl::Hash algorithm over all the elements passed in and verify that
// their hash expansion is congruent with their `==` operator.
@@ -372,7 +372,7 @@ VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
equals);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HASH_HASH_TESTING_H_
diff --git a/absl/hash/internal/city.cc b/absl/hash/internal/city.cc
index c7ad1591..e122c184 100644
--- a/absl/hash/internal/city.cc
+++ b/absl/hash/internal/city.cc
@@ -30,7 +30,7 @@
#include "absl/base/optimization.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace hash_internal {
#ifdef ABSL_IS_BIG_ENDIAN
@@ -342,5 +342,5 @@ uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0,
}
} // namespace hash_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/hash/internal/city.h b/absl/hash/internal/city.h
index 7586ba08..161c7748 100644
--- a/absl/hash/internal/city.h
+++ b/absl/hash/internal/city.h
@@ -47,10 +47,13 @@
#include <stdint.h>
#include <stdlib.h> // for size_t.
+
#include <utility>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace hash_internal {
typedef std::pair<uint64_t, uint64_t> uint128;
@@ -87,7 +90,7 @@ inline uint64_t Hash128to64(const uint128 &x) {
}
} // namespace hash_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HASH_INTERNAL_CITY_H_
diff --git a/absl/hash/internal/city_test.cc b/absl/hash/internal/city_test.cc
index 1b9373c2..251d381d 100644
--- a/absl/hash/internal/city_test.cc
+++ b/absl/hash/internal/city_test.cc
@@ -20,7 +20,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace hash_internal {
static const uint64_t k0 = 0xc3a5c85c97cb3127ULL;
@@ -591,5 +591,5 @@ TEST(CityHashTest, Unchanging) {
}
} // namespace hash_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc
index 087b389b..b44ecb3a 100644
--- a/absl/hash/internal/hash.cc
+++ b/absl/hash/internal/hash.cc
@@ -15,11 +15,41 @@
#include "absl/hash/internal/hash.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace hash_internal {
+uint64_t CityHashState::CombineLargeContiguousImpl32(uint64_t state,
+ const unsigned char* first,
+ size_t len) {
+ while (len >= PiecewiseChunkSize()) {
+ state =
+ Mix(state, absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first),
+ PiecewiseChunkSize()));
+ len -= PiecewiseChunkSize();
+ first += PiecewiseChunkSize();
+ }
+ // Handle the remainder.
+ return CombineContiguousImpl(state, first, len,
+ std::integral_constant<int, 4>{});
+}
+
+uint64_t CityHashState::CombineLargeContiguousImpl64(uint64_t state,
+ const unsigned char* first,
+ size_t len) {
+ while (len >= PiecewiseChunkSize()) {
+ state =
+ Mix(state, absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first),
+ PiecewiseChunkSize()));
+ len -= PiecewiseChunkSize();
+ first += PiecewiseChunkSize();
+ }
+ // Handle the remainder.
+ return CombineContiguousImpl(state, first, len,
+ std::integral_constant<int, 8>{});
+}
+
ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed;
} // namespace hash_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h
index 81f1edf8..ae7a60cd 100644
--- a/absl/hash/internal/hash.h
+++ b/absl/hash/internal/hash.h
@@ -50,9 +50,15 @@
#include "absl/hash/internal/city.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace hash_internal {
+class PiecewiseCombiner;
+
+// Internal detail: Large buffers are hashed in smaller chunks. This function
+// returns the size of these chunks.
+constexpr size_t PiecewiseChunkSize() { return 1024; }
+
// HashStateBase
//
// A hash state object represents an intermediate state in the computation
@@ -69,7 +75,7 @@ namespace hash_internal {
//
// `static H combine_contiguous(H state, const unsigned char*, size_t)`.
//
-// `HashStateBase` will provide a complete implementations for a hash state
+// `HashStateBase` will provide a complete implementation for a hash state
// object in terms of this method.
//
// Example:
@@ -118,6 +124,9 @@ class HashStateBase {
// for-loop instead.
template <typename T>
static H combine_contiguous(H state, const T* data, size_t size);
+
+ private:
+ friend class PiecewiseCombiner;
};
// is_uniquely_represented
@@ -188,6 +197,61 @@ H hash_bytes(H hash_state, const T& value) {
return H::combine_contiguous(std::move(hash_state), start, sizeof(value));
}
+// PiecewiseCombiner
+//
+// PiecewiseCombiner is an internal-only helper class for hashing a piecewise
+// buffer of `char` or `unsigned char` as though it were contiguous. This class
+// provides two methods:
+//
+// H add_buffer(state, data, size)
+// H finalize(state)
+//
+// `add_buffer` can be called zero or more times, followed by a single call to
+// `finalize`. This will produce the same hash expansion as concatenating each
+// buffer piece into a single contiguous buffer, and passing this to
+// `H::combine_contiguous`.
+//
+// Example usage:
+// PiecewiseCombiner combiner;
+// for (const auto& piece : pieces) {
+// state = combiner.add_buffer(std::move(state), piece.data, piece.size);
+// }
+// return combiner.finalize(std::move(state));
+class PiecewiseCombiner {
+ public:
+ PiecewiseCombiner() : position_(0) {}
+ PiecewiseCombiner(const PiecewiseCombiner&) = delete;
+ PiecewiseCombiner& operator=(const PiecewiseCombiner&) = delete;
+
+ // PiecewiseCombiner::add_buffer()
+ //
+ // Appends the given range of bytes to the sequence to be hashed, which may
+ // modify the provided hash state.
+ template <typename H>
+ H add_buffer(H state, const unsigned char* data, size_t size);
+ template <typename H>
+ H add_buffer(H state, const char* data, size_t size) {
+ return add_buffer(std::move(state),
+ reinterpret_cast<const unsigned char*>(data), size);
+ }
+
+ // PiecewiseCombiner::finalize()
+ //
+ // Finishes combining the hash sequence, which may may modify the provided
+ // hash state.
+ //
+ // Once finalize() is called, add_buffer() may no longer be called. The
+ // resulting hash state will be the same as if the pieces passed to
+ // add_buffer() were concatenated into a single flat buffer, and then provided
+ // to H::combine_contiguous().
+ template <typename H>
+ H finalize(H state);
+
+ private:
+ unsigned char buf_[PiecewiseChunkSize()];
+ size_t position_;
+};
+
// -----------------------------------------------------------------------------
// AbslHashValue for Basic Types
// -----------------------------------------------------------------------------
@@ -364,6 +428,19 @@ H AbslHashValue(H hash_state, absl::string_view str) {
str.size());
}
+// Support std::wstring, std::u16string and std::u32string.
+template <typename Char, typename Alloc, typename H,
+ typename = absl::enable_if_t<std::is_same<Char, wchar_t>::value ||
+ std::is_same<Char, char16_t>::value ||
+ std::is_same<Char, char32_t>::value>>
+H AbslHashValue(
+ H hash_state,
+ const std::basic_string<Char, std::char_traits<Char>, Alloc>& str) {
+ return H::combine(
+ H::combine_contiguous(std::move(hash_state), str.data(), str.size()),
+ str.size());
+}
+
// -----------------------------------------------------------------------------
// AbslHashValue for Sequence Containers
// -----------------------------------------------------------------------------
@@ -631,7 +708,8 @@ struct is_hashable
: std::integral_constant<bool, HashSelect::template Apply<T>::value> {};
// CityHashState
-class CityHashState : public HashStateBase<CityHashState> {
+class ABSL_DLL CityHashState
+ : public HashStateBase<CityHashState> {
// absl::uint128 is not an alias or a thin wrapper around the intrinsic.
// We use the intrinsic when available to improve performance.
#ifdef ABSL_HAVE_INTRINSIC_INT128
@@ -710,6 +788,16 @@ class CityHashState : public HashStateBase<CityHashState> {
std::integral_constant<int, 8>
/* sizeof_size_t*/);
+ // Slow dispatch path for calls to CombineContiguousImpl with a size argument
+ // larger than PiecewiseChunkSize(). Has the same effect as calling
+ // CombineContiguousImpl() repeatedly with the chunk stride size.
+ static uint64_t CombineLargeContiguousImpl32(uint64_t state,
+ const unsigned char* first,
+ size_t len);
+ static uint64_t CombineLargeContiguousImpl64(uint64_t state,
+ const unsigned char* first,
+ size_t len);
+
// Reads 9 to 16 bytes from p.
// The first 8 bytes are in .first, the rest (zero padded) bytes are in
// .second.
@@ -777,6 +865,9 @@ inline uint64_t CityHashState::CombineContiguousImpl(
// multiplicative hash.
uint64_t v;
if (len > 8) {
+ if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) {
+ return CombineLargeContiguousImpl32(state, first, len);
+ }
v = absl::hash_internal::CityHash32(reinterpret_cast<const char*>(first), len);
} else if (len >= 4) {
v = Read4To8(first, len);
@@ -797,6 +888,9 @@ inline uint64_t CityHashState::CombineContiguousImpl(
// multiplicative hash.
uint64_t v;
if (len > 16) {
+ if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) {
+ return CombineLargeContiguousImpl64(state, first, len);
+ }
v = absl::hash_internal::CityHash64(reinterpret_cast<const char*>(first), len);
} else if (len > 8) {
auto p = Read9To16(first, len);
@@ -813,7 +907,6 @@ inline uint64_t CityHashState::CombineContiguousImpl(
return Mix(state, v);
}
-
struct AggregateBarrier {};
// HashImpl
@@ -850,8 +943,46 @@ template <typename T>
H HashStateBase<H>::combine_contiguous(H state, const T* data, size_t size) {
return hash_internal::hash_range_or_bytes(std::move(state), data, size);
}
+
+// HashStateBase::PiecewiseCombiner::add_buffer()
+template <typename H>
+H PiecewiseCombiner::add_buffer(H state, const unsigned char* data,
+ size_t size) {
+ if (position_ + size < PiecewiseChunkSize()) {
+ // This partial chunk does not fill our existing buffer
+ memcpy(buf_ + position_, data, size);
+ position_ += size;
+ return state;
+ }
+
+ // Complete the buffer and hash it
+ const size_t bytes_needed = PiecewiseChunkSize() - position_;
+ memcpy(buf_ + position_, data, bytes_needed);
+ state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize());
+ data += bytes_needed;
+ size -= bytes_needed;
+
+ // Hash whatever chunks we can without copying
+ while (size >= PiecewiseChunkSize()) {
+ state = H::combine_contiguous(std::move(state), data, PiecewiseChunkSize());
+ data += PiecewiseChunkSize();
+ size -= PiecewiseChunkSize();
+ }
+ // Fill the buffer with the remainder
+ memcpy(buf_, data, size);
+ position_ = size;
+ return state;
+}
+
+// HashStateBase::PiecewiseCombiner::finalize()
+template <typename H>
+H PiecewiseCombiner::finalize(H state) {
+ // Hash the remainder left in the buffer, which may be empty
+ return H::combine_contiguous(std::move(state), buf_, position_);
+}
+
} // namespace hash_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HASH_INTERNAL_HASH_H_
diff --git a/absl/hash/internal/spy_hash_state.h b/absl/hash/internal/spy_hash_state.h
index 57cd70b2..c0831208 100644
--- a/absl/hash/internal/spy_hash_state.h
+++ b/absl/hash/internal/spy_hash_state.h
@@ -25,7 +25,7 @@
#include "absl/strings/str_join.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace hash_internal {
// SpyHashState is an implementation of the HashState API that simply
@@ -147,6 +147,19 @@ class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> {
static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state,
const unsigned char* begin,
size_t size) {
+ const size_t large_chunk_stride = PiecewiseChunkSize();
+ if (size > large_chunk_stride) {
+ // Combining a large contiguous buffer must have the same effect as
+ // doing it piecewise by the stride length, followed by the (possibly
+ // empty) remainder.
+ while (size >= large_chunk_stride) {
+ hash_state = SpyHashStateImpl::combine_contiguous(
+ std::move(hash_state), begin, large_chunk_stride);
+ begin += large_chunk_stride;
+ size -= large_chunk_stride;
+ }
+ }
+
hash_state.hash_representation_.emplace_back(
reinterpret_cast<const char*>(begin), size);
return hash_state;
@@ -212,7 +225,7 @@ void AbslHashValue(SpyHashStateImpl<T>, const U&);
using SpyHashState = SpyHashStateImpl<void>;
} // namespace hash_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_
diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel
index f815ef94..2ba9d7cb 100644
--- a/absl/memory/BUILD.bazel
+++ b/absl/memory/BUILD.bazel
@@ -1,5 +1,5 @@
#
-# Copyright 2017 The Abseil Authors.
+# 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.
@@ -14,13 +14,12 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
"ABSL_DEFAULT_LINKOPTS",
"ABSL_TEST_COPTS",
- "ABSL_EXCEPTIONS_FLAG",
- "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
)
package(default_visibility = ["//visibility:public"])
@@ -45,7 +44,6 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":memory",
- "//absl/base",
"//absl/base:core_headers",
"@com_google_googletest//:gtest_main",
],
@@ -56,10 +54,11 @@ cc_test(
srcs = [
"memory_exception_safety_test.cc",
],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":memory",
+ "//absl/base:config",
"//absl/base:exception_safety_testing",
"@com_google_googletest//:gtest_main",
],
diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt
index 0a812203..78fb7e1b 100644
--- a/absl/memory/CMakeLists.txt
+++ b/absl/memory/CMakeLists.txt
@@ -36,7 +36,6 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::memory
- absl::base
absl::core_headers
gmock_main
)
@@ -48,11 +47,9 @@ absl_cc_test(
"memory_exception_safety_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::memory
+ absl::config
absl::exception_safety_testing
gmock_main
)
diff --git a/absl/memory/memory.h b/absl/memory/memory.h
index 7f525e4c..513f7103 100644
--- a/absl/memory/memory.h
+++ b/absl/memory/memory.h
@@ -34,7 +34,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// Function Template: WrapUnique()
@@ -93,11 +93,12 @@ struct MakeUniqueResult<T[N]> {
} // namespace memory_internal
-// gcc 4.8 has __cplusplus at 201301 but doesn't define make_unique. Other
-// supported compilers either just define __cplusplus as 201103 but have
-// make_unique (msvc), or have make_unique whenever __cplusplus > 201103 (clang)
+// gcc 4.8 has __cplusplus at 201301 but the libstdc++ shipped with it doesn't
+// define make_unique. Other supported compilers either just define __cplusplus
+// as 201103 but have make_unique (msvc), or have make_unique whenever
+// __cplusplus > 201103 (clang).
#if (__cplusplus > 201103L || defined(_MSC_VER)) && \
- !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8)
+ !(defined(__GLIBCXX__) && !defined(__cpp_lib_make_unique))
using std::make_unique;
#else
// -----------------------------------------------------------------------------
@@ -688,7 +689,7 @@ void CopyRange(Allocator& alloc, Iterator destination, InputIterator first,
}
}
} // namespace memory_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_MEMORY_MEMORY_H_
diff --git a/absl/memory/memory_exception_safety_test.cc b/absl/memory/memory_exception_safety_test.cc
index f5b39b34..1df72614 100644
--- a/absl/memory/memory_exception_safety_test.cc
+++ b/absl/memory/memory_exception_safety_test.cc
@@ -14,18 +14,19 @@
#include "absl/memory/memory.h"
+#include "absl/base/config.h"
+
+#ifdef ABSL_HAVE_EXCEPTIONS
+
#include "gtest/gtest.h"
#include "absl/base/internal/exception_safety_testing.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
constexpr int kLength = 50;
using Thrower = testing::ThrowingValue<testing::TypeSpec::kEverythingThrows>;
-using ThrowerStorage =
- absl::aligned_storage_t<sizeof(Thrower), alignof(Thrower)>;
-using ThrowerList = std::array<ThrowerStorage, kLength>;
TEST(MakeUnique, CheckForLeaks) {
constexpr int kValue = 321;
@@ -50,5 +51,7 @@ TEST(MakeUnique, CheckForLeaks) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
+
+#endif // ABSL_HAVE_EXCEPTIONS
diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel
index e004b509..c06d2d97 100644
--- a/absl/meta/BUILD.bazel
+++ b/absl/meta/BUILD.bazel
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index ae7130dd..ba87d2f0 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -41,8 +41,18 @@
#include "absl/base/config.h"
+// MSVC constructibility traits do not detect destructor properties and so our
+// implementations should not use them as a source-of-truth.
+#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)
+#define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1
+#endif
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
+
+// Defined and documented later on in this file.
+template <typename T>
+struct is_trivially_destructible;
// Defined and documented later on in this file.
template <typename T>
@@ -67,6 +77,20 @@ union SingleMemberUnion {
#endif // defined(_MSC_VER) && !defined(__GNUC__)
template <class T>
+struct IsTriviallyMoveConstructibleObject
+ : std::integral_constant<
+ bool, std::is_move_constructible<
+ type_traits_internal::SingleMemberUnion<T>>::value &&
+ absl::is_trivially_destructible<T>::value> {};
+
+template <class T>
+struct IsTriviallyCopyConstructibleObject
+ : std::integral_constant<
+ bool, std::is_copy_constructible<
+ type_traits_internal::SingleMemberUnion<T>>::value &&
+ absl::is_trivially_destructible<T>::value> {};
+
+template <class T>
struct IsTriviallyMoveAssignableReference : std::false_type {};
template <class T>
@@ -146,6 +170,18 @@ using IsMoveAssignableImpl = decltype(std::declval<T&>() = std::declval<T&&>());
} // namespace type_traits_internal
+// MSVC 19.20 has a regression that causes our workarounds to fail, but their
+// std forms now appear to be compliant.
+#if defined(_MSC_VER) && !defined(__clang__) && (_MSC_VER >= 1920)
+
+template <typename T>
+using is_copy_assignable = std::is_copy_assignable<T>;
+
+template <typename T>
+using is_move_assignable = std::is_move_assignable<T>;
+
+#else
+
template <typename T>
struct is_copy_assignable : type_traits_internal::is_detected<
type_traits_internal::IsCopyAssignableImpl, T> {
@@ -156,6 +192,8 @@ struct is_move_assignable : type_traits_internal::is_detected<
type_traits_internal::IsMoveAssignableImpl, T> {
};
+#endif
+
// void_t()
//
// Ignores the type of any its arguments and returns `void`. In general, this
@@ -244,7 +282,7 @@ struct is_function
// is_trivially_destructible()
//
-// Determines whether the passed type `T` is trivially destructable.
+// Determines whether the passed type `T` is trivially destructible.
//
// This metafunction is designed to be a drop-in replacement for the C++11
// `std::is_trivially_destructible()` metafunction for platforms that have
@@ -310,7 +348,9 @@ struct is_trivially_default_constructible
: std::integral_constant<bool, __has_trivial_constructor(T) &&
std::is_default_constructible<T>::value &&
is_trivially_destructible<T>::value> {
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
+ !defined( \
+ ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
private:
static constexpr bool compliant =
std::is_trivially_default_constructible<T>::value ==
@@ -341,10 +381,11 @@ template <typename T>
struct is_trivially_move_constructible
: std::conditional<
std::is_object<T>::value && !std::is_array<T>::value,
- std::is_move_constructible<
- type_traits_internal::SingleMemberUnion<T>>,
+ type_traits_internal::IsTriviallyMoveConstructibleObject<T>,
std::is_reference<T>>::type::type {
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
+ !defined( \
+ ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
private:
static constexpr bool compliant =
std::is_trivially_move_constructible<T>::value ==
@@ -375,10 +416,11 @@ template <typename T>
struct is_trivially_copy_constructible
: std::conditional<
std::is_object<T>::value && !std::is_array<T>::value,
- std::is_copy_constructible<
- type_traits_internal::SingleMemberUnion<T>>,
+ type_traits_internal::IsTriviallyCopyConstructibleObject<T>,
std::is_lvalue_reference<T>>::type::type {
-#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE
+#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \
+ !defined( \
+ ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION)
private:
static constexpr bool compliant =
std::is_trivially_copy_constructible<T>::value ==
@@ -410,7 +452,8 @@ struct is_trivially_copy_constructible
template <typename T>
struct is_trivially_move_assignable
: std::conditional<
- std::is_object<T>::value && !std::is_array<T>::value,
+ std::is_object<T>::value && !std::is_array<T>::value &&
+ std::is_move_assignable<T>::value,
std::is_move_assignable<type_traits_internal::SingleMemberUnion<T>>,
type_traits_internal::IsTriviallyMoveAssignableReference<T>>::type::
type {
@@ -710,7 +753,7 @@ using swap_internal::Swap;
using swap_internal::StdSwapIsUnconstrained;
} // namespace type_traits_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_META_TYPE_TRAITS_H_
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc
index a7a9c5c9..1aafd0d4 100644
--- a/absl/meta/type_traits_test.cc
+++ b/absl/meta/type_traits_test.cc
@@ -347,21 +347,6 @@ class Base {
virtual ~Base() {}
};
-// In GCC/Clang, std::is_trivially_constructible requires that the destructor is
-// trivial. However, MSVC doesn't require that. This results in different
-// behavior when checking is_trivially_constructible on any type with
-// nontrivial destructor. Since absl::is_trivially_default_constructible and
-// absl::is_trivially_copy_constructible both follows Clang/GCC's interpretation
-// and check is_trivially_destructible, it results in inconsistency with
-// std::is_trivially_xxx_constructible on MSVC. This macro is used to work
-// around this issue in test. In practice, a trivially constructible type
-// should also be trivially destructible.
-// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452
-// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116
-#ifndef _MSC_VER
-#define ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE 1
-#endif
-
// Old versions of libc++, around Clang 3.5 to 3.6, consider deleted destructors
// as also being trivial. With the resolution of CWG 1928 and CWG 1734, this
// is no longer considered true and has thus been amended.
@@ -499,11 +484,9 @@ TEST(TypeTraitsTest, TestTrivialDefaultCtor) {
EXPECT_FALSE(
absl::is_trivially_default_constructible<DeletedDefaultCtor>::value);
-#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE
// types with nontrivial destructor are nontrivial
EXPECT_FALSE(
absl::is_trivially_default_constructible<NontrivialDestructor>::value);
-#endif
// types with vtables
EXPECT_FALSE(absl::is_trivially_default_constructible<Base>::value);
@@ -546,6 +529,28 @@ TEST(TypeTraitsTest, TestTrivialDefaultCtor) {
#endif
}
+// GCC prior to 7.4 had a bug in its trivially-constructible traits
+// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80654).
+// This test makes sure that we do not depend on the trait in these cases when
+// implementing absl triviality traits.
+
+template <class T>
+struct BadConstructors {
+ BadConstructors() { static_assert(T::value, ""); }
+
+ BadConstructors(BadConstructors&&) { static_assert(T::value, ""); }
+
+ BadConstructors(const BadConstructors&) { static_assert(T::value, ""); }
+};
+
+TEST(TypeTraitsTest, TestTrivialityBadConstructors) {
+ using BadType = BadConstructors<int>;
+
+ EXPECT_FALSE(absl::is_trivially_default_constructible<BadType>::value);
+ EXPECT_FALSE(absl::is_trivially_move_constructible<BadType>::value);
+ EXPECT_FALSE(absl::is_trivially_copy_constructible<BadType>::value);
+}
+
TEST(TypeTraitsTest, TestTrivialMoveCtor) {
// Verify that arithmetic types and pointers have trivial move
// constructors.
@@ -585,11 +590,9 @@ TEST(TypeTraitsTest, TestTrivialMoveCtor) {
EXPECT_FALSE(
absl::is_trivially_move_constructible<NonCopyableOrMovable>::value);
-#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE
// type with nontrivial destructor are nontrivial move construbtible
EXPECT_FALSE(
absl::is_trivially_move_constructible<NontrivialDestructor>::value);
-#endif
// types with vtables
EXPECT_FALSE(absl::is_trivially_move_constructible<Base>::value);
@@ -660,11 +663,9 @@ TEST(TypeTraitsTest, TestTrivialCopyCtor) {
EXPECT_FALSE(
absl::is_trivially_copy_constructible<NonCopyableOrMovable>::value);
-#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE
// type with nontrivial destructor are nontrivial copy construbtible
EXPECT_FALSE(
absl::is_trivially_copy_constructible<NontrivialDestructor>::value);
-#endif
// types with vtables
EXPECT_FALSE(absl::is_trivially_copy_constructible<Base>::value);
diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel
index d9b561df..e09e52d2 100644
--- a/absl/numeric/BUILD.bazel
+++ b/absl/numeric/BUILD.bazel
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc
index a22f1e3e..b605a870 100644
--- a/absl/numeric/int128.cc
+++ b/absl/numeric/int128.cc
@@ -23,10 +23,10 @@
#include <type_traits>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
-const uint128 kuint128max = MakeUint128(std::numeric_limits<uint64_t>::max(),
- std::numeric_limits<uint64_t>::max());
+ABSL_DLL const uint128 kuint128max = MakeUint128(
+ std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max());
namespace {
@@ -245,7 +245,112 @@ std::ostream& operator<<(std::ostream& os, uint128 v) {
return os << rep;
}
-} // inline namespace lts_2019_08_08
+namespace {
+
+uint128 UnsignedAbsoluteValue(int128 v) {
+ // Cast to uint128 before possibly negating because -Int128Min() is undefined.
+ return Int128High64(v) < 0 ? -uint128(v) : uint128(v);
+}
+
+} // namespace
+
+#if !defined(ABSL_HAVE_INTRINSIC_INT128)
+namespace {
+
+template <typename T>
+int128 MakeInt128FromFloat(T v) {
+ // Conversion when v is NaN or cannot fit into int128 would be undefined
+ // behavior if using an intrinsic 128-bit integer.
+ assert(std::isfinite(v) && (std::numeric_limits<T>::max_exponent <= 127 ||
+ (v >= -std::ldexp(static_cast<T>(1), 127) &&
+ v < std::ldexp(static_cast<T>(1), 127))));
+
+ // We must convert the absolute value and then negate as needed, because
+ // floating point types are typically sign-magnitude. Otherwise, the
+ // difference between the high and low 64 bits when interpreted as two's
+ // complement overwhelms the precision of the mantissa.
+ uint128 result = v < 0 ? -MakeUint128FromFloat(-v) : MakeUint128FromFloat(v);
+ return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)),
+ Uint128Low64(result));
+}
+
+} // namespace
+
+int128::int128(float v) : int128(MakeInt128FromFloat(v)) {}
+int128::int128(double v) : int128(MakeInt128FromFloat(v)) {}
+int128::int128(long double v) : int128(MakeInt128FromFloat(v)) {}
+
+int128 operator/(int128 lhs, int128 rhs) {
+ assert(lhs != Int128Min() || rhs != -1); // UB on two's complement.
+
+ uint128 quotient = 0;
+ uint128 remainder = 0;
+ DivModImpl(UnsignedAbsoluteValue(lhs), UnsignedAbsoluteValue(rhs),
+ &quotient, &remainder);
+ if ((Int128High64(lhs) < 0) != (Int128High64(rhs) < 0)) quotient = -quotient;
+ return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(quotient)),
+ Uint128Low64(quotient));
+}
+
+int128 operator%(int128 lhs, int128 rhs) {
+ assert(lhs != Int128Min() || rhs != -1); // UB on two's complement.
+
+ uint128 quotient = 0;
+ uint128 remainder = 0;
+ DivModImpl(UnsignedAbsoluteValue(lhs), UnsignedAbsoluteValue(rhs),
+ &quotient, &remainder);
+ if (Int128High64(lhs) < 0) remainder = -remainder;
+ return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(remainder)),
+ Uint128Low64(remainder));
+}
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+std::ostream& operator<<(std::ostream& os, int128 v) {
+ std::ios_base::fmtflags flags = os.flags();
+ std::string rep;
+
+ // Add the sign if needed.
+ bool print_as_decimal =
+ (flags & std::ios::basefield) == std::ios::dec ||
+ (flags & std::ios::basefield) == std::ios_base::fmtflags();
+ if (print_as_decimal) {
+ if (Int128High64(v) < 0) {
+ rep = "-";
+ } else if (flags & std::ios::showpos) {
+ rep = "+";
+ }
+ }
+
+ rep.append(Uint128ToFormattedString(
+ print_as_decimal ? UnsignedAbsoluteValue(v) : uint128(v), os.flags()));
+
+ // Add the requisite padding.
+ std::streamsize width = os.width(0);
+ if (static_cast<size_t>(width) > rep.size()) {
+ switch (flags & std::ios::adjustfield) {
+ case std::ios::left:
+ rep.append(width - rep.size(), os.fill());
+ break;
+ case std::ios::internal:
+ if (print_as_decimal && (rep[0] == '+' || rep[0] == '-')) {
+ rep.insert(1, width - rep.size(), os.fill());
+ } else if ((flags & std::ios::basefield) == std::ios::hex &&
+ (flags & std::ios::showbase) && v != 0) {
+ rep.insert(2, width - rep.size(), os.fill());
+ } else {
+ rep.insert(0, width - rep.size(), os.fill());
+ }
+ break;
+ default: // std::ios::right
+ rep.insert(0, width - rep.size(), os.fill());
+ break;
+ }
+ }
+
+ return os << rep;
+}
+
+ABSL_NAMESPACE_END
} // namespace absl
namespace std {
@@ -272,4 +377,28 @@ constexpr int numeric_limits<absl::uint128>::max_exponent;
constexpr int numeric_limits<absl::uint128>::max_exponent10;
constexpr bool numeric_limits<absl::uint128>::traps;
constexpr bool numeric_limits<absl::uint128>::tinyness_before;
+
+constexpr bool numeric_limits<absl::int128>::is_specialized;
+constexpr bool numeric_limits<absl::int128>::is_signed;
+constexpr bool numeric_limits<absl::int128>::is_integer;
+constexpr bool numeric_limits<absl::int128>::is_exact;
+constexpr bool numeric_limits<absl::int128>::has_infinity;
+constexpr bool numeric_limits<absl::int128>::has_quiet_NaN;
+constexpr bool numeric_limits<absl::int128>::has_signaling_NaN;
+constexpr float_denorm_style numeric_limits<absl::int128>::has_denorm;
+constexpr bool numeric_limits<absl::int128>::has_denorm_loss;
+constexpr float_round_style numeric_limits<absl::int128>::round_style;
+constexpr bool numeric_limits<absl::int128>::is_iec559;
+constexpr bool numeric_limits<absl::int128>::is_bounded;
+constexpr bool numeric_limits<absl::int128>::is_modulo;
+constexpr int numeric_limits<absl::int128>::digits;
+constexpr int numeric_limits<absl::int128>::digits10;
+constexpr int numeric_limits<absl::int128>::max_digits10;
+constexpr int numeric_limits<absl::int128>::radix;
+constexpr int numeric_limits<absl::int128>::min_exponent;
+constexpr int numeric_limits<absl::int128>::min_exponent10;
+constexpr int numeric_limits<absl::int128>::max_exponent;
+constexpr int numeric_limits<absl::int128>::max_exponent10;
+constexpr bool numeric_limits<absl::int128>::traps;
+constexpr bool numeric_limits<absl::int128>::tinyness_before;
} // namespace std
diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h
index a9693a27..636e3a5b 100644
--- a/absl/numeric/int128.h
+++ b/absl/numeric/int128.h
@@ -17,10 +17,7 @@
// File: int128.h
// -----------------------------------------------------------------------------
//
-// This header file defines 128-bit integer types.
-//
-// Currently, this file defines `uint128`, an unsigned 128-bit integer;
-// a signed 128-bit integer is forthcoming.
+// This header file defines 128-bit integer types, `uint128` and `int128`.
#ifndef ABSL_NUMERIC_INT128_H_
#define ABSL_NUMERIC_INT128_H_
@@ -52,7 +49,9 @@
#endif // defined(_MSC_VER)
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
+
+class int128;
// uint128
//
@@ -117,6 +116,7 @@ class
constexpr uint128(__int128 v); // NOLINT(runtime/explicit)
constexpr uint128(unsigned __int128 v); // NOLINT(runtime/explicit)
#endif // ABSL_HAVE_INTRINSIC_INT128
+ constexpr uint128(int128 v); // NOLINT(runtime/explicit)
explicit uint128(float v);
explicit uint128(double v);
explicit uint128(long double v);
@@ -132,6 +132,7 @@ class
uint128& operator=(__int128 v);
uint128& operator=(unsigned __int128 v);
#endif // ABSL_HAVE_INTRINSIC_INT128
+ uint128& operator=(int128 v);
// Conversion operators to other arithmetic types
constexpr explicit operator bool() const;
@@ -233,7 +234,7 @@ class
// Prefer to use the constexpr `Uint128Max()`.
//
// TODO(absl-team) deprecate kuint128max once migration tool is released.
-extern const uint128 kuint128max;
+ABSL_DLL extern const uint128 kuint128max;
// allow uint128 to be logged
std::ostream& operator<<(std::ostream& os, uint128 v);
@@ -245,7 +246,7 @@ constexpr uint128 Uint128Max() {
(std::numeric_limits<uint64_t>::max)());
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// Specialized numeric_limits for uint128.
@@ -293,13 +294,246 @@ class numeric_limits<absl::uint128> {
};
} // namespace std
-// TODO(absl-team): Implement signed 128-bit type
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// int128
+//
+// A signed 128-bit integer type. The API is meant to mimic an intrinsic
+// integral type as closely as is practical, including exhibiting undefined
+// behavior in analogous cases (e.g. division by zero).
+//
+// An `int128` supports the following:
+//
+// * Implicit construction from integral types
+// * Explicit conversion to integral types
+//
+// However, an `int128` differs from intrinsic integral types in the following
+// ways:
+//
+// * It is not implicitly convertible to other integral types.
+// * Requires explicit construction from and conversion to floating point
+// types.
+
+// Additionally, if your compiler supports `__int128`, `int128` is
+// interoperable with that type. (Abseil checks for this compatibility through
+// the `ABSL_HAVE_INTRINSIC_INT128` macro.)
+//
+// The design goal for `int128` is that it will be compatible with a future
+// `int128_t`, if that type becomes a part of the standard.
+//
+// Example:
+//
+// float y = absl::int128(17); // Error. int128 cannot be implicitly
+// // converted to float.
+//
+// absl::int128 v;
+// int64_t i = v; // Error
+// int64_t i = static_cast<int64_t>(v); // OK
+//
+class int128 {
+ public:
+ int128() = default;
+
+ // Constructors from arithmetic types
+ constexpr int128(int v); // NOLINT(runtime/explicit)
+ constexpr int128(unsigned int v); // NOLINT(runtime/explicit)
+ constexpr int128(long v); // NOLINT(runtime/int)
+ constexpr int128(unsigned long v); // NOLINT(runtime/int)
+ constexpr int128(long long v); // NOLINT(runtime/int)
+ constexpr int128(unsigned long long v); // NOLINT(runtime/int)
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+ constexpr int128(__int128 v); // NOLINT(runtime/explicit)
+ constexpr explicit int128(unsigned __int128 v);
+#endif // ABSL_HAVE_INTRINSIC_INT128
+ constexpr explicit int128(uint128 v);
+ explicit int128(float v);
+ explicit int128(double v);
+ explicit int128(long double v);
+
+ // Assignment operators from arithmetic types
+ int128& operator=(int v);
+ int128& operator=(unsigned int v);
+ int128& operator=(long v); // NOLINT(runtime/int)
+ int128& operator=(unsigned long v); // NOLINT(runtime/int)
+ int128& operator=(long long v); // NOLINT(runtime/int)
+ int128& operator=(unsigned long long v); // NOLINT(runtime/int)
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+ int128& operator=(__int128 v);
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+ // Conversion operators to other arithmetic types
+ constexpr explicit operator bool() const;
+ constexpr explicit operator char() const;
+ constexpr explicit operator signed char() const;
+ constexpr explicit operator unsigned char() const;
+ constexpr explicit operator char16_t() const;
+ constexpr explicit operator char32_t() const;
+ constexpr explicit operator ABSL_INTERNAL_WCHAR_T() const;
+ constexpr explicit operator short() const; // NOLINT(runtime/int)
+ // NOLINTNEXTLINE(runtime/int)
+ constexpr explicit operator unsigned short() const;
+ constexpr explicit operator int() const;
+ constexpr explicit operator unsigned int() const;
+ constexpr explicit operator long() const; // NOLINT(runtime/int)
+ // NOLINTNEXTLINE(runtime/int)
+ constexpr explicit operator unsigned long() const;
+ // NOLINTNEXTLINE(runtime/int)
+ constexpr explicit operator long long() const;
+ // NOLINTNEXTLINE(runtime/int)
+ constexpr explicit operator unsigned long long() const;
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+ constexpr explicit operator __int128() const;
+ constexpr explicit operator unsigned __int128() const;
+#endif // ABSL_HAVE_INTRINSIC_INT128
+ explicit operator float() const;
+ explicit operator double() const;
+ explicit operator long double() const;
+
+ // Trivial copy constructor, assignment operator and destructor.
+
+ // Arithmetic operators
+ int128& operator+=(int128 other);
+ int128& operator-=(int128 other);
+ int128& operator*=(int128 other);
+ int128& operator/=(int128 other);
+ int128& operator%=(int128 other);
+ int128 operator++(int); // postfix increment: i++
+ int128 operator--(int); // postfix decrement: i--
+ int128& operator++(); // prefix increment: ++i
+ int128& operator--(); // prefix decrement: --i
+ int128& operator&=(int128 other);
+ int128& operator|=(int128 other);
+ int128& operator^=(int128 other);
+ int128& operator<<=(int amount);
+ int128& operator>>=(int amount);
+
+ // Int128Low64()
+ //
+ // Returns the lower 64-bit value of a `int128` value.
+ friend constexpr uint64_t Int128Low64(int128 v);
+
+ // Int128High64()
+ //
+ // Returns the higher 64-bit value of a `int128` value.
+ friend constexpr int64_t Int128High64(int128 v);
+
+ // MakeInt128()
+ //
+ // Constructs a `int128` numeric value from two 64-bit integers. Note that
+ // signedness is conveyed in the upper `high` value.
+ //
+ // (absl::int128(1) << 64) * high + low
+ //
+ // Note that this factory function is the only way to construct a `int128`
+ // from integer values greater than 2^64 or less than -2^64.
+ //
+ // Example:
+ //
+ // absl::int128 big = absl::MakeInt128(1, 0);
+ // absl::int128 big_n = absl::MakeInt128(-1, 0);
+ friend constexpr int128 MakeInt128(int64_t high, uint64_t low);
+
+ // Int128Max()
+ //
+ // Returns the maximum value for a 128-bit signed integer.
+ friend constexpr int128 Int128Max();
+
+ // Int128Min()
+ //
+ // Returns the minimum value for a 128-bit signed integer.
+ friend constexpr int128 Int128Min();
+
+ // Support for absl::Hash.
+ template <typename H>
+ friend H AbslHashValue(H h, int128 v) {
+ return H::combine(std::move(h), Int128High64(v), Int128Low64(v));
+ }
+
+ private:
+ constexpr int128(int64_t high, uint64_t low);
+
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+ __int128 v_;
+#else // ABSL_HAVE_INTRINSIC_INT128
+#if defined(ABSL_IS_LITTLE_ENDIAN)
+ uint64_t lo_;
+ int64_t hi_;
+#elif defined(ABSL_IS_BIG_ENDIAN)
+ int64_t hi_;
+ uint64_t lo_;
+#else // byte order
+#error "Unsupported byte order: must be little-endian or big-endian."
+#endif // byte order
+#endif // ABSL_HAVE_INTRINSIC_INT128
+};
+
+std::ostream& operator<<(std::ostream& os, int128 v);
+
+// TODO(absl-team) add operator>>(std::istream&, int128)
+
+constexpr int128 Int128Max() {
+ return int128((std::numeric_limits<int64_t>::max)(),
+ (std::numeric_limits<uint64_t>::max)());
+}
+
+constexpr int128 Int128Min() {
+ return int128((std::numeric_limits<int64_t>::min)(), 0);
+}
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+// Specialized numeric_limits for int128.
+namespace std {
+template <>
+class numeric_limits<absl::int128> {
+ public:
+ static constexpr bool is_specialized = true;
+ static constexpr bool is_signed = true;
+ static constexpr bool is_integer = true;
+ static constexpr bool is_exact = true;
+ static constexpr bool has_infinity = false;
+ static constexpr bool has_quiet_NaN = false;
+ static constexpr bool has_signaling_NaN = false;
+ static constexpr float_denorm_style has_denorm = denorm_absent;
+ static constexpr bool has_denorm_loss = false;
+ static constexpr float_round_style round_style = round_toward_zero;
+ static constexpr bool is_iec559 = false;
+ static constexpr bool is_bounded = true;
+ static constexpr bool is_modulo = false;
+ static constexpr int digits = 127;
+ static constexpr int digits10 = 38;
+ static constexpr int max_digits10 = 0;
+ static constexpr int radix = 2;
+ static constexpr int min_exponent = 0;
+ static constexpr int min_exponent10 = 0;
+ static constexpr int max_exponent = 0;
+ static constexpr int max_exponent10 = 0;
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+ static constexpr bool traps = numeric_limits<__int128>::traps;
+#else // ABSL_HAVE_INTRINSIC_INT128
+ static constexpr bool traps = numeric_limits<uint64_t>::traps;
+#endif // ABSL_HAVE_INTRINSIC_INT128
+ static constexpr bool tinyness_before = false;
+
+ static constexpr absl::int128 (min)() { return absl::Int128Min(); }
+ static constexpr absl::int128 lowest() { return absl::Int128Min(); }
+ static constexpr absl::int128 (max)() { return absl::Int128Max(); }
+ static constexpr absl::int128 epsilon() { return 0; }
+ static constexpr absl::int128 round_error() { return 0; }
+ static constexpr absl::int128 infinity() { return 0; }
+ static constexpr absl::int128 quiet_NaN() { return 0; }
+ static constexpr absl::int128 signaling_NaN() { return 0; }
+ static constexpr absl::int128 denorm_min() { return 0; }
+};
+} // namespace std
// --------------------------------------------------------------------------
// Implementation details follow
// --------------------------------------------------------------------------
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
constexpr uint128 MakeUint128(uint64_t high, uint64_t low) {
return uint128(high, low);
@@ -342,6 +576,10 @@ inline uint128& uint128::operator=(unsigned __int128 v) {
}
#endif // ABSL_HAVE_INTRINSIC_INT128
+inline uint128& uint128::operator=(int128 v) {
+ return *this = uint128(v);
+}
+
// Arithmetic operators.
uint128 operator<<(uint128 lhs, int amount);
@@ -423,6 +661,9 @@ constexpr uint128::uint128(unsigned __int128 v)
hi_{static_cast<uint64_t>(v >> 64)} {}
#endif // ABSL_HAVE_INTRINSIC_INT128
+constexpr uint128::uint128(int128 v)
+ : lo_{Int128Low64(v)}, hi_{static_cast<uint64_t>(Int128High64(v))} {}
+
#elif defined(ABSL_IS_BIG_ENDIAN)
constexpr uint128::uint128(uint64_t high, uint64_t low)
@@ -453,6 +694,9 @@ constexpr uint128::uint128(unsigned __int128 v)
lo_{static_cast<uint64_t>(v & ~uint64_t{0})} {}
#endif // ABSL_HAVE_INTRINSIC_INT128
+constexpr uint128::uint128(int128 v)
+ : hi_{static_cast<uint64_t>(Int128High64(v))}, lo_{Int128Low64(v)} {}
+
#else // byte order
#error "Unsupported byte order: must be little-endian or big-endian."
#endif // byte order
@@ -722,13 +966,124 @@ inline uint128& uint128::operator--() {
return *this;
}
+constexpr int128 MakeInt128(int64_t high, uint64_t low) {
+ return int128(high, low);
+}
+
+// Assignment from integer types.
+inline int128& int128::operator=(int v) {
+ return *this = int128(v);
+}
+
+inline int128& int128::operator=(unsigned int v) {
+ return *this = int128(v);
+}
+
+inline int128& int128::operator=(long v) { // NOLINT(runtime/int)
+ return *this = int128(v);
+}
+
+// NOLINTNEXTLINE(runtime/int)
+inline int128& int128::operator=(unsigned long v) {
+ return *this = int128(v);
+}
+
+// NOLINTNEXTLINE(runtime/int)
+inline int128& int128::operator=(long long v) {
+ return *this = int128(v);
+}
+
+// NOLINTNEXTLINE(runtime/int)
+inline int128& int128::operator=(unsigned long long v) {
+ return *this = int128(v);
+}
+
+// Arithmetic operators.
+
+int128 operator+(int128 lhs, int128 rhs);
+int128 operator-(int128 lhs, int128 rhs);
+int128 operator*(int128 lhs, int128 rhs);
+int128 operator/(int128 lhs, int128 rhs);
+int128 operator%(int128 lhs, int128 rhs);
+int128 operator|(int128 lhs, int128 rhs);
+int128 operator&(int128 lhs, int128 rhs);
+int128 operator^(int128 lhs, int128 rhs);
+int128 operator<<(int128 lhs, int amount);
+int128 operator>>(int128 lhs, int amount);
+
+inline int128& int128::operator+=(int128 other) {
+ *this = *this + other;
+ return *this;
+}
+
+inline int128& int128::operator-=(int128 other) {
+ *this = *this - other;
+ return *this;
+}
+
+inline int128& int128::operator*=(int128 other) {
+ *this = *this * other;
+ return *this;
+}
+
+inline int128& int128::operator/=(int128 other) {
+ *this = *this / other;
+ return *this;
+}
+
+inline int128& int128::operator%=(int128 other) {
+ *this = *this % other;
+ return *this;
+}
+
+inline int128& int128::operator|=(int128 other) {
+ *this = *this | other;
+ return *this;
+}
+
+inline int128& int128::operator&=(int128 other) {
+ *this = *this & other;
+ return *this;
+}
+
+inline int128& int128::operator^=(int128 other) {
+ *this = *this ^ other;
+ return *this;
+}
+
+inline int128& int128::operator<<=(int amount) {
+ *this = *this << amount;
+ return *this;
+}
+
+inline int128& int128::operator>>=(int amount) {
+ *this = *this >> amount;
+ return *this;
+}
+
+namespace int128_internal {
+
+// Casts from unsigned to signed while preserving the underlying binary
+// representation.
+constexpr int64_t BitCastToSigned(uint64_t v) {
+ // Casting an unsigned integer to a signed integer of the same
+ // width is implementation defined behavior if the source value would not fit
+ // in the destination type. We step around it with a roundtrip bitwise not
+ // operation to make sure this function remains constexpr. Clang, GCC, and
+ // MSVC optimize this to a no-op on x86-64.
+ return v & (uint64_t{1} << 63) ? ~static_cast<int64_t>(~v)
+ : static_cast<int64_t>(v);
+}
+
+} // namespace int128_internal
+
#if defined(ABSL_HAVE_INTRINSIC_INT128)
-#include "absl/numeric/int128_have_intrinsic.inc"
+#include "absl/numeric/int128_have_intrinsic.inc" // IWYU pragma: export
#else // ABSL_HAVE_INTRINSIC_INT128
-#include "absl/numeric/int128_no_intrinsic.inc"
+#include "absl/numeric/int128_no_intrinsic.inc" // IWYU pragma: export
#endif // ABSL_HAVE_INTRINSIC_INT128
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#undef ABSL_INTERNAL_WCHAR_T
diff --git a/absl/numeric/int128_have_intrinsic.inc b/absl/numeric/int128_have_intrinsic.inc
index c7ea6834..d6c76dd3 100644
--- a/absl/numeric/int128_have_intrinsic.inc
+++ b/absl/numeric/int128_have_intrinsic.inc
@@ -16,3 +16,287 @@
// This file contains :int128 implementation details that depend on internal
// representation when ABSL_HAVE_INTRINSIC_INT128 is defined. This file is
// included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined.
+
+namespace int128_internal {
+
+// Casts from unsigned to signed while preserving the underlying binary
+// representation.
+constexpr __int128 BitCastToSigned(unsigned __int128 v) {
+ // Casting an unsigned integer to a signed integer of the same
+ // width is implementation defined behavior if the source value would not fit
+ // in the destination type. We step around it with a roundtrip bitwise not
+ // operation to make sure this function remains constexpr. Clang and GCC
+ // optimize this to a no-op on x86-64.
+ return v & (static_cast<unsigned __int128>(1) << 127)
+ ? ~static_cast<__int128>(~v)
+ : static_cast<__int128>(v);
+}
+
+} // namespace int128_internal
+
+inline int128& int128::operator=(__int128 v) {
+ v_ = v;
+ return *this;
+}
+
+constexpr uint64_t Int128Low64(int128 v) {
+ return static_cast<uint64_t>(v.v_ & ~uint64_t{0});
+}
+
+constexpr int64_t Int128High64(int128 v) {
+ // Initially cast to unsigned to prevent a right shift on a negative value.
+ return int128_internal::BitCastToSigned(
+ static_cast<uint64_t>(static_cast<unsigned __int128>(v.v_) >> 64));
+}
+
+constexpr int128::int128(int64_t high, uint64_t low)
+ // Initially cast to unsigned to prevent a left shift that overflows.
+ : v_(int128_internal::BitCastToSigned(static_cast<unsigned __int128>(high)
+ << 64) |
+ low) {}
+
+
+constexpr int128::int128(int v) : v_{v} {}
+
+constexpr int128::int128(long v) : v_{v} {} // NOLINT(runtime/int)
+
+constexpr int128::int128(long long v) : v_{v} {} // NOLINT(runtime/int)
+
+constexpr int128::int128(__int128 v) : v_{v} {}
+
+constexpr int128::int128(unsigned int v) : v_{v} {}
+
+constexpr int128::int128(unsigned long v) : v_{v} {} // NOLINT(runtime/int)
+
+// NOLINTNEXTLINE(runtime/int)
+constexpr int128::int128(unsigned long long v) : v_{v} {}
+
+constexpr int128::int128(unsigned __int128 v) : v_{static_cast<__int128>(v)} {}
+
+inline int128::int128(float v) {
+ v_ = static_cast<__int128>(v);
+}
+
+inline int128::int128(double v) {
+ v_ = static_cast<__int128>(v);
+}
+
+inline int128::int128(long double v) {
+ v_ = static_cast<__int128>(v);
+}
+
+constexpr int128::int128(uint128 v) : v_{static_cast<__int128>(v)} {}
+
+constexpr int128::operator bool() const { return static_cast<bool>(v_); }
+
+constexpr int128::operator char() const { return static_cast<char>(v_); }
+
+constexpr int128::operator signed char() const {
+ return static_cast<signed char>(v_);
+}
+
+constexpr int128::operator unsigned char() const {
+ return static_cast<unsigned char>(v_);
+}
+
+constexpr int128::operator char16_t() const {
+ return static_cast<char16_t>(v_);
+}
+
+constexpr int128::operator char32_t() const {
+ return static_cast<char32_t>(v_);
+}
+
+constexpr int128::operator ABSL_INTERNAL_WCHAR_T() const {
+ return static_cast<ABSL_INTERNAL_WCHAR_T>(v_);
+}
+
+constexpr int128::operator short() const { // NOLINT(runtime/int)
+ return static_cast<short>(v_); // NOLINT(runtime/int)
+}
+
+constexpr int128::operator unsigned short() const { // NOLINT(runtime/int)
+ return static_cast<unsigned short>(v_); // NOLINT(runtime/int)
+}
+
+constexpr int128::operator int() const {
+ return static_cast<int>(v_);
+}
+
+constexpr int128::operator unsigned int() const {
+ return static_cast<unsigned int>(v_);
+}
+
+constexpr int128::operator long() const { // NOLINT(runtime/int)
+ return static_cast<long>(v_); // NOLINT(runtime/int)
+}
+
+constexpr int128::operator unsigned long() const { // NOLINT(runtime/int)
+ return static_cast<unsigned long>(v_); // NOLINT(runtime/int)
+}
+
+constexpr int128::operator long long() const { // NOLINT(runtime/int)
+ return static_cast<long long>(v_); // NOLINT(runtime/int)
+}
+
+constexpr int128::operator unsigned long long() const { // NOLINT(runtime/int)
+ return static_cast<unsigned long long>(v_); // NOLINT(runtime/int)
+}
+
+constexpr int128::operator __int128() const { return v_; }
+
+constexpr int128::operator unsigned __int128() const {
+ return static_cast<unsigned __int128>(v_);
+}
+
+// Clang on PowerPC sometimes produces incorrect __int128 to floating point
+// conversions. In that case, we do the conversion with a similar implementation
+// to the conversion operators in int128_no_intrinsic.inc.
+#if defined(__clang__) && !defined(__ppc64__)
+inline int128::operator float() const { return static_cast<float>(v_); }
+
+inline int128::operator double () const { return static_cast<double>(v_); }
+
+inline int128::operator long double() const {
+ return static_cast<long double>(v_);
+}
+
+#else // Clang on PowerPC
+// Forward declaration for conversion operators to floating point types.
+int128 operator-(int128 v);
+bool operator!=(int128 lhs, int128 rhs);
+
+inline int128::operator float() const {
+ // We must convert the absolute value and then negate as needed, because
+ // floating point types are typically sign-magnitude. Otherwise, the
+ // difference between the high and low 64 bits when interpreted as two's
+ // complement overwhelms the precision of the mantissa.
+ //
+ // Also check to make sure we don't negate Int128Min()
+ return v_ < 0 && *this != Int128Min()
+ ? -static_cast<float>(-*this)
+ : static_cast<float>(Int128Low64(*this)) +
+ std::ldexp(static_cast<float>(Int128High64(*this)), 64);
+}
+
+inline int128::operator double() const {
+ // See comment in int128::operator float() above.
+ return v_ < 0 && *this != Int128Min()
+ ? -static_cast<double>(-*this)
+ : static_cast<double>(Int128Low64(*this)) +
+ std::ldexp(static_cast<double>(Int128High64(*this)), 64);
+}
+
+inline int128::operator long double() const {
+ // See comment in int128::operator float() above.
+ return v_ < 0 && *this != Int128Min()
+ ? -static_cast<long double>(-*this)
+ : static_cast<long double>(Int128Low64(*this)) +
+ std::ldexp(static_cast<long double>(Int128High64(*this)),
+ 64);
+}
+#endif // Clang on PowerPC
+
+// Comparison operators.
+
+inline bool operator==(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) == static_cast<__int128>(rhs);
+}
+
+inline bool operator!=(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) != static_cast<__int128>(rhs);
+}
+
+inline bool operator<(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) < static_cast<__int128>(rhs);
+}
+
+inline bool operator>(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) > static_cast<__int128>(rhs);
+}
+
+inline bool operator<=(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) <= static_cast<__int128>(rhs);
+}
+
+inline bool operator>=(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) >= static_cast<__int128>(rhs);
+}
+
+// Unary operators.
+
+inline int128 operator-(int128 v) {
+ return -static_cast<__int128>(v);
+}
+
+inline bool operator!(int128 v) {
+ return !static_cast<__int128>(v);
+}
+
+inline int128 operator~(int128 val) {
+ return ~static_cast<__int128>(val);
+}
+
+// Arithmetic operators.
+
+inline int128 operator+(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) + static_cast<__int128>(rhs);
+}
+
+inline int128 operator-(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) - static_cast<__int128>(rhs);
+}
+
+inline int128 operator*(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) * static_cast<__int128>(rhs);
+}
+
+inline int128 operator/(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) / static_cast<__int128>(rhs);
+}
+
+inline int128 operator%(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) % static_cast<__int128>(rhs);
+}
+
+inline int128 int128::operator++(int) {
+ int128 tmp(*this);
+ ++v_;
+ return tmp;
+}
+
+inline int128 int128::operator--(int) {
+ int128 tmp(*this);
+ --v_;
+ return tmp;
+}
+
+inline int128& int128::operator++() {
+ ++v_;
+ return *this;
+}
+
+inline int128& int128::operator--() {
+ --v_;
+ return *this;
+}
+
+inline int128 operator|(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) | static_cast<__int128>(rhs);
+}
+
+inline int128 operator&(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) & static_cast<__int128>(rhs);
+}
+
+inline int128 operator^(int128 lhs, int128 rhs) {
+ return static_cast<__int128>(lhs) ^ static_cast<__int128>(rhs);
+}
+
+inline int128 operator<<(int128 lhs, int amount) {
+ return static_cast<__int128>(lhs) << amount;
+}
+
+inline int128 operator>>(int128 lhs, int amount) {
+ return static_cast<__int128>(lhs) >> amount;
+}
diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc
index 046cb9b3..c753771a 100644
--- a/absl/numeric/int128_no_intrinsic.inc
+++ b/absl/numeric/int128_no_intrinsic.inc
@@ -16,3 +16,293 @@
// This file contains :int128 implementation details that depend on internal
// representation when ABSL_HAVE_INTRINSIC_INT128 is *not* defined. This file
// is included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined.
+
+constexpr uint64_t Int128Low64(int128 v) { return v.lo_; }
+
+constexpr int64_t Int128High64(int128 v) { return v.hi_; }
+
+#if defined(ABSL_IS_LITTLE_ENDIAN)
+
+constexpr int128::int128(int64_t high, uint64_t low) :
+ lo_(low), hi_(high) {}
+
+constexpr int128::int128(int v)
+ : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {}
+constexpr int128::int128(long v) // NOLINT(runtime/int)
+ : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {}
+constexpr int128::int128(long long v) // NOLINT(runtime/int)
+ : lo_{static_cast<uint64_t>(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {}
+
+constexpr int128::int128(unsigned int v) : lo_{v}, hi_{0} {}
+// NOLINTNEXTLINE(runtime/int)
+constexpr int128::int128(unsigned long v) : lo_{v}, hi_{0} {}
+// NOLINTNEXTLINE(runtime/int)
+constexpr int128::int128(unsigned long long v) : lo_{v}, hi_{0} {}
+
+constexpr int128::int128(uint128 v)
+ : lo_{Uint128Low64(v)}, hi_{static_cast<int64_t>(Uint128High64(v))} {}
+
+#elif defined(ABSL_IS_BIG_ENDIAN)
+
+constexpr int128::int128(int64_t high, uint64_t low) :
+ hi_{high}, lo_{low} {}
+
+constexpr int128::int128(int v)
+ : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {}
+constexpr int128::int128(long v) // NOLINT(runtime/int)
+ : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {}
+constexpr int128::int128(long long v) // NOLINT(runtime/int)
+ : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast<uint64_t>(v)} {}
+
+constexpr int128::int128(unsigned int v) : hi_{0}, lo_{v} {}
+// NOLINTNEXTLINE(runtime/int)
+constexpr int128::int128(unsigned long v) : hi_{0}, lo_{v} {}
+// NOLINTNEXTLINE(runtime/int)
+constexpr int128::int128(unsigned long long v) : hi_{0}, lo_{v} {}
+
+constexpr int128::int128(uint128 v)
+ : hi_{static_cast<int64_t>(Uint128High64(v))}, lo_{Uint128Low64(v)} {}
+
+#else // byte order
+#error "Unsupported byte order: must be little-endian or big-endian."
+#endif // byte order
+
+constexpr int128::operator bool() const { return lo_ || hi_; }
+
+constexpr int128::operator char() const {
+ // NOLINTNEXTLINE(runtime/int)
+ return static_cast<char>(static_cast<long long>(*this));
+}
+
+constexpr int128::operator signed char() const {
+ // NOLINTNEXTLINE(runtime/int)
+ return static_cast<signed char>(static_cast<long long>(*this));
+}
+
+constexpr int128::operator unsigned char() const {
+ return static_cast<unsigned char>(lo_);
+}
+
+constexpr int128::operator char16_t() const {
+ return static_cast<char16_t>(lo_);
+}
+
+constexpr int128::operator char32_t() const {
+ return static_cast<char32_t>(lo_);
+}
+
+constexpr int128::operator ABSL_INTERNAL_WCHAR_T() const {
+ // NOLINTNEXTLINE(runtime/int)
+ return static_cast<ABSL_INTERNAL_WCHAR_T>(static_cast<long long>(*this));
+}
+
+constexpr int128::operator short() const { // NOLINT(runtime/int)
+ // NOLINTNEXTLINE(runtime/int)
+ return static_cast<short>(static_cast<long long>(*this));
+}
+
+constexpr int128::operator unsigned short() const { // NOLINT(runtime/int)
+ return static_cast<unsigned short>(lo_); // NOLINT(runtime/int)
+}
+
+constexpr int128::operator int() const {
+ // NOLINTNEXTLINE(runtime/int)
+ return static_cast<int>(static_cast<long long>(*this));
+}
+
+constexpr int128::operator unsigned int() const {
+ return static_cast<unsigned int>(lo_);
+}
+
+constexpr int128::operator long() const { // NOLINT(runtime/int)
+ // NOLINTNEXTLINE(runtime/int)
+ return static_cast<long>(static_cast<long long>(*this));
+}
+
+constexpr int128::operator unsigned long() const { // NOLINT(runtime/int)
+ return static_cast<unsigned long>(lo_); // NOLINT(runtime/int)
+}
+
+constexpr int128::operator long long() const { // NOLINT(runtime/int)
+ // We don't bother checking the value of hi_. If *this < 0, lo_'s high bit
+ // must be set in order for the value to fit into a long long. Conversely, if
+ // lo_'s high bit is set, *this must be < 0 for the value to fit.
+ return int128_internal::BitCastToSigned(lo_);
+}
+
+constexpr int128::operator unsigned long long() const { // NOLINT(runtime/int)
+ return static_cast<unsigned long long>(lo_); // NOLINT(runtime/int)
+}
+
+// Forward declaration for conversion operators to floating point types.
+int128 operator-(int128 v);
+bool operator!=(int128 lhs, int128 rhs);
+
+inline int128::operator float() const {
+ // We must convert the absolute value and then negate as needed, because
+ // floating point types are typically sign-magnitude. Otherwise, the
+ // difference between the high and low 64 bits when interpreted as two's
+ // complement overwhelms the precision of the mantissa.
+ //
+ // Also check to make sure we don't negate Int128Min()
+ return hi_ < 0 && *this != Int128Min()
+ ? -static_cast<float>(-*this)
+ : static_cast<float>(lo_) +
+ std::ldexp(static_cast<float>(hi_), 64);
+}
+
+inline int128::operator double() const {
+ // See comment in int128::operator float() above.
+ return hi_ < 0 && *this != Int128Min()
+ ? -static_cast<double>(-*this)
+ : static_cast<double>(lo_) +
+ std::ldexp(static_cast<double>(hi_), 64);
+}
+
+inline int128::operator long double() const {
+ // See comment in int128::operator float() above.
+ return hi_ < 0 && *this != Int128Min()
+ ? -static_cast<long double>(-*this)
+ : static_cast<long double>(lo_) +
+ std::ldexp(static_cast<long double>(hi_), 64);
+}
+
+// Comparison operators.
+
+inline bool operator==(int128 lhs, int128 rhs) {
+ return (Int128Low64(lhs) == Int128Low64(rhs) &&
+ Int128High64(lhs) == Int128High64(rhs));
+}
+
+inline bool operator!=(int128 lhs, int128 rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator<(int128 lhs, int128 rhs) {
+ return (Int128High64(lhs) == Int128High64(rhs))
+ ? (Int128Low64(lhs) < Int128Low64(rhs))
+ : (Int128High64(lhs) < Int128High64(rhs));
+}
+
+inline bool operator>(int128 lhs, int128 rhs) {
+ return (Int128High64(lhs) == Int128High64(rhs))
+ ? (Int128Low64(lhs) > Int128Low64(rhs))
+ : (Int128High64(lhs) > Int128High64(rhs));
+}
+
+inline bool operator<=(int128 lhs, int128 rhs) {
+ return !(lhs > rhs);
+}
+
+inline bool operator>=(int128 lhs, int128 rhs) {
+ return !(lhs < rhs);
+}
+
+// Unary operators.
+
+inline int128 operator-(int128 v) {
+ int64_t hi = ~Int128High64(v);
+ uint64_t lo = ~Int128Low64(v) + 1;
+ if (lo == 0) ++hi; // carry
+ return MakeInt128(hi, lo);
+}
+
+inline bool operator!(int128 v) {
+ return !Int128Low64(v) && !Int128High64(v);
+}
+
+inline int128 operator~(int128 val) {
+ return MakeInt128(~Int128High64(val), ~Int128Low64(val));
+}
+
+// Arithmetic operators.
+
+inline int128 operator+(int128 lhs, int128 rhs) {
+ int128 result = MakeInt128(Int128High64(lhs) + Int128High64(rhs),
+ Int128Low64(lhs) + Int128Low64(rhs));
+ if (Int128Low64(result) < Int128Low64(lhs)) { // check for carry
+ return MakeInt128(Int128High64(result) + 1, Int128Low64(result));
+ }
+ return result;
+}
+
+inline int128 operator-(int128 lhs, int128 rhs) {
+ int128 result = MakeInt128(Int128High64(lhs) - Int128High64(rhs),
+ Int128Low64(lhs) - Int128Low64(rhs));
+ if (Int128Low64(lhs) < Int128Low64(rhs)) { // check for carry
+ return MakeInt128(Int128High64(result) - 1, Int128Low64(result));
+ }
+ return result;
+}
+
+inline int128 operator*(int128 lhs, int128 rhs) {
+ uint128 result = uint128(lhs) * rhs;
+ return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)),
+ Uint128Low64(result));
+}
+
+inline int128 int128::operator++(int) {
+ int128 tmp(*this);
+ *this += 1;
+ return tmp;
+}
+
+inline int128 int128::operator--(int) {
+ int128 tmp(*this);
+ *this -= 1;
+ return tmp;
+}
+
+inline int128& int128::operator++() {
+ *this += 1;
+ return *this;
+}
+
+inline int128& int128::operator--() {
+ *this -= 1;
+ return *this;
+}
+
+inline int128 operator|(int128 lhs, int128 rhs) {
+ return MakeInt128(Int128High64(lhs) | Int128High64(rhs),
+ Int128Low64(lhs) | Int128Low64(rhs));
+}
+
+inline int128 operator&(int128 lhs, int128 rhs) {
+ return MakeInt128(Int128High64(lhs) & Int128High64(rhs),
+ Int128Low64(lhs) & Int128Low64(rhs));
+}
+
+inline int128 operator^(int128 lhs, int128 rhs) {
+ return MakeInt128(Int128High64(lhs) ^ Int128High64(rhs),
+ Int128Low64(lhs) ^ Int128Low64(rhs));
+}
+
+inline int128 operator<<(int128 lhs, int amount) {
+ // uint64_t shifts of >= 64 are undefined, so we need some special-casing.
+ if (amount < 64) {
+ if (amount != 0) {
+ return MakeInt128(
+ (Int128High64(lhs) << amount) |
+ static_cast<int64_t>(Int128Low64(lhs) >> (64 - amount)),
+ Int128Low64(lhs) << amount);
+ }
+ return lhs;
+ }
+ return MakeInt128(static_cast<int64_t>(Int128Low64(lhs) << (amount - 64)), 0);
+}
+
+inline int128 operator>>(int128 lhs, int amount) {
+ // uint64_t shifts of >= 64 are undefined, so we need some special-casing.
+ if (amount < 64) {
+ if (amount != 0) {
+ return MakeInt128(
+ Int128High64(lhs) >> amount,
+ (Int128Low64(lhs) >> amount) |
+ (static_cast<uint64_t>(Int128High64(lhs)) << (64 - amount)));
+ }
+ return lhs;
+ }
+ return MakeInt128(0,
+ static_cast<uint64_t>(Int128High64(lhs) >> (amount - 64)));
+}
diff --git a/absl/numeric/int128_stream_test.cc b/absl/numeric/int128_stream_test.cc
index 3cfa9dc1..479ad66c 100644
--- a/absl/numeric/int128_stream_test.cc
+++ b/absl/numeric/int128_stream_test.cc
@@ -147,6 +147,735 @@ TEST(Uint128, OStreamFormatTest) {
}
}
+struct Int128TestCase {
+ absl::int128 value;
+ std::ios_base::fmtflags flags;
+ std::streamsize width;
+ const char* expected;
+};
+
+void CheckInt128Case(const Int128TestCase& test_case) {
+ std::ostringstream os;
+ os.flags(test_case.flags);
+ os.width(test_case.width);
+ os.fill(kFill);
+ os << test_case.value;
+ SCOPED_TRACE(StreamFormatToString(test_case.flags, test_case.width));
+ EXPECT_EQ(test_case.expected, os.str());
+}
+
+TEST(Int128, OStreamValueTest) {
+ CheckInt128Case({1, kDec, /*width = */ 0, "1"});
+ CheckInt128Case({1, kOct, /*width = */ 0, "1"});
+ CheckInt128Case({1, kHex, /*width = */ 0, "1"});
+ CheckInt128Case({9, kDec, /*width = */ 0, "9"});
+ CheckInt128Case({9, kOct, /*width = */ 0, "11"});
+ CheckInt128Case({9, kHex, /*width = */ 0, "9"});
+ CheckInt128Case({12345, kDec, /*width = */ 0, "12345"});
+ CheckInt128Case({12345, kOct, /*width = */ 0, "30071"});
+ CheckInt128Case({12345, kHex, /*width = */ 0, "3039"});
+ CheckInt128Case(
+ {0x8000000000000000, kDec, /*width = */ 0, "9223372036854775808"});
+ CheckInt128Case(
+ {0x8000000000000000, kOct, /*width = */ 0, "1000000000000000000000"});
+ CheckInt128Case(
+ {0x8000000000000000, kHex, /*width = */ 0, "8000000000000000"});
+ CheckInt128Case({std::numeric_limits<uint64_t>::max(), kDec,
+ /*width = */ 0, "18446744073709551615"});
+ CheckInt128Case({std::numeric_limits<uint64_t>::max(), kOct,
+ /*width = */ 0, "1777777777777777777777"});
+ CheckInt128Case({std::numeric_limits<uint64_t>::max(), kHex,
+ /*width = */ 0, "ffffffffffffffff"});
+ CheckInt128Case(
+ {absl::MakeInt128(1, 0), kDec, /*width = */ 0, "18446744073709551616"});
+ CheckInt128Case(
+ {absl::MakeInt128(1, 0), kOct, /*width = */ 0, "2000000000000000000000"});
+ CheckInt128Case(
+ {absl::MakeInt128(1, 0), kHex, /*width = */ 0, "10000000000000000"});
+ CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<uint64_t>::max()),
+ std::ios::dec, /*width = */ 0,
+ "170141183460469231731687303715884105727"});
+ CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<uint64_t>::max()),
+ std::ios::oct, /*width = */ 0,
+ "1777777777777777777777777777777777777777777"});
+ CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<uint64_t>::max()),
+ std::ios::hex, /*width = */ 0,
+ "7fffffffffffffffffffffffffffffff"});
+ CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0),
+ std::ios::dec, /*width = */ 0,
+ "-170141183460469231731687303715884105728"});
+ CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0),
+ std::ios::oct, /*width = */ 0,
+ "2000000000000000000000000000000000000000000"});
+ CheckInt128Case({absl::MakeInt128(std::numeric_limits<int64_t>::min(), 0),
+ std::ios::hex, /*width = */ 0,
+ "80000000000000000000000000000000"});
+ CheckInt128Case({-1, std::ios::dec, /*width = */ 0, "-1"});
+ CheckInt128Case({-1, std::ios::oct, /*width = */ 0,
+ "3777777777777777777777777777777777777777777"});
+ CheckInt128Case(
+ {-1, std::ios::hex, /*width = */ 0, "ffffffffffffffffffffffffffffffff"});
+ CheckInt128Case({-12345, std::ios::dec, /*width = */ 0, "-12345"});
+ CheckInt128Case({-12345, std::ios::oct, /*width = */ 0,
+ "3777777777777777777777777777777777777747707"});
+ CheckInt128Case({-12345, std::ios::hex, /*width = */ 0,
+ "ffffffffffffffffffffffffffffcfc7"});
+}
+
+std::vector<Int128TestCase> GetInt128FormatCases();
+TEST(Int128, OStreamFormatTest) {
+ for (const Int128TestCase& test_case : GetInt128FormatCases()) {
+ CheckInt128Case(test_case);
+ }
+}
+
+std::vector<Int128TestCase> GetInt128FormatCases() {
+ return {
+ {0, std::ios_base::fmtflags(), /*width = */ 0, "0"},
+ {0, std::ios_base::fmtflags(), /*width = */ 6, "_____0"},
+ {0, kPos, /*width = */ 0, "+0"},
+ {0, kPos, /*width = */ 6, "____+0"},
+ {0, kBase, /*width = */ 0, "0"},
+ {0, kBase, /*width = */ 6, "_____0"},
+ {0, kBase | kPos, /*width = */ 0, "+0"},
+ {0, kBase | kPos, /*width = */ 6, "____+0"},
+ {0, kUpper, /*width = */ 0, "0"},
+ {0, kUpper, /*width = */ 6, "_____0"},
+ {0, kUpper | kPos, /*width = */ 0, "+0"},
+ {0, kUpper | kPos, /*width = */ 6, "____+0"},
+ {0, kUpper | kBase, /*width = */ 0, "0"},
+ {0, kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kUpper | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kUpper | kBase | kPos, /*width = */ 6, "____+0"},
+ {0, kLeft, /*width = */ 0, "0"},
+ {0, kLeft, /*width = */ 6, "0_____"},
+ {0, kLeft | kPos, /*width = */ 0, "+0"},
+ {0, kLeft | kPos, /*width = */ 6, "+0____"},
+ {0, kLeft | kBase, /*width = */ 0, "0"},
+ {0, kLeft | kBase, /*width = */ 6, "0_____"},
+ {0, kLeft | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kLeft | kBase | kPos, /*width = */ 6, "+0____"},
+ {0, kLeft | kUpper, /*width = */ 0, "0"},
+ {0, kLeft | kUpper, /*width = */ 6, "0_____"},
+ {0, kLeft | kUpper | kPos, /*width = */ 0, "+0"},
+ {0, kLeft | kUpper | kPos, /*width = */ 6, "+0____"},
+ {0, kLeft | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
+ {0, kLeft | kUpper | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kLeft | kUpper | kBase | kPos, /*width = */ 6, "+0____"},
+ {0, kInt, /*width = */ 0, "0"},
+ {0, kInt, /*width = */ 6, "_____0"},
+ {0, kInt | kPos, /*width = */ 0, "+0"},
+ {0, kInt | kPos, /*width = */ 6, "+____0"},
+ {0, kInt | kBase, /*width = */ 0, "0"},
+ {0, kInt | kBase, /*width = */ 6, "_____0"},
+ {0, kInt | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kInt | kBase | kPos, /*width = */ 6, "+____0"},
+ {0, kInt | kUpper, /*width = */ 0, "0"},
+ {0, kInt | kUpper, /*width = */ 6, "_____0"},
+ {0, kInt | kUpper | kPos, /*width = */ 0, "+0"},
+ {0, kInt | kUpper | kPos, /*width = */ 6, "+____0"},
+ {0, kInt | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kInt | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kInt | kUpper | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kInt | kUpper | kBase | kPos, /*width = */ 6, "+____0"},
+ {0, kRight, /*width = */ 0, "0"},
+ {0, kRight, /*width = */ 6, "_____0"},
+ {0, kRight | kPos, /*width = */ 0, "+0"},
+ {0, kRight | kPos, /*width = */ 6, "____+0"},
+ {0, kRight | kBase, /*width = */ 0, "0"},
+ {0, kRight | kBase, /*width = */ 6, "_____0"},
+ {0, kRight | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kRight | kBase | kPos, /*width = */ 6, "____+0"},
+ {0, kRight | kUpper, /*width = */ 0, "0"},
+ {0, kRight | kUpper, /*width = */ 6, "_____0"},
+ {0, kRight | kUpper | kPos, /*width = */ 0, "+0"},
+ {0, kRight | kUpper | kPos, /*width = */ 6, "____+0"},
+ {0, kRight | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kRight | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kRight | kUpper | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kRight | kUpper | kBase | kPos, /*width = */ 6, "____+0"},
+ {0, kDec, /*width = */ 0, "0"},
+ {0, kDec, /*width = */ 6, "_____0"},
+ {0, kDec | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kPos, /*width = */ 6, "____+0"},
+ {0, kDec | kBase, /*width = */ 0, "0"},
+ {0, kDec | kBase, /*width = */ 6, "_____0"},
+ {0, kDec | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kBase | kPos, /*width = */ 6, "____+0"},
+ {0, kDec | kUpper, /*width = */ 0, "0"},
+ {0, kDec | kUpper, /*width = */ 6, "_____0"},
+ {0, kDec | kUpper | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kUpper | kPos, /*width = */ 6, "____+0"},
+ {0, kDec | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kDec | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kDec | kUpper | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kUpper | kBase | kPos, /*width = */ 6, "____+0"},
+ {0, kDec | kLeft, /*width = */ 0, "0"},
+ {0, kDec | kLeft, /*width = */ 6, "0_____"},
+ {0, kDec | kLeft | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kLeft | kPos, /*width = */ 6, "+0____"},
+ {0, kDec | kLeft | kBase, /*width = */ 0, "0"},
+ {0, kDec | kLeft | kBase, /*width = */ 6, "0_____"},
+ {0, kDec | kLeft | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kLeft | kBase | kPos, /*width = */ 6, "+0____"},
+ {0, kDec | kLeft | kUpper, /*width = */ 0, "0"},
+ {0, kDec | kLeft | kUpper, /*width = */ 6, "0_____"},
+ {0, kDec | kLeft | kUpper | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kLeft | kUpper | kPos, /*width = */ 6, "+0____"},
+ {0, kDec | kLeft | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kDec | kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
+ {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "+0____"},
+ {0, kDec | kInt, /*width = */ 0, "0"},
+ {0, kDec | kInt, /*width = */ 6, "_____0"},
+ {0, kDec | kInt | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kInt | kPos, /*width = */ 6, "+____0"},
+ {0, kDec | kInt | kBase, /*width = */ 0, "0"},
+ {0, kDec | kInt | kBase, /*width = */ 6, "_____0"},
+ {0, kDec | kInt | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kInt | kBase | kPos, /*width = */ 6, "+____0"},
+ {0, kDec | kInt | kUpper, /*width = */ 0, "0"},
+ {0, kDec | kInt | kUpper, /*width = */ 6, "_____0"},
+ {0, kDec | kInt | kUpper | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kInt | kUpper | kPos, /*width = */ 6, "+____0"},
+ {0, kDec | kInt | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kDec | kInt | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "+____0"},
+ {0, kDec | kRight, /*width = */ 0, "0"},
+ {0, kDec | kRight, /*width = */ 6, "_____0"},
+ {0, kDec | kRight | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kRight | kPos, /*width = */ 6, "____+0"},
+ {0, kDec | kRight | kBase, /*width = */ 0, "0"},
+ {0, kDec | kRight | kBase, /*width = */ 6, "_____0"},
+ {0, kDec | kRight | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kRight | kBase | kPos, /*width = */ 6, "____+0"},
+ {0, kDec | kRight | kUpper, /*width = */ 0, "0"},
+ {0, kDec | kRight | kUpper, /*width = */ 6, "_____0"},
+ {0, kDec | kRight | kUpper | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kRight | kUpper | kPos, /*width = */ 6, "____+0"},
+ {0, kDec | kRight | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kDec | kRight | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "+0"},
+ {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "____+0"},
+ {0, kOct, /*width = */ 0, "0"},
+ {0, kOct, /*width = */ 6, "_____0"},
+ {0, kOct | kPos, /*width = */ 0, "0"},
+ {0, kOct | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kBase, /*width = */ 0, "0"},
+ {0, kOct | kBase, /*width = */ 6, "_____0"},
+ {0, kOct | kBase | kPos, /*width = */ 0, "0"},
+ {0, kOct | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kUpper, /*width = */ 0, "0"},
+ {0, kOct | kUpper, /*width = */ 6, "_____0"},
+ {0, kOct | kUpper | kPos, /*width = */ 0, "0"},
+ {0, kOct | kUpper | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kOct | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kOct | kUpper | kBase | kPos, /*width = */ 0, "0"},
+ {0, kOct | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kLeft, /*width = */ 0, "0"},
+ {0, kOct | kLeft, /*width = */ 6, "0_____"},
+ {0, kOct | kLeft | kPos, /*width = */ 0, "0"},
+ {0, kOct | kLeft | kPos, /*width = */ 6, "0_____"},
+ {0, kOct | kLeft | kBase, /*width = */ 0, "0"},
+ {0, kOct | kLeft | kBase, /*width = */ 6, "0_____"},
+ {0, kOct | kLeft | kBase | kPos, /*width = */ 0, "0"},
+ {0, kOct | kLeft | kBase | kPos, /*width = */ 6, "0_____"},
+ {0, kOct | kLeft | kUpper, /*width = */ 0, "0"},
+ {0, kOct | kLeft | kUpper, /*width = */ 6, "0_____"},
+ {0, kOct | kLeft | kUpper | kPos, /*width = */ 0, "0"},
+ {0, kOct | kLeft | kUpper | kPos, /*width = */ 6, "0_____"},
+ {0, kOct | kLeft | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kOct | kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
+ {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"},
+ {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"},
+ {0, kOct | kInt, /*width = */ 0, "0"},
+ {0, kOct | kInt, /*width = */ 6, "_____0"},
+ {0, kOct | kInt | kPos, /*width = */ 0, "0"},
+ {0, kOct | kInt | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kInt | kBase, /*width = */ 0, "0"},
+ {0, kOct | kInt | kBase, /*width = */ 6, "_____0"},
+ {0, kOct | kInt | kBase | kPos, /*width = */ 0, "0"},
+ {0, kOct | kInt | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kInt | kUpper, /*width = */ 0, "0"},
+ {0, kOct | kInt | kUpper, /*width = */ 6, "_____0"},
+ {0, kOct | kInt | kUpper | kPos, /*width = */ 0, "0"},
+ {0, kOct | kInt | kUpper | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kInt | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kOct | kInt | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"},
+ {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kRight, /*width = */ 0, "0"},
+ {0, kOct | kRight, /*width = */ 6, "_____0"},
+ {0, kOct | kRight | kPos, /*width = */ 0, "0"},
+ {0, kOct | kRight | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kRight | kBase, /*width = */ 0, "0"},
+ {0, kOct | kRight | kBase, /*width = */ 6, "_____0"},
+ {0, kOct | kRight | kBase | kPos, /*width = */ 0, "0"},
+ {0, kOct | kRight | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kRight | kUpper, /*width = */ 0, "0"},
+ {0, kOct | kRight | kUpper, /*width = */ 6, "_____0"},
+ {0, kOct | kRight | kUpper | kPos, /*width = */ 0, "0"},
+ {0, kOct | kRight | kUpper | kPos, /*width = */ 6, "_____0"},
+ {0, kOct | kRight | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kOct | kRight | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"},
+ {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kHex, /*width = */ 0, "0"},
+ {0, kHex, /*width = */ 6, "_____0"},
+ {0, kHex | kPos, /*width = */ 0, "0"},
+ {0, kHex | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kBase, /*width = */ 0, "0"},
+ {0, kHex | kBase, /*width = */ 6, "_____0"},
+ {0, kHex | kBase | kPos, /*width = */ 0, "0"},
+ {0, kHex | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kUpper, /*width = */ 0, "0"},
+ {0, kHex | kUpper, /*width = */ 6, "_____0"},
+ {0, kHex | kUpper | kPos, /*width = */ 0, "0"},
+ {0, kHex | kUpper | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kHex | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kHex | kUpper | kBase | kPos, /*width = */ 0, "0"},
+ {0, kHex | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kLeft, /*width = */ 0, "0"},
+ {0, kHex | kLeft, /*width = */ 6, "0_____"},
+ {0, kHex | kLeft | kPos, /*width = */ 0, "0"},
+ {0, kHex | kLeft | kPos, /*width = */ 6, "0_____"},
+ {0, kHex | kLeft | kBase, /*width = */ 0, "0"},
+ {0, kHex | kLeft | kBase, /*width = */ 6, "0_____"},
+ {0, kHex | kLeft | kBase | kPos, /*width = */ 0, "0"},
+ {0, kHex | kLeft | kBase | kPos, /*width = */ 6, "0_____"},
+ {0, kHex | kLeft | kUpper, /*width = */ 0, "0"},
+ {0, kHex | kLeft | kUpper, /*width = */ 6, "0_____"},
+ {0, kHex | kLeft | kUpper | kPos, /*width = */ 0, "0"},
+ {0, kHex | kLeft | kUpper | kPos, /*width = */ 6, "0_____"},
+ {0, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0_____"},
+ {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"},
+ {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"},
+ {0, kHex | kInt, /*width = */ 0, "0"},
+ {0, kHex | kInt, /*width = */ 6, "_____0"},
+ {0, kHex | kInt | kPos, /*width = */ 0, "0"},
+ {0, kHex | kInt | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kInt | kBase, /*width = */ 0, "0"},
+ {0, kHex | kInt | kBase, /*width = */ 6, "_____0"},
+ {0, kHex | kInt | kBase | kPos, /*width = */ 0, "0"},
+ {0, kHex | kInt | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kInt | kUpper, /*width = */ 0, "0"},
+ {0, kHex | kInt | kUpper, /*width = */ 6, "_____0"},
+ {0, kHex | kInt | kUpper | kPos, /*width = */ 0, "0"},
+ {0, kHex | kInt | kUpper | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kInt | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kHex | kInt | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"},
+ {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kRight, /*width = */ 0, "0"},
+ {0, kHex | kRight, /*width = */ 6, "_____0"},
+ {0, kHex | kRight | kPos, /*width = */ 0, "0"},
+ {0, kHex | kRight | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kRight | kBase, /*width = */ 0, "0"},
+ {0, kHex | kRight | kBase, /*width = */ 6, "_____0"},
+ {0, kHex | kRight | kBase | kPos, /*width = */ 0, "0"},
+ {0, kHex | kRight | kBase | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kRight | kUpper, /*width = */ 0, "0"},
+ {0, kHex | kRight | kUpper, /*width = */ 6, "_____0"},
+ {0, kHex | kRight | kUpper | kPos, /*width = */ 0, "0"},
+ {0, kHex | kRight | kUpper | kPos, /*width = */ 6, "_____0"},
+ {0, kHex | kRight | kUpper | kBase, /*width = */ 0, "0"},
+ {0, kHex | kRight | kUpper | kBase, /*width = */ 6, "_____0"},
+ {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"},
+ {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"},
+ {42, std::ios_base::fmtflags(), /*width = */ 0, "42"},
+ {42, std::ios_base::fmtflags(), /*width = */ 6, "____42"},
+ {42, kPos, /*width = */ 0, "+42"},
+ {42, kPos, /*width = */ 6, "___+42"},
+ {42, kBase, /*width = */ 0, "42"},
+ {42, kBase, /*width = */ 6, "____42"},
+ {42, kBase | kPos, /*width = */ 0, "+42"},
+ {42, kBase | kPos, /*width = */ 6, "___+42"},
+ {42, kUpper, /*width = */ 0, "42"},
+ {42, kUpper, /*width = */ 6, "____42"},
+ {42, kUpper | kPos, /*width = */ 0, "+42"},
+ {42, kUpper | kPos, /*width = */ 6, "___+42"},
+ {42, kUpper | kBase, /*width = */ 0, "42"},
+ {42, kUpper | kBase, /*width = */ 6, "____42"},
+ {42, kUpper | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kUpper | kBase | kPos, /*width = */ 6, "___+42"},
+ {42, kLeft, /*width = */ 0, "42"},
+ {42, kLeft, /*width = */ 6, "42____"},
+ {42, kLeft | kPos, /*width = */ 0, "+42"},
+ {42, kLeft | kPos, /*width = */ 6, "+42___"},
+ {42, kLeft | kBase, /*width = */ 0, "42"},
+ {42, kLeft | kBase, /*width = */ 6, "42____"},
+ {42, kLeft | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kLeft | kBase | kPos, /*width = */ 6, "+42___"},
+ {42, kLeft | kUpper, /*width = */ 0, "42"},
+ {42, kLeft | kUpper, /*width = */ 6, "42____"},
+ {42, kLeft | kUpper | kPos, /*width = */ 0, "+42"},
+ {42, kLeft | kUpper | kPos, /*width = */ 6, "+42___"},
+ {42, kLeft | kUpper | kBase, /*width = */ 0, "42"},
+ {42, kLeft | kUpper | kBase, /*width = */ 6, "42____"},
+ {42, kLeft | kUpper | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kLeft | kUpper | kBase | kPos, /*width = */ 6, "+42___"},
+ {42, kInt, /*width = */ 0, "42"},
+ {42, kInt, /*width = */ 6, "____42"},
+ {42, kInt | kPos, /*width = */ 0, "+42"},
+ {42, kInt | kPos, /*width = */ 6, "+___42"},
+ {42, kInt | kBase, /*width = */ 0, "42"},
+ {42, kInt | kBase, /*width = */ 6, "____42"},
+ {42, kInt | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kInt | kBase | kPos, /*width = */ 6, "+___42"},
+ {42, kInt | kUpper, /*width = */ 0, "42"},
+ {42, kInt | kUpper, /*width = */ 6, "____42"},
+ {42, kInt | kUpper | kPos, /*width = */ 0, "+42"},
+ {42, kInt | kUpper | kPos, /*width = */ 6, "+___42"},
+ {42, kInt | kUpper | kBase, /*width = */ 0, "42"},
+ {42, kInt | kUpper | kBase, /*width = */ 6, "____42"},
+ {42, kInt | kUpper | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kInt | kUpper | kBase | kPos, /*width = */ 6, "+___42"},
+ {42, kRight, /*width = */ 0, "42"},
+ {42, kRight, /*width = */ 6, "____42"},
+ {42, kRight | kPos, /*width = */ 0, "+42"},
+ {42, kRight | kPos, /*width = */ 6, "___+42"},
+ {42, kRight | kBase, /*width = */ 0, "42"},
+ {42, kRight | kBase, /*width = */ 6, "____42"},
+ {42, kRight | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kRight | kBase | kPos, /*width = */ 6, "___+42"},
+ {42, kRight | kUpper, /*width = */ 0, "42"},
+ {42, kRight | kUpper, /*width = */ 6, "____42"},
+ {42, kRight | kUpper | kPos, /*width = */ 0, "+42"},
+ {42, kRight | kUpper | kPos, /*width = */ 6, "___+42"},
+ {42, kRight | kUpper | kBase, /*width = */ 0, "42"},
+ {42, kRight | kUpper | kBase, /*width = */ 6, "____42"},
+ {42, kRight | kUpper | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kRight | kUpper | kBase | kPos, /*width = */ 6, "___+42"},
+ {42, kDec, /*width = */ 0, "42"},
+ {42, kDec, /*width = */ 6, "____42"},
+ {42, kDec | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kPos, /*width = */ 6, "___+42"},
+ {42, kDec | kBase, /*width = */ 0, "42"},
+ {42, kDec | kBase, /*width = */ 6, "____42"},
+ {42, kDec | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kBase | kPos, /*width = */ 6, "___+42"},
+ {42, kDec | kUpper, /*width = */ 0, "42"},
+ {42, kDec | kUpper, /*width = */ 6, "____42"},
+ {42, kDec | kUpper | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kUpper | kPos, /*width = */ 6, "___+42"},
+ {42, kDec | kUpper | kBase, /*width = */ 0, "42"},
+ {42, kDec | kUpper | kBase, /*width = */ 6, "____42"},
+ {42, kDec | kUpper | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kUpper | kBase | kPos, /*width = */ 6, "___+42"},
+ {42, kDec | kLeft, /*width = */ 0, "42"},
+ {42, kDec | kLeft, /*width = */ 6, "42____"},
+ {42, kDec | kLeft | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kLeft | kPos, /*width = */ 6, "+42___"},
+ {42, kDec | kLeft | kBase, /*width = */ 0, "42"},
+ {42, kDec | kLeft | kBase, /*width = */ 6, "42____"},
+ {42, kDec | kLeft | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kLeft | kBase | kPos, /*width = */ 6, "+42___"},
+ {42, kDec | kLeft | kUpper, /*width = */ 0, "42"},
+ {42, kDec | kLeft | kUpper, /*width = */ 6, "42____"},
+ {42, kDec | kLeft | kUpper | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kLeft | kUpper | kPos, /*width = */ 6, "+42___"},
+ {42, kDec | kLeft | kUpper | kBase, /*width = */ 0, "42"},
+ {42, kDec | kLeft | kUpper | kBase, /*width = */ 6, "42____"},
+ {42, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "+42___"},
+ {42, kDec | kInt, /*width = */ 0, "42"},
+ {42, kDec | kInt, /*width = */ 6, "____42"},
+ {42, kDec | kInt | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kInt | kPos, /*width = */ 6, "+___42"},
+ {42, kDec | kInt | kBase, /*width = */ 0, "42"},
+ {42, kDec | kInt | kBase, /*width = */ 6, "____42"},
+ {42, kDec | kInt | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kInt | kBase | kPos, /*width = */ 6, "+___42"},
+ {42, kDec | kInt | kUpper, /*width = */ 0, "42"},
+ {42, kDec | kInt | kUpper, /*width = */ 6, "____42"},
+ {42, kDec | kInt | kUpper | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kInt | kUpper | kPos, /*width = */ 6, "+___42"},
+ {42, kDec | kInt | kUpper | kBase, /*width = */ 0, "42"},
+ {42, kDec | kInt | kUpper | kBase, /*width = */ 6, "____42"},
+ {42, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "+___42"},
+ {42, kDec | kRight, /*width = */ 0, "42"},
+ {42, kDec | kRight, /*width = */ 6, "____42"},
+ {42, kDec | kRight | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kRight | kPos, /*width = */ 6, "___+42"},
+ {42, kDec | kRight | kBase, /*width = */ 0, "42"},
+ {42, kDec | kRight | kBase, /*width = */ 6, "____42"},
+ {42, kDec | kRight | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kRight | kBase | kPos, /*width = */ 6, "___+42"},
+ {42, kDec | kRight | kUpper, /*width = */ 0, "42"},
+ {42, kDec | kRight | kUpper, /*width = */ 6, "____42"},
+ {42, kDec | kRight | kUpper | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kRight | kUpper | kPos, /*width = */ 6, "___+42"},
+ {42, kDec | kRight | kUpper | kBase, /*width = */ 0, "42"},
+ {42, kDec | kRight | kUpper | kBase, /*width = */ 6, "____42"},
+ {42, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "+42"},
+ {42, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "___+42"},
+ {42, kOct, /*width = */ 0, "52"},
+ {42, kOct, /*width = */ 6, "____52"},
+ {42, kOct | kPos, /*width = */ 0, "52"},
+ {42, kOct | kPos, /*width = */ 6, "____52"},
+ {42, kOct | kBase, /*width = */ 0, "052"},
+ {42, kOct | kBase, /*width = */ 6, "___052"},
+ {42, kOct | kBase | kPos, /*width = */ 0, "052"},
+ {42, kOct | kBase | kPos, /*width = */ 6, "___052"},
+ {42, kOct | kUpper, /*width = */ 0, "52"},
+ {42, kOct | kUpper, /*width = */ 6, "____52"},
+ {42, kOct | kUpper | kPos, /*width = */ 0, "52"},
+ {42, kOct | kUpper | kPos, /*width = */ 6, "____52"},
+ {42, kOct | kUpper | kBase, /*width = */ 0, "052"},
+ {42, kOct | kUpper | kBase, /*width = */ 6, "___052"},
+ {42, kOct | kUpper | kBase | kPos, /*width = */ 0, "052"},
+ {42, kOct | kUpper | kBase | kPos, /*width = */ 6, "___052"},
+ {42, kOct | kLeft, /*width = */ 0, "52"},
+ {42, kOct | kLeft, /*width = */ 6, "52____"},
+ {42, kOct | kLeft | kPos, /*width = */ 0, "52"},
+ {42, kOct | kLeft | kPos, /*width = */ 6, "52____"},
+ {42, kOct | kLeft | kBase, /*width = */ 0, "052"},
+ {42, kOct | kLeft | kBase, /*width = */ 6, "052___"},
+ {42, kOct | kLeft | kBase | kPos, /*width = */ 0, "052"},
+ {42, kOct | kLeft | kBase | kPos, /*width = */ 6, "052___"},
+ {42, kOct | kLeft | kUpper, /*width = */ 0, "52"},
+ {42, kOct | kLeft | kUpper, /*width = */ 6, "52____"},
+ {42, kOct | kLeft | kUpper | kPos, /*width = */ 0, "52"},
+ {42, kOct | kLeft | kUpper | kPos, /*width = */ 6, "52____"},
+ {42, kOct | kLeft | kUpper | kBase, /*width = */ 0, "052"},
+ {42, kOct | kLeft | kUpper | kBase, /*width = */ 6, "052___"},
+ {42, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "052"},
+ {42, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "052___"},
+ {42, kOct | kInt, /*width = */ 0, "52"},
+ {42, kOct | kInt, /*width = */ 6, "____52"},
+ {42, kOct | kInt | kPos, /*width = */ 0, "52"},
+ {42, kOct | kInt | kPos, /*width = */ 6, "____52"},
+ {42, kOct | kInt | kBase, /*width = */ 0, "052"},
+ {42, kOct | kInt | kBase, /*width = */ 6, "___052"},
+ {42, kOct | kInt | kBase | kPos, /*width = */ 0, "052"},
+ {42, kOct | kInt | kBase | kPos, /*width = */ 6, "___052"},
+ {42, kOct | kInt | kUpper, /*width = */ 0, "52"},
+ {42, kOct | kInt | kUpper, /*width = */ 6, "____52"},
+ {42, kOct | kInt | kUpper | kPos, /*width = */ 0, "52"},
+ {42, kOct | kInt | kUpper | kPos, /*width = */ 6, "____52"},
+ {42, kOct | kInt | kUpper | kBase, /*width = */ 0, "052"},
+ {42, kOct | kInt | kUpper | kBase, /*width = */ 6, "___052"},
+ {42, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "052"},
+ {42, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "___052"},
+ {42, kOct | kRight, /*width = */ 0, "52"},
+ {42, kOct | kRight, /*width = */ 6, "____52"},
+ {42, kOct | kRight | kPos, /*width = */ 0, "52"},
+ {42, kOct | kRight | kPos, /*width = */ 6, "____52"},
+ {42, kOct | kRight | kBase, /*width = */ 0, "052"},
+ {42, kOct | kRight | kBase, /*width = */ 6, "___052"},
+ {42, kOct | kRight | kBase | kPos, /*width = */ 0, "052"},
+ {42, kOct | kRight | kBase | kPos, /*width = */ 6, "___052"},
+ {42, kOct | kRight | kUpper, /*width = */ 0, "52"},
+ {42, kOct | kRight | kUpper, /*width = */ 6, "____52"},
+ {42, kOct | kRight | kUpper | kPos, /*width = */ 0, "52"},
+ {42, kOct | kRight | kUpper | kPos, /*width = */ 6, "____52"},
+ {42, kOct | kRight | kUpper | kBase, /*width = */ 0, "052"},
+ {42, kOct | kRight | kUpper | kBase, /*width = */ 6, "___052"},
+ {42, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "052"},
+ {42, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "___052"},
+ {42, kHex, /*width = */ 0, "2a"},
+ {42, kHex, /*width = */ 6, "____2a"},
+ {42, kHex | kPos, /*width = */ 0, "2a"},
+ {42, kHex | kPos, /*width = */ 6, "____2a"},
+ {42, kHex | kBase, /*width = */ 0, "0x2a"},
+ {42, kHex | kBase, /*width = */ 6, "__0x2a"},
+ {42, kHex | kBase | kPos, /*width = */ 0, "0x2a"},
+ {42, kHex | kBase | kPos, /*width = */ 6, "__0x2a"},
+ {42, kHex | kUpper, /*width = */ 0, "2A"},
+ {42, kHex | kUpper, /*width = */ 6, "____2A"},
+ {42, kHex | kUpper | kPos, /*width = */ 0, "2A"},
+ {42, kHex | kUpper | kPos, /*width = */ 6, "____2A"},
+ {42, kHex | kUpper | kBase, /*width = */ 0, "0X2A"},
+ {42, kHex | kUpper | kBase, /*width = */ 6, "__0X2A"},
+ {42, kHex | kUpper | kBase | kPos, /*width = */ 0, "0X2A"},
+ {42, kHex | kUpper | kBase | kPos, /*width = */ 6, "__0X2A"},
+ {42, kHex | kLeft, /*width = */ 0, "2a"},
+ {42, kHex | kLeft, /*width = */ 6, "2a____"},
+ {42, kHex | kLeft | kPos, /*width = */ 0, "2a"},
+ {42, kHex | kLeft | kPos, /*width = */ 6, "2a____"},
+ {42, kHex | kLeft | kBase, /*width = */ 0, "0x2a"},
+ {42, kHex | kLeft | kBase, /*width = */ 6, "0x2a__"},
+ {42, kHex | kLeft | kBase | kPos, /*width = */ 0, "0x2a"},
+ {42, kHex | kLeft | kBase | kPos, /*width = */ 6, "0x2a__"},
+ {42, kHex | kLeft | kUpper, /*width = */ 0, "2A"},
+ {42, kHex | kLeft | kUpper, /*width = */ 6, "2A____"},
+ {42, kHex | kLeft | kUpper | kPos, /*width = */ 0, "2A"},
+ {42, kHex | kLeft | kUpper | kPos, /*width = */ 6, "2A____"},
+ {42, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0X2A"},
+ {42, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0X2A__"},
+ {42, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0X2A"},
+ {42, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0X2A__"},
+ {42, kHex | kInt, /*width = */ 0, "2a"},
+ {42, kHex | kInt, /*width = */ 6, "____2a"},
+ {42, kHex | kInt | kPos, /*width = */ 0, "2a"},
+ {42, kHex | kInt | kPos, /*width = */ 6, "____2a"},
+ {42, kHex | kInt | kBase, /*width = */ 0, "0x2a"},
+ {42, kHex | kInt | kBase, /*width = */ 6, "0x__2a"},
+ {42, kHex | kInt | kBase | kPos, /*width = */ 0, "0x2a"},
+ {42, kHex | kInt | kBase | kPos, /*width = */ 6, "0x__2a"},
+ {42, kHex | kInt | kUpper, /*width = */ 0, "2A"},
+ {42, kHex | kInt | kUpper, /*width = */ 6, "____2A"},
+ {42, kHex | kInt | kUpper | kPos, /*width = */ 0, "2A"},
+ {42, kHex | kInt | kUpper | kPos, /*width = */ 6, "____2A"},
+ {42, kHex | kInt | kUpper | kBase, /*width = */ 0, "0X2A"},
+ {42, kHex | kInt | kUpper | kBase, /*width = */ 6, "0X__2A"},
+ {42, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0X2A"},
+ {42, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "0X__2A"},
+ {42, kHex | kRight, /*width = */ 0, "2a"},
+ {42, kHex | kRight, /*width = */ 6, "____2a"},
+ {42, kHex | kRight | kPos, /*width = */ 0, "2a"},
+ {42, kHex | kRight | kPos, /*width = */ 6, "____2a"},
+ {42, kHex | kRight | kBase, /*width = */ 0, "0x2a"},
+ {42, kHex | kRight | kBase, /*width = */ 6, "__0x2a"},
+ {42, kHex | kRight | kBase | kPos, /*width = */ 0, "0x2a"},
+ {42, kHex | kRight | kBase | kPos, /*width = */ 6, "__0x2a"},
+ {42, kHex | kRight | kUpper, /*width = */ 0, "2A"},
+ {42, kHex | kRight | kUpper, /*width = */ 6, "____2A"},
+ {42, kHex | kRight | kUpper | kPos, /*width = */ 0, "2A"},
+ {42, kHex | kRight | kUpper | kPos, /*width = */ 6, "____2A"},
+ {42, kHex | kRight | kUpper | kBase, /*width = */ 0, "0X2A"},
+ {42, kHex | kRight | kUpper | kBase, /*width = */ 6, "__0X2A"},
+ {42, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0X2A"},
+ {42, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "__0X2A"},
+ {-321, std::ios_base::fmtflags(), /*width = */ 0, "-321"},
+ {-321, std::ios_base::fmtflags(), /*width = */ 6, "__-321"},
+ {-321, kPos, /*width = */ 0, "-321"},
+ {-321, kPos, /*width = */ 6, "__-321"},
+ {-321, kBase, /*width = */ 0, "-321"},
+ {-321, kBase, /*width = */ 6, "__-321"},
+ {-321, kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kBase | kPos, /*width = */ 6, "__-321"},
+ {-321, kUpper, /*width = */ 0, "-321"},
+ {-321, kUpper, /*width = */ 6, "__-321"},
+ {-321, kUpper | kPos, /*width = */ 0, "-321"},
+ {-321, kUpper | kPos, /*width = */ 6, "__-321"},
+ {-321, kUpper | kBase, /*width = */ 0, "-321"},
+ {-321, kUpper | kBase, /*width = */ 6, "__-321"},
+ {-321, kUpper | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kUpper | kBase | kPos, /*width = */ 6, "__-321"},
+ {-321, kLeft, /*width = */ 0, "-321"},
+ {-321, kLeft, /*width = */ 6, "-321__"},
+ {-321, kLeft | kPos, /*width = */ 0, "-321"},
+ {-321, kLeft | kPos, /*width = */ 6, "-321__"},
+ {-321, kLeft | kBase, /*width = */ 0, "-321"},
+ {-321, kLeft | kBase, /*width = */ 6, "-321__"},
+ {-321, kLeft | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kLeft | kBase | kPos, /*width = */ 6, "-321__"},
+ {-321, kLeft | kUpper, /*width = */ 0, "-321"},
+ {-321, kLeft | kUpper, /*width = */ 6, "-321__"},
+ {-321, kLeft | kUpper | kPos, /*width = */ 0, "-321"},
+ {-321, kLeft | kUpper | kPos, /*width = */ 6, "-321__"},
+ {-321, kLeft | kUpper | kBase, /*width = */ 0, "-321"},
+ {-321, kLeft | kUpper | kBase, /*width = */ 6, "-321__"},
+ {-321, kLeft | kUpper | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kLeft | kUpper | kBase | kPos, /*width = */ 6, "-321__"},
+ {-321, kInt, /*width = */ 0, "-321"},
+ {-321, kInt, /*width = */ 6, "-__321"},
+ {-321, kInt | kPos, /*width = */ 0, "-321"},
+ {-321, kInt | kPos, /*width = */ 6, "-__321"},
+ {-321, kInt | kBase, /*width = */ 0, "-321"},
+ {-321, kInt | kBase, /*width = */ 6, "-__321"},
+ {-321, kInt | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kInt | kBase | kPos, /*width = */ 6, "-__321"},
+ {-321, kInt | kUpper, /*width = */ 0, "-321"},
+ {-321, kInt | kUpper, /*width = */ 6, "-__321"},
+ {-321, kInt | kUpper | kPos, /*width = */ 0, "-321"},
+ {-321, kInt | kUpper | kPos, /*width = */ 6, "-__321"},
+ {-321, kInt | kUpper | kBase, /*width = */ 0, "-321"},
+ {-321, kInt | kUpper | kBase, /*width = */ 6, "-__321"},
+ {-321, kInt | kUpper | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kInt | kUpper | kBase | kPos, /*width = */ 6, "-__321"},
+ {-321, kRight, /*width = */ 0, "-321"},
+ {-321, kRight, /*width = */ 6, "__-321"},
+ {-321, kRight | kPos, /*width = */ 0, "-321"},
+ {-321, kRight | kPos, /*width = */ 6, "__-321"},
+ {-321, kRight | kBase, /*width = */ 0, "-321"},
+ {-321, kRight | kBase, /*width = */ 6, "__-321"},
+ {-321, kRight | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kRight | kBase | kPos, /*width = */ 6, "__-321"},
+ {-321, kRight | kUpper, /*width = */ 0, "-321"},
+ {-321, kRight | kUpper, /*width = */ 6, "__-321"},
+ {-321, kRight | kUpper | kPos, /*width = */ 0, "-321"},
+ {-321, kRight | kUpper | kPos, /*width = */ 6, "__-321"},
+ {-321, kRight | kUpper | kBase, /*width = */ 0, "-321"},
+ {-321, kRight | kUpper | kBase, /*width = */ 6, "__-321"},
+ {-321, kRight | kUpper | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kRight | kUpper | kBase | kPos, /*width = */ 6, "__-321"},
+ {-321, kDec, /*width = */ 0, "-321"},
+ {-321, kDec, /*width = */ 6, "__-321"},
+ {-321, kDec | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kPos, /*width = */ 6, "__-321"},
+ {-321, kDec | kBase, /*width = */ 0, "-321"},
+ {-321, kDec | kBase, /*width = */ 6, "__-321"},
+ {-321, kDec | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kBase | kPos, /*width = */ 6, "__-321"},
+ {-321, kDec | kUpper, /*width = */ 0, "-321"},
+ {-321, kDec | kUpper, /*width = */ 6, "__-321"},
+ {-321, kDec | kUpper | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kUpper | kPos, /*width = */ 6, "__-321"},
+ {-321, kDec | kUpper | kBase, /*width = */ 0, "-321"},
+ {-321, kDec | kUpper | kBase, /*width = */ 6, "__-321"},
+ {-321, kDec | kUpper | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kUpper | kBase | kPos, /*width = */ 6, "__-321"},
+ {-321, kDec | kLeft, /*width = */ 0, "-321"},
+ {-321, kDec | kLeft, /*width = */ 6, "-321__"},
+ {-321, kDec | kLeft | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kLeft | kPos, /*width = */ 6, "-321__"},
+ {-321, kDec | kLeft | kBase, /*width = */ 0, "-321"},
+ {-321, kDec | kLeft | kBase, /*width = */ 6, "-321__"},
+ {-321, kDec | kLeft | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kLeft | kBase | kPos, /*width = */ 6, "-321__"},
+ {-321, kDec | kLeft | kUpper, /*width = */ 0, "-321"},
+ {-321, kDec | kLeft | kUpper, /*width = */ 6, "-321__"},
+ {-321, kDec | kLeft | kUpper | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kLeft | kUpper | kPos, /*width = */ 6, "-321__"},
+ {-321, kDec | kLeft | kUpper | kBase, /*width = */ 0, "-321"},
+ {-321, kDec | kLeft | kUpper | kBase, /*width = */ 6, "-321__"},
+ {-321, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "-321__"},
+ {-321, kDec | kInt, /*width = */ 0, "-321"},
+ {-321, kDec | kInt, /*width = */ 6, "-__321"},
+ {-321, kDec | kInt | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kInt | kPos, /*width = */ 6, "-__321"},
+ {-321, kDec | kInt | kBase, /*width = */ 0, "-321"},
+ {-321, kDec | kInt | kBase, /*width = */ 6, "-__321"},
+ {-321, kDec | kInt | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kInt | kBase | kPos, /*width = */ 6, "-__321"},
+ {-321, kDec | kInt | kUpper, /*width = */ 0, "-321"},
+ {-321, kDec | kInt | kUpper, /*width = */ 6, "-__321"},
+ {-321, kDec | kInt | kUpper | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kInt | kUpper | kPos, /*width = */ 6, "-__321"},
+ {-321, kDec | kInt | kUpper | kBase, /*width = */ 0, "-321"},
+ {-321, kDec | kInt | kUpper | kBase, /*width = */ 6, "-__321"},
+ {-321, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "-__321"},
+ {-321, kDec | kRight, /*width = */ 0, "-321"},
+ {-321, kDec | kRight, /*width = */ 6, "__-321"},
+ {-321, kDec | kRight | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kRight | kPos, /*width = */ 6, "__-321"},
+ {-321, kDec | kRight | kBase, /*width = */ 0, "-321"},
+ {-321, kDec | kRight | kBase, /*width = */ 6, "__-321"},
+ {-321, kDec | kRight | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kRight | kBase | kPos, /*width = */ 6, "__-321"},
+ {-321, kDec | kRight | kUpper, /*width = */ 0, "-321"},
+ {-321, kDec | kRight | kUpper, /*width = */ 6, "__-321"},
+ {-321, kDec | kRight | kUpper | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kRight | kUpper | kPos, /*width = */ 6, "__-321"},
+ {-321, kDec | kRight | kUpper | kBase, /*width = */ 0, "-321"},
+ {-321, kDec | kRight | kUpper | kBase, /*width = */ 6, "__-321"},
+ {-321, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "-321"},
+ {-321, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "__-321"}};
+}
+
std::vector<Uint128TestCase> GetUint128FormatCases() {
return {
{0, std::ios_base::fmtflags(), /*width = */ 0, "0"},
diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc
index 5e1b5ec3..bc86c714 100644
--- a/absl/numeric/int128_test.cc
+++ b/absl/numeric/int128_test.cc
@@ -479,4 +479,747 @@ TEST(Uint128, Hash) {
}));
}
+
+TEST(Int128Uint128, ConversionTest) {
+ absl::int128 nonnegative_signed_values[] = {
+ 0,
+ 1,
+ 0xffeeddccbbaa9988,
+ absl::MakeInt128(0x7766554433221100, 0),
+ absl::MakeInt128(0x1234567890abcdef, 0xfedcba0987654321),
+ absl::Int128Max()};
+ for (absl::int128 value : nonnegative_signed_values) {
+ EXPECT_EQ(value, absl::int128(absl::uint128(value)));
+
+ absl::uint128 assigned_value;
+ assigned_value = value;
+ EXPECT_EQ(value, absl::int128(assigned_value));
+ }
+
+ absl::int128 negative_values[] = {
+ -1, -0x1234567890abcdef,
+ absl::MakeInt128(-0x5544332211ffeedd, 0),
+ -absl::MakeInt128(0x76543210fedcba98, 0xabcdef0123456789)};
+ for (absl::int128 value : negative_values) {
+ EXPECT_EQ(absl::uint128(-value), -absl::uint128(value));
+
+ absl::uint128 assigned_value;
+ assigned_value = value;
+ EXPECT_EQ(absl::uint128(-value), -assigned_value);
+ }
+}
+
+template <typename T>
+class Int128IntegerTraitsTest : public ::testing::Test {};
+
+TYPED_TEST_SUITE(Int128IntegerTraitsTest, IntegerTypes);
+
+TYPED_TEST(Int128IntegerTraitsTest, ConstructAssignTest) {
+ static_assert(std::is_constructible<absl::int128, TypeParam>::value,
+ "absl::int128 must be constructible from TypeParam");
+ static_assert(std::is_assignable<absl::int128&, TypeParam>::value,
+ "absl::int128 must be assignable from TypeParam");
+ static_assert(!std::is_assignable<TypeParam&, absl::int128>::value,
+ "TypeParam must not be assignable from absl::int128");
+}
+
+template <typename T>
+class Int128FloatTraitsTest : public ::testing::Test {};
+
+TYPED_TEST_SUITE(Int128FloatTraitsTest, FloatingPointTypes);
+
+TYPED_TEST(Int128FloatTraitsTest, ConstructAssignTest) {
+ static_assert(std::is_constructible<absl::int128, TypeParam>::value,
+ "absl::int128 must be constructible from TypeParam");
+ static_assert(!std::is_assignable<absl::int128&, TypeParam>::value,
+ "absl::int128 must not be assignable from TypeParam");
+ static_assert(!std::is_assignable<TypeParam&, absl::int128>::value,
+ "TypeParam must not be assignable from absl::int128");
+}
+
+#ifdef ABSL_HAVE_INTRINSIC_INT128
+// These type traits done separately as TYPED_TEST requires typeinfo, and not
+// all platforms have this for __int128 even though they define the type.
+TEST(Int128, IntrinsicTypeTraitsTest) {
+ static_assert(std::is_constructible<absl::int128, __int128>::value,
+ "absl::int128 must be constructible from __int128");
+ static_assert(std::is_assignable<absl::int128&, __int128>::value,
+ "absl::int128 must be assignable from __int128");
+ static_assert(!std::is_assignable<__int128&, absl::int128>::value,
+ "__int128 must not be assignable from absl::int128");
+
+ static_assert(std::is_constructible<absl::int128, unsigned __int128>::value,
+ "absl::int128 must be constructible from unsigned __int128");
+ static_assert(!std::is_assignable<absl::int128&, unsigned __int128>::value,
+ "absl::int128 must be assignable from unsigned __int128");
+ static_assert(!std::is_assignable<unsigned __int128&, absl::int128>::value,
+ "unsigned __int128 must not be assignable from absl::int128");
+}
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+TEST(Int128, TrivialTraitsTest) {
+ static_assert(absl::is_trivially_default_constructible<absl::int128>::value,
+ "");
+ static_assert(absl::is_trivially_copy_constructible<absl::int128>::value, "");
+ static_assert(absl::is_trivially_copy_assignable<absl::int128>::value, "");
+ static_assert(std::is_trivially_destructible<absl::int128>::value, "");
+}
+
+TEST(Int128, BoolConversionTest) {
+ EXPECT_FALSE(absl::int128(0));
+ for (int i = 0; i < 64; ++i) {
+ EXPECT_TRUE(absl::MakeInt128(0, uint64_t{1} << i));
+ }
+ for (int i = 0; i < 63; ++i) {
+ EXPECT_TRUE(absl::MakeInt128(int64_t{1} << i, 0));
+ }
+ EXPECT_TRUE(absl::Int128Min());
+
+ EXPECT_EQ(absl::int128(1), absl::int128(true));
+ EXPECT_EQ(absl::int128(0), absl::int128(false));
+}
+
+template <typename T>
+class Int128IntegerConversionTest : public ::testing::Test {};
+
+TYPED_TEST_SUITE(Int128IntegerConversionTest, IntegerTypes);
+
+TYPED_TEST(Int128IntegerConversionTest, RoundTripTest) {
+ EXPECT_EQ(TypeParam{0}, static_cast<TypeParam>(absl::int128(0)));
+ EXPECT_EQ(std::numeric_limits<TypeParam>::min(),
+ static_cast<TypeParam>(
+ absl::int128(std::numeric_limits<TypeParam>::min())));
+ EXPECT_EQ(std::numeric_limits<TypeParam>::max(),
+ static_cast<TypeParam>(
+ absl::int128(std::numeric_limits<TypeParam>::max())));
+}
+
+template <typename T>
+class Int128FloatConversionTest : public ::testing::Test {};
+
+TYPED_TEST_SUITE(Int128FloatConversionTest, FloatingPointTypes);
+
+TYPED_TEST(Int128FloatConversionTest, ConstructAndCastTest) {
+ // Conversions where the floating point values should be exactly the same.
+ // 0x9f5b is a randomly chosen small value.
+ for (int i = 0; i < 110; ++i) { // 110 = 126 - #bits in 0x9f5b
+ SCOPED_TRACE(::testing::Message() << "i = " << i);
+
+ TypeParam float_value = std::ldexp(static_cast<TypeParam>(0x9f5b), i);
+ absl::int128 int_value = absl::int128(0x9f5b) << i;
+
+ EXPECT_EQ(float_value, static_cast<TypeParam>(int_value));
+ EXPECT_EQ(-float_value, static_cast<TypeParam>(-int_value));
+ EXPECT_EQ(int_value, absl::int128(float_value));
+ EXPECT_EQ(-int_value, absl::int128(-float_value));
+ }
+
+ // Round trip conversions with a small sample of randomly generated uint64_t
+ // values (less than int64_t max so that value * 2^64 fits into int128).
+ uint64_t values[] = {0x6d4492c24fb86199, 0x26ead65e4cb359b5,
+ 0x2c43407433ba3fd1, 0x3b574ec668df6b55,
+ 0x1c750e55a29f4f0f};
+ for (uint64_t value : values) {
+ for (int i = 0; i <= 64; ++i) {
+ SCOPED_TRACE(::testing::Message()
+ << "value = " << value << "; i = " << i);
+
+ TypeParam fvalue = std::ldexp(static_cast<TypeParam>(value), i);
+ EXPECT_DOUBLE_EQ(fvalue, static_cast<TypeParam>(absl::int128(fvalue)));
+ EXPECT_DOUBLE_EQ(-fvalue, static_cast<TypeParam>(-absl::int128(fvalue)));
+ EXPECT_DOUBLE_EQ(-fvalue, static_cast<TypeParam>(absl::int128(-fvalue)));
+ EXPECT_DOUBLE_EQ(fvalue, static_cast<TypeParam>(-absl::int128(-fvalue)));
+ }
+ }
+
+ // Round trip conversions with a small sample of random large positive values.
+ absl::int128 large_values[] = {
+ absl::MakeInt128(0x5b0640d96c7b3d9f, 0xb7a7189e51d18622),
+ absl::MakeInt128(0x34bed042c6f65270, 0x73b236570669a089),
+ absl::MakeInt128(0x43deba9e6da12724, 0xf7f0f83da686797d),
+ absl::MakeInt128(0x71e8d383be4e5589, 0x75c3f96fb00752b6)};
+ for (absl::int128 value : large_values) {
+ // Make value have as many significant bits as can be represented by
+ // the mantissa, also making sure the highest and lowest bit in the range
+ // are set.
+ value >>= (127 - std::numeric_limits<TypeParam>::digits);
+ value |= absl::int128(1) << (std::numeric_limits<TypeParam>::digits - 1);
+ value |= 1;
+ for (int i = 0; i < 127 - std::numeric_limits<TypeParam>::digits; ++i) {
+ absl::int128 int_value = value << i;
+ EXPECT_EQ(int_value,
+ static_cast<absl::int128>(static_cast<TypeParam>(int_value)));
+ EXPECT_EQ(-int_value,
+ static_cast<absl::int128>(static_cast<TypeParam>(-int_value)));
+ }
+ }
+
+ // Small sample of checks that rounding is toward zero
+ EXPECT_EQ(0, absl::int128(TypeParam(0.1)));
+ EXPECT_EQ(17, absl::int128(TypeParam(17.8)));
+ EXPECT_EQ(0, absl::int128(TypeParam(-0.8)));
+ EXPECT_EQ(-53, absl::int128(TypeParam(-53.1)));
+ EXPECT_EQ(0, absl::int128(TypeParam(0.5)));
+ EXPECT_EQ(0, absl::int128(TypeParam(-0.5)));
+ TypeParam just_lt_one = std::nexttoward(TypeParam(1), TypeParam(0));
+ EXPECT_EQ(0, absl::int128(just_lt_one));
+ TypeParam just_gt_minus_one = std::nexttoward(TypeParam(-1), TypeParam(0));
+ EXPECT_EQ(0, absl::int128(just_gt_minus_one));
+
+ // Check limits
+ EXPECT_DOUBLE_EQ(std::ldexp(static_cast<TypeParam>(1), 127),
+ static_cast<TypeParam>(absl::Int128Max()));
+ EXPECT_DOUBLE_EQ(-std::ldexp(static_cast<TypeParam>(1), 127),
+ static_cast<TypeParam>(absl::Int128Min()));
+}
+
+TEST(Int128, FactoryTest) {
+ EXPECT_EQ(absl::int128(-1), absl::MakeInt128(-1, -1));
+ EXPECT_EQ(absl::int128(-31), absl::MakeInt128(-1, -31));
+ EXPECT_EQ(absl::int128(std::numeric_limits<int64_t>::min()),
+ absl::MakeInt128(-1, std::numeric_limits<int64_t>::min()));
+ EXPECT_EQ(absl::int128(0), absl::MakeInt128(0, 0));
+ EXPECT_EQ(absl::int128(1), absl::MakeInt128(0, 1));
+ EXPECT_EQ(absl::int128(std::numeric_limits<int64_t>::max()),
+ absl::MakeInt128(0, std::numeric_limits<int64_t>::max()));
+}
+
+TEST(Int128, HighLowTest) {
+ struct HighLowPair {
+ int64_t high;
+ uint64_t low;
+ };
+ HighLowPair values[]{{0, 0}, {0, 1}, {1, 0}, {123, 456}, {-654, 321}};
+ for (const HighLowPair& pair : values) {
+ absl::int128 value = absl::MakeInt128(pair.high, pair.low);
+ EXPECT_EQ(pair.low, absl::Int128Low64(value));
+ EXPECT_EQ(pair.high, absl::Int128High64(value));
+ }
+}
+
+TEST(Int128, LimitsTest) {
+ EXPECT_EQ(absl::MakeInt128(0x7fffffffffffffff, 0xffffffffffffffff),
+ absl::Int128Max());
+ EXPECT_EQ(absl::Int128Max(), ~absl::Int128Min());
+}
+
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+TEST(Int128, IntrinsicConversionTest) {
+ __int128 intrinsic =
+ (static_cast<__int128>(0x3a5b76c209de76f6) << 64) + 0x1f25e1d63a2b46c5;
+ absl::int128 custom =
+ absl::MakeInt128(0x3a5b76c209de76f6, 0x1f25e1d63a2b46c5);
+
+ EXPECT_EQ(custom, absl::int128(intrinsic));
+ EXPECT_EQ(intrinsic, static_cast<__int128>(custom));
+}
+#endif // ABSL_HAVE_INTRINSIC_INT128
+
+TEST(Int128, ConstexprTest) {
+ constexpr absl::int128 zero = absl::int128();
+ constexpr absl::int128 one = 1;
+ constexpr absl::int128 minus_two = -2;
+ constexpr absl::int128 min = absl::Int128Min();
+ constexpr absl::int128 max = absl::Int128Max();
+ EXPECT_EQ(zero, absl::int128(0));
+ EXPECT_EQ(one, absl::int128(1));
+ EXPECT_EQ(minus_two, absl::MakeInt128(-1, -2));
+ EXPECT_GT(max, one);
+ EXPECT_LT(min, minus_two);
+}
+
+TEST(Int128, ComparisonTest) {
+ struct TestCase {
+ absl::int128 smaller;
+ absl::int128 larger;
+ };
+ TestCase cases[] = {
+ {absl::int128(0), absl::int128(123)},
+ {absl::MakeInt128(-12, 34), absl::MakeInt128(12, 34)},
+ {absl::MakeInt128(1, 1000), absl::MakeInt128(1000, 1)},
+ {absl::MakeInt128(-1000, 1000), absl::MakeInt128(-1, 1)},
+ };
+ for (const TestCase& pair : cases) {
+ SCOPED_TRACE(::testing::Message() << "pair.smaller = " << pair.smaller
+ << "; pair.larger = " << pair.larger);
+
+ EXPECT_TRUE(pair.smaller == pair.smaller); // NOLINT(readability/check)
+ EXPECT_TRUE(pair.larger == pair.larger); // NOLINT(readability/check)
+ EXPECT_FALSE(pair.smaller == pair.larger); // NOLINT(readability/check)
+
+ EXPECT_TRUE(pair.smaller != pair.larger); // NOLINT(readability/check)
+ EXPECT_FALSE(pair.smaller != pair.smaller); // NOLINT(readability/check)
+ EXPECT_FALSE(pair.larger != pair.larger); // NOLINT(readability/check)
+
+ EXPECT_TRUE(pair.smaller < pair.larger); // NOLINT(readability/check)
+ EXPECT_FALSE(pair.larger < pair.smaller); // NOLINT(readability/check)
+
+ EXPECT_TRUE(pair.larger > pair.smaller); // NOLINT(readability/check)
+ EXPECT_FALSE(pair.smaller > pair.larger); // NOLINT(readability/check)
+
+ EXPECT_TRUE(pair.smaller <= pair.larger); // NOLINT(readability/check)
+ EXPECT_FALSE(pair.larger <= pair.smaller); // NOLINT(readability/check)
+ EXPECT_TRUE(pair.smaller <= pair.smaller); // NOLINT(readability/check)
+ EXPECT_TRUE(pair.larger <= pair.larger); // NOLINT(readability/check)
+
+ EXPECT_TRUE(pair.larger >= pair.smaller); // NOLINT(readability/check)
+ EXPECT_FALSE(pair.smaller >= pair.larger); // NOLINT(readability/check)
+ EXPECT_TRUE(pair.smaller >= pair.smaller); // NOLINT(readability/check)
+ EXPECT_TRUE(pair.larger >= pair.larger); // NOLINT(readability/check)
+ }
+}
+
+TEST(Int128, UnaryNegationTest) {
+ int64_t values64[] = {0, 1, 12345, 0x4000000000000000,
+ std::numeric_limits<int64_t>::max()};
+ for (int64_t value : values64) {
+ SCOPED_TRACE(::testing::Message() << "value = " << value);
+
+ EXPECT_EQ(absl::int128(-value), -absl::int128(value));
+ EXPECT_EQ(absl::int128(value), -absl::int128(-value));
+ EXPECT_EQ(absl::MakeInt128(-value, 0), -absl::MakeInt128(value, 0));
+ EXPECT_EQ(absl::MakeInt128(value, 0), -absl::MakeInt128(-value, 0));
+ }
+}
+
+TEST(Int128, LogicalNotTest) {
+ EXPECT_TRUE(!absl::int128(0));
+ for (int i = 0; i < 64; ++i) {
+ EXPECT_FALSE(!absl::MakeInt128(0, uint64_t{1} << i));
+ }
+ for (int i = 0; i < 63; ++i) {
+ EXPECT_FALSE(!absl::MakeInt128(int64_t{1} << i, 0));
+ }
+}
+
+TEST(Int128, AdditionSubtractionTest) {
+ // 64 bit pairs that will not cause overflow / underflow. These test negative
+ // carry; positive carry must be checked separately.
+ std::pair<int64_t, int64_t> cases[]{
+ {0, 0}, // 0, 0
+ {0, 2945781290834}, // 0, +
+ {1908357619234, 0}, // +, 0
+ {0, -1204895918245}, // 0, -
+ {-2957928523560, 0}, // -, 0
+ {89023982312461, 98346012567134}, // +, +
+ {-63454234568239, -23456235230773}, // -, -
+ {98263457263502, -21428561935925}, // +, -
+ {-88235237438467, 15923659234573}, // -, +
+ };
+ for (const auto& pair : cases) {
+ SCOPED_TRACE(::testing::Message()
+ << "pair = {" << pair.first << ", " << pair.second << '}');
+
+ EXPECT_EQ(absl::int128(pair.first + pair.second),
+ absl::int128(pair.first) + absl::int128(pair.second));
+ EXPECT_EQ(absl::int128(pair.second + pair.first),
+ absl::int128(pair.second) += absl::int128(pair.first));
+
+ EXPECT_EQ(absl::int128(pair.first - pair.second),
+ absl::int128(pair.first) - absl::int128(pair.second));
+ EXPECT_EQ(absl::int128(pair.second - pair.first),
+ absl::int128(pair.second) -= absl::int128(pair.first));
+
+ EXPECT_EQ(
+ absl::MakeInt128(pair.second + pair.first, 0),
+ absl::MakeInt128(pair.second, 0) + absl::MakeInt128(pair.first, 0));
+ EXPECT_EQ(
+ absl::MakeInt128(pair.first + pair.second, 0),
+ absl::MakeInt128(pair.first, 0) += absl::MakeInt128(pair.second, 0));
+
+ EXPECT_EQ(
+ absl::MakeInt128(pair.second - pair.first, 0),
+ absl::MakeInt128(pair.second, 0) - absl::MakeInt128(pair.first, 0));
+ EXPECT_EQ(
+ absl::MakeInt128(pair.first - pair.second, 0),
+ absl::MakeInt128(pair.first, 0) -= absl::MakeInt128(pair.second, 0));
+ }
+
+ // check positive carry
+ EXPECT_EQ(absl::MakeInt128(31, 0),
+ absl::MakeInt128(20, 1) +
+ absl::MakeInt128(10, std::numeric_limits<uint64_t>::max()));
+}
+
+TEST(Int128, IncrementDecrementTest) {
+ absl::int128 value = 0;
+ EXPECT_EQ(0, value++);
+ EXPECT_EQ(1, value);
+ EXPECT_EQ(1, value--);
+ EXPECT_EQ(0, value);
+ EXPECT_EQ(-1, --value);
+ EXPECT_EQ(-1, value);
+ EXPECT_EQ(0, ++value);
+ EXPECT_EQ(0, value);
+}
+
+TEST(Int128, MultiplicationTest) {
+ // 1 bit x 1 bit, and negative combinations
+ for (int i = 0; i < 64; ++i) {
+ for (int j = 0; j < 127 - i; ++j) {
+ SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
+ absl::int128 a = absl::int128(1) << i;
+ absl::int128 b = absl::int128(1) << j;
+ absl::int128 c = absl::int128(1) << (i + j);
+
+ EXPECT_EQ(c, a * b);
+ EXPECT_EQ(-c, -a * b);
+ EXPECT_EQ(-c, a * -b);
+ EXPECT_EQ(c, -a * -b);
+
+ EXPECT_EQ(c, absl::int128(a) *= b);
+ EXPECT_EQ(-c, absl::int128(-a) *= b);
+ EXPECT_EQ(-c, absl::int128(a) *= -b);
+ EXPECT_EQ(c, absl::int128(-a) *= -b);
+ }
+ }
+
+ // Pairs of random values that will not overflow signed 64-bit multiplication
+ std::pair<int64_t, int64_t> small_values[] = {
+ {0x5e61, 0xf29f79ca14b4}, // +, +
+ {0x3e033b, -0x612c0ee549}, // +, -
+ {-0x052ce7e8, 0x7c728f0f}, // -, +
+ {-0x3af7054626, -0xfb1e1d}, // -, -
+ };
+ for (const std::pair<int64_t, int64_t>& pair : small_values) {
+ SCOPED_TRACE(::testing::Message()
+ << "pair = {" << pair.first << ", " << pair.second << '}');
+
+ EXPECT_EQ(absl::int128(pair.first * pair.second),
+ absl::int128(pair.first) * absl::int128(pair.second));
+ EXPECT_EQ(absl::int128(pair.first * pair.second),
+ absl::int128(pair.first) *= absl::int128(pair.second));
+
+ EXPECT_EQ(absl::MakeInt128(pair.first * pair.second, 0),
+ absl::MakeInt128(pair.first, 0) * absl::int128(pair.second));
+ EXPECT_EQ(absl::MakeInt128(pair.first * pair.second, 0),
+ absl::MakeInt128(pair.first, 0) *= absl::int128(pair.second));
+ }
+
+ // Pairs of positive random values that will not overflow 64-bit
+ // multiplication and can be left shifted by 32 without overflow
+ std::pair<int64_t, int64_t> small_values2[] = {
+ {0x1bb0a110, 0x31487671},
+ {0x4792784e, 0x28add7d7},
+ {0x7b66553a, 0x11dff8ef},
+ };
+ for (const std::pair<int64_t, int64_t>& pair : small_values2) {
+ SCOPED_TRACE(::testing::Message()
+ << "pair = {" << pair.first << ", " << pair.second << '}');
+
+ absl::int128 a = absl::int128(pair.first << 32);
+ absl::int128 b = absl::int128(pair.second << 32);
+ absl::int128 c = absl::MakeInt128(pair.first * pair.second, 0);
+
+ EXPECT_EQ(c, a * b);
+ EXPECT_EQ(-c, -a * b);
+ EXPECT_EQ(-c, a * -b);
+ EXPECT_EQ(c, -a * -b);
+
+ EXPECT_EQ(c, absl::int128(a) *= b);
+ EXPECT_EQ(-c, absl::int128(-a) *= b);
+ EXPECT_EQ(-c, absl::int128(a) *= -b);
+ EXPECT_EQ(c, absl::int128(-a) *= -b);
+ }
+
+ // check 0, 1, and -1 behavior with large values
+ absl::int128 large_values[] = {
+ {absl::MakeInt128(0xd66f061af02d0408, 0x727d2846cb475b53)},
+ {absl::MakeInt128(0x27b8d5ed6104452d, 0x03f8a33b0ee1df4f)},
+ {-absl::MakeInt128(0x621b6626b9e8d042, 0x27311ac99df00938)},
+ {-absl::MakeInt128(0x34e0656f1e95fb60, 0x4281cfd731257a47)},
+ };
+ for (absl::int128 value : large_values) {
+ EXPECT_EQ(0, 0 * value);
+ EXPECT_EQ(0, value * 0);
+ EXPECT_EQ(0, absl::int128(0) *= value);
+ EXPECT_EQ(0, value *= 0);
+
+ EXPECT_EQ(value, 1 * value);
+ EXPECT_EQ(value, value * 1);
+ EXPECT_EQ(value, absl::int128(1) *= value);
+ EXPECT_EQ(value, value *= 1);
+
+ EXPECT_EQ(-value, -1 * value);
+ EXPECT_EQ(-value, value * -1);
+ EXPECT_EQ(-value, absl::int128(-1) *= value);
+ EXPECT_EQ(-value, value *= -1);
+ }
+
+ // Manually calculated random large value cases
+ EXPECT_EQ(absl::MakeInt128(0xcd0efd3442219bb, 0xde47c05bcd9df6e1),
+ absl::MakeInt128(0x7c6448, 0x3bc4285c47a9d253) * 0x1a6037537b);
+ EXPECT_EQ(-absl::MakeInt128(0x1f8f149850b1e5e6, 0x1e50d6b52d272c3e),
+ -absl::MakeInt128(0x23, 0x2e68a513ca1b8859) * 0xe5a434cd14866e);
+ EXPECT_EQ(-absl::MakeInt128(0x55cae732029d1fce, 0xca6474b6423263e4),
+ 0xa9b98a8ddf66bc * -absl::MakeInt128(0x81, 0x672e58231e2469d7));
+ EXPECT_EQ(absl::MakeInt128(0x19c8b7620b507dc4, 0xfec042b71a5f29a4),
+ -0x3e39341147 * -absl::MakeInt128(0x6a14b2, 0x5ed34cca42327b3c));
+
+ EXPECT_EQ(absl::MakeInt128(0xcd0efd3442219bb, 0xde47c05bcd9df6e1),
+ absl::MakeInt128(0x7c6448, 0x3bc4285c47a9d253) *= 0x1a6037537b);
+ EXPECT_EQ(-absl::MakeInt128(0x1f8f149850b1e5e6, 0x1e50d6b52d272c3e),
+ -absl::MakeInt128(0x23, 0x2e68a513ca1b8859) *= 0xe5a434cd14866e);
+ EXPECT_EQ(-absl::MakeInt128(0x55cae732029d1fce, 0xca6474b6423263e4),
+ absl::int128(0xa9b98a8ddf66bc) *=
+ -absl::MakeInt128(0x81, 0x672e58231e2469d7));
+ EXPECT_EQ(absl::MakeInt128(0x19c8b7620b507dc4, 0xfec042b71a5f29a4),
+ absl::int128(-0x3e39341147) *=
+ -absl::MakeInt128(0x6a14b2, 0x5ed34cca42327b3c));
+}
+
+TEST(Int128, DivisionAndModuloTest) {
+ // Check against 64 bit division and modulo operators with a sample of
+ // randomly generated pairs.
+ std::pair<int64_t, int64_t> small_pairs[] = {
+ {0x15f2a64138, 0x67da05}, {0x5e56d194af43045f, 0xcf1543fb99},
+ {0x15e61ed052036a, -0xc8e6}, {0x88125a341e85, -0xd23fb77683},
+ {-0xc06e20, 0x5a}, {-0x4f100219aea3e85d, 0xdcc56cb4efe993},
+ {-0x168d629105, -0xa7}, {-0x7b44e92f03ab2375, -0x6516},
+ };
+ for (const std::pair<int64_t, int64_t>& pair : small_pairs) {
+ SCOPED_TRACE(::testing::Message()
+ << "pair = {" << pair.first << ", " << pair.second << '}');
+
+ absl::int128 dividend = pair.first;
+ absl::int128 divisor = pair.second;
+ int64_t quotient = pair.first / pair.second;
+ int64_t remainder = pair.first % pair.second;
+
+ EXPECT_EQ(quotient, dividend / divisor);
+ EXPECT_EQ(quotient, absl::int128(dividend) /= divisor);
+ EXPECT_EQ(remainder, dividend % divisor);
+ EXPECT_EQ(remainder, absl::int128(dividend) %= divisor);
+ }
+
+ // Test behavior with 0, 1, and -1 with a sample of randomly generated large
+ // values.
+ absl::int128 values[] = {
+ absl::MakeInt128(0x63d26ee688a962b2, 0x9e1411abda5c1d70),
+ absl::MakeInt128(0x152f385159d6f986, 0xbf8d48ef63da395d),
+ -absl::MakeInt128(0x3098d7567030038c, 0x14e7a8a098dc2164),
+ -absl::MakeInt128(0x49a037aca35c809f, 0xa6a87525480ef330),
+ };
+ for (absl::int128 value : values) {
+ SCOPED_TRACE(::testing::Message() << "value = " << value);
+
+ EXPECT_EQ(0, 0 / value);
+ EXPECT_EQ(0, absl::int128(0) /= value);
+ EXPECT_EQ(0, 0 % value);
+ EXPECT_EQ(0, absl::int128(0) %= value);
+
+ EXPECT_EQ(value, value / 1);
+ EXPECT_EQ(value, absl::int128(value) /= 1);
+ EXPECT_EQ(0, value % 1);
+ EXPECT_EQ(0, absl::int128(value) %= 1);
+
+ EXPECT_EQ(-value, value / -1);
+ EXPECT_EQ(-value, absl::int128(value) /= -1);
+ EXPECT_EQ(0, value % -1);
+ EXPECT_EQ(0, absl::int128(value) %= -1);
+ }
+
+ // Min and max values
+ EXPECT_EQ(0, absl::Int128Max() / absl::Int128Min());
+ EXPECT_EQ(absl::Int128Max(), absl::Int128Max() % absl::Int128Min());
+ EXPECT_EQ(-1, absl::Int128Min() / absl::Int128Max());
+ EXPECT_EQ(-1, absl::Int128Min() % absl::Int128Max());
+
+ // Power of two division and modulo of random large dividends
+ absl::int128 positive_values[] = {
+ absl::MakeInt128(0x21e1a1cc69574620, 0xe7ac447fab2fc869),
+ absl::MakeInt128(0x32c2ff3ab89e66e8, 0x03379a613fd1ce74),
+ absl::MakeInt128(0x6f32ca786184dcaf, 0x046f9c9ecb3a9ce1),
+ absl::MakeInt128(0x1aeb469dd990e0ee, 0xda2740f243cd37eb),
+ };
+ for (absl::int128 value : positive_values) {
+ for (int i = 0; i < 127; ++i) {
+ SCOPED_TRACE(::testing::Message()
+ << "value = " << value << "; i = " << i);
+ absl::int128 power_of_two = absl::int128(1) << i;
+
+ EXPECT_EQ(value >> i, value / power_of_two);
+ EXPECT_EQ(value >> i, absl::int128(value) /= power_of_two);
+ EXPECT_EQ(value & (power_of_two - 1), value % power_of_two);
+ EXPECT_EQ(value & (power_of_two - 1),
+ absl::int128(value) %= power_of_two);
+ }
+ }
+
+ // Manually calculated cases with random large dividends
+ struct DivisionModCase {
+ absl::int128 dividend;
+ absl::int128 divisor;
+ absl::int128 quotient;
+ absl::int128 remainder;
+ };
+ DivisionModCase manual_cases[] = {
+ {absl::MakeInt128(0x6ada48d489007966, 0x3c9c5c98150d5d69),
+ absl::MakeInt128(0x8bc308fb, 0x8cb9cc9a3b803344), 0xc3b87e08,
+ absl::MakeInt128(0x1b7db5e1, 0xd9eca34b7af04b49)},
+ {absl::MakeInt128(0xd6946511b5b, 0x4886c5c96546bf5f),
+ -absl::MakeInt128(0x263b, 0xfd516279efcfe2dc), -0x59cbabf0,
+ absl::MakeInt128(0x622, 0xf462909155651d1f)},
+ {-absl::MakeInt128(0x33db734f9e8d1399, 0x8447ac92482bca4d), 0x37495078240,
+ -absl::MakeInt128(0xf01f1, 0xbc0368bf9a77eae8), -0x21a508f404d},
+ {-absl::MakeInt128(0x13f837b409a07e7d, 0x7fc8e248a7d73560), -0x1b9f,
+ absl::MakeInt128(0xb9157556d724, 0xb14f635714d7563e), -0x1ade},
+ };
+ for (const DivisionModCase test_case : manual_cases) {
+ EXPECT_EQ(test_case.quotient, test_case.dividend / test_case.divisor);
+ EXPECT_EQ(test_case.quotient,
+ absl::int128(test_case.dividend) /= test_case.divisor);
+ EXPECT_EQ(test_case.remainder, test_case.dividend % test_case.divisor);
+ EXPECT_EQ(test_case.remainder,
+ absl::int128(test_case.dividend) %= test_case.divisor);
+ }
+}
+
+TEST(Int128, BitwiseLogicTest) {
+ EXPECT_EQ(absl::int128(-1), ~absl::int128(0));
+
+ absl::int128 values[]{
+ 0, -1, 0xde400bee05c3ff6b, absl::MakeInt128(0x7f32178dd81d634a, 0),
+ absl::MakeInt128(0xaf539057055613a9, 0x7d104d7d946c2e4d)};
+ for (absl::int128 value : values) {
+ EXPECT_EQ(value, ~~value);
+
+ EXPECT_EQ(value, value | value);
+ EXPECT_EQ(value, value & value);
+ EXPECT_EQ(0, value ^ value);
+
+ EXPECT_EQ(value, absl::int128(value) |= value);
+ EXPECT_EQ(value, absl::int128(value) &= value);
+ EXPECT_EQ(0, absl::int128(value) ^= value);
+
+ EXPECT_EQ(value, value | 0);
+ EXPECT_EQ(0, value & 0);
+ EXPECT_EQ(value, value ^ 0);
+
+ EXPECT_EQ(absl::int128(-1), value | absl::int128(-1));
+ EXPECT_EQ(value, value & absl::int128(-1));
+ EXPECT_EQ(~value, value ^ absl::int128(-1));
+ }
+
+ // small sample of randomly generated int64_t's
+ std::pair<int64_t, int64_t> pairs64[]{
+ {0x7f86797f5e991af4, 0x1ee30494fb007c97},
+ {0x0b278282bacf01af, 0x58780e0a57a49e86},
+ {0x059f266ccb93a666, 0x3d5b731bae9286f5},
+ {0x63c0c4820f12108c, 0x58166713c12e1c3a},
+ {0x381488bb2ed2a66e, 0x2220a3eb76a3698c},
+ {0x2a0a0dfb81e06f21, 0x4b60585927f5523c},
+ {0x555b1c3a03698537, 0x25478cd19d8e53cb},
+ {0x4750f6f27d779225, 0x16397553c6ff05fc},
+ };
+ for (const std::pair<int64_t, int64_t>& pair : pairs64) {
+ SCOPED_TRACE(::testing::Message()
+ << "pair = {" << pair.first << ", " << pair.second << '}');
+
+ EXPECT_EQ(absl::MakeInt128(~pair.first, ~pair.second),
+ ~absl::MakeInt128(pair.first, pair.second));
+
+ EXPECT_EQ(absl::int128(pair.first & pair.second),
+ absl::int128(pair.first) & absl::int128(pair.second));
+ EXPECT_EQ(absl::int128(pair.first | pair.second),
+ absl::int128(pair.first) | absl::int128(pair.second));
+ EXPECT_EQ(absl::int128(pair.first ^ pair.second),
+ absl::int128(pair.first) ^ absl::int128(pair.second));
+
+ EXPECT_EQ(absl::int128(pair.first & pair.second),
+ absl::int128(pair.first) &= absl::int128(pair.second));
+ EXPECT_EQ(absl::int128(pair.first | pair.second),
+ absl::int128(pair.first) |= absl::int128(pair.second));
+ EXPECT_EQ(absl::int128(pair.first ^ pair.second),
+ absl::int128(pair.first) ^= absl::int128(pair.second));
+
+ EXPECT_EQ(
+ absl::MakeInt128(pair.first & pair.second, 0),
+ absl::MakeInt128(pair.first, 0) & absl::MakeInt128(pair.second, 0));
+ EXPECT_EQ(
+ absl::MakeInt128(pair.first | pair.second, 0),
+ absl::MakeInt128(pair.first, 0) | absl::MakeInt128(pair.second, 0));
+ EXPECT_EQ(
+ absl::MakeInt128(pair.first ^ pair.second, 0),
+ absl::MakeInt128(pair.first, 0) ^ absl::MakeInt128(pair.second, 0));
+
+ EXPECT_EQ(
+ absl::MakeInt128(pair.first & pair.second, 0),
+ absl::MakeInt128(pair.first, 0) &= absl::MakeInt128(pair.second, 0));
+ EXPECT_EQ(
+ absl::MakeInt128(pair.first | pair.second, 0),
+ absl::MakeInt128(pair.first, 0) |= absl::MakeInt128(pair.second, 0));
+ EXPECT_EQ(
+ absl::MakeInt128(pair.first ^ pair.second, 0),
+ absl::MakeInt128(pair.first, 0) ^= absl::MakeInt128(pair.second, 0));
+ }
+}
+
+TEST(Int128, BitwiseShiftTest) {
+ for (int i = 0; i < 64; ++i) {
+ for (int j = 0; j <= i; ++j) {
+ // Left shift from j-th bit to i-th bit.
+ SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
+ EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) << (i - j));
+ EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) <<= (i - j));
+ }
+ }
+ for (int i = 0; i < 63; ++i) {
+ for (int j = 0; j < 64; ++j) {
+ // Left shift from j-th bit to (i + 64)-th bit.
+ SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
+ EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
+ absl::int128(uint64_t{1} << j) << (i + 64 - j));
+ EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
+ absl::int128(uint64_t{1} << j) <<= (i + 64 - j));
+ }
+ for (int j = 0; j <= i; ++j) {
+ // Left shift from (j + 64)-th bit to (i + 64)-th bit.
+ SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
+ EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
+ absl::MakeInt128(uint64_t{1} << j, 0) << (i - j));
+ EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
+ absl::MakeInt128(uint64_t{1} << j, 0) <<= (i - j));
+ }
+ }
+
+ for (int i = 0; i < 64; ++i) {
+ for (int j = i; j < 64; ++j) {
+ // Right shift from j-th bit to i-th bit.
+ SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
+ EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) >> (j - i));
+ EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) >>= (j - i));
+ }
+ for (int j = 0; j < 63; ++j) {
+ // Right shift from (j + 64)-th bit to i-th bit.
+ SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
+ EXPECT_EQ(uint64_t{1} << i,
+ absl::MakeInt128(uint64_t{1} << j, 0) >> (j + 64 - i));
+ EXPECT_EQ(uint64_t{1} << i,
+ absl::MakeInt128(uint64_t{1} << j, 0) >>= (j + 64 - i));
+ }
+ }
+ for (int i = 0; i < 63; ++i) {
+ for (int j = i; j < 63; ++j) {
+ // Right shift from (j + 64)-th bit to (i + 64)-th bit.
+ SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j);
+ EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
+ absl::MakeInt128(uint64_t{1} << j, 0) >> (j - i));
+ EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0),
+ absl::MakeInt128(uint64_t{1} << j, 0) >>= (j - i));
+ }
+ }
+}
+
+TEST(Int128, NumericLimitsTest) {
+ static_assert(std::numeric_limits<absl::int128>::is_specialized, "");
+ static_assert(std::numeric_limits<absl::int128>::is_signed, "");
+ static_assert(std::numeric_limits<absl::int128>::is_integer, "");
+ EXPECT_EQ(static_cast<int>(127 * std::log10(2)),
+ std::numeric_limits<absl::int128>::digits10);
+ EXPECT_EQ(absl::Int128Min(), std::numeric_limits<absl::int128>::min());
+ EXPECT_EQ(absl::Int128Min(), std::numeric_limits<absl::int128>::lowest());
+ EXPECT_EQ(absl::Int128Max(), std::numeric_limits<absl::int128>::max());
+}
+
} // namespace
diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel
index f7587bf9..f78fbc7e 100644
--- a/absl/random/BUILD.bazel
+++ b/absl/random/BUILD.bazel
@@ -1,17 +1,32 @@
+#
+# 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.
+#
+
# ABSL random-number generation libraries.
+load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
"ABSL_DEFAULT_LINKOPTS",
- "ABSL_EXCEPTIONS_FLAG",
- "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
"ABSL_TEST_COPTS",
)
package(default_visibility = ["//visibility:public"])
-licenses(["notice"]) # Apache 2.0
+licenses(["notice"])
cc_library(
name = "random",
@@ -52,15 +67,17 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
"//absl/base:base_internal",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/meta:type_traits",
- "//absl/random/internal:distribution_impl",
"//absl/random/internal:distributions",
"//absl/random/internal:fast_uniform_bits",
"//absl/random/internal:fastmath",
+ "//absl/random/internal:generate_real",
"//absl/random/internal:iostream_state_saver",
"//absl/random/internal:traits",
"//absl/random/internal:uniform_helper",
+ "//absl/random/internal:wide_multiply",
"//absl/strings",
"//absl/types:span",
],
@@ -70,8 +87,8 @@ cc_library(
name = "seed_gen_exception",
srcs = ["seed_gen_exception.cc"],
hdrs = ["seed_gen_exception.h"],
- copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = ["//absl/base:config"],
)
@@ -94,6 +111,58 @@ cc_library(
],
)
+cc_library(
+ name = "bit_gen_ref",
+ hdrs = ["bit_gen_ref.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/base:core_headers",
+ "//absl/meta:type_traits",
+ "//absl/random/internal:distribution_caller",
+ "//absl/random/internal:fast_uniform_bits",
+ "//absl/random/internal:mocking_bit_gen_base",
+ ],
+)
+
+cc_library(
+ name = "mock_distributions",
+ testonly = 1,
+ hdrs = ["mock_distributions.h"],
+ deps = [
+ ":distributions",
+ ":mocking_bit_gen",
+ "//absl/meta:type_traits",
+ "//absl/random/internal:mock_overload_set",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_library(
+ name = "mocking_bit_gen",
+ testonly = 1,
+ srcs = [
+ "mocking_bit_gen.cc",
+ ],
+ hdrs = [
+ "mocking_bit_gen.h",
+ ],
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":distributions",
+ "//absl/base:raw_logging_internal",
+ "//absl/container:flat_hash_map",
+ "//absl/meta:type_traits",
+ "//absl/random/internal:distribution_caller",
+ "//absl/random/internal:mocking_bit_gen_base",
+ "//absl/strings",
+ "//absl/types:span",
+ "//absl/types:variant",
+ "//absl/utility",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
cc_test(
name = "bernoulli_distribution_test",
size = "small",
@@ -115,11 +184,12 @@ cc_test(
timeout = "eternal", # Android can take a very long time
srcs = ["beta_distribution_test.cc"],
copts = ABSL_TEST_COPTS,
+ flaky = 1,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":distributions",
":random",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
@@ -168,8 +238,8 @@ cc_test(
deps = [
":distributions",
":random",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
@@ -189,7 +259,7 @@ cc_test(
deps = [
":distributions",
":random",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
@@ -214,8 +284,8 @@ cc_test(
deps = [
":distributions",
":random",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/container:flat_hash_map",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
@@ -234,8 +304,8 @@ cc_test(
deps = [
":distributions",
":random",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
@@ -256,8 +326,8 @@ cc_test(
deps = [
":distributions",
":random",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
@@ -278,7 +348,7 @@ cc_test(
deps = [
":distributions",
":random",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
@@ -302,7 +372,7 @@ cc_test(
deps = [
":distributions",
":random",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
@@ -321,7 +391,7 @@ cc_test(
deps = [
":distributions",
":random",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/random/internal:distribution_test_util",
"//absl/random/internal:sequence_urbg",
"//absl/strings",
@@ -330,6 +400,46 @@ cc_test(
)
cc_test(
+ name = "bit_gen_ref_test",
+ size = "small",
+ srcs = ["bit_gen_ref_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":bit_gen_ref",
+ ":random",
+ "//absl/random/internal:sequence_urbg",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "mocking_bit_gen_test",
+ size = "small",
+ srcs = ["mocking_bit_gen_test.cc"],
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":bit_gen_ref",
+ ":mock_distributions",
+ ":mocking_bit_gen",
+ ":random",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "mock_distributions_test",
+ size = "small",
+ srcs = ["mock_distributions_test.cc"],
+ deps = [
+ ":mock_distributions",
+ ":mocking_bit_gen",
+ ":random",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
name = "examples_test",
size = "small",
srcs = ["examples_test.cc"],
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt
index 2d5c0658..efa55d8f 100644
--- a/absl/random/CMakeLists.txt
+++ b/absl/random/CMakeLists.txt
@@ -34,6 +34,132 @@ absl_cc_library(
absl_cc_library(
NAME
+ random_bit_gen_ref
+ HDRS
+ "bit_gen_ref.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::core_headers
+ absl::random_internal_distribution_caller
+ absl::random_internal_fast_uniform_bits
+ absl::random_internal_mocking_bit_gen_base
+ absl::type_traits
+)
+
+absl_cc_test(
+ NAME
+ random_bit_gen_ref_test
+ SRCS
+ "bit_gen_ref_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::random_bit_gen_ref
+ absl::random_random
+ absl::random_internal_sequence_urbg
+ gmock
+ gtest_main
+)
+
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+ NAME
+ random_internal_mocking_bit_gen_base
+ HDRS
+ "internal/mocking_bit_gen_base.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::random_random
+ absl::strings
+)
+
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+ NAME
+ random_internal_mock_overload_set
+ HDRS
+ "internal/mock_overload_set.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::random_mocking_bit_gen
+ TESTONLY
+)
+
+absl_cc_library(
+ NAME
+ random_mocking_bit_gen
+ HDRS
+ "mock_distributions.h"
+ "mocking_bit_gen.h"
+ SRCS
+ "mocking_bit_gen.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::flat_hash_map
+ absl::raw_logging_internal
+ absl::random_distributions
+ absl::random_internal_distribution_caller
+ absl::random_internal_mocking_bit_gen_base
+ absl::random_internal_mock_overload_set
+ absl::strings
+ absl::span
+ absl::type_traits
+ absl::utility
+ absl::variant
+ gmock
+ gtest
+ TESTONLY
+)
+
+absl_cc_test(
+ NAME
+ random_mock_distributions_test
+ SRCS
+ "mock_distributions_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::random_mocking_bit_gen
+ absl::random_random
+ gmock
+ gtest_main
+)
+
+absl_cc_test(
+ NAME
+ random_mocking_bit_gen_test
+ SRCS
+ "mocking_bit_gen_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::random_bit_gen_ref
+ absl::random_mocking_bit_gen
+ absl::random_random
+ gmock
+ gtest_main
+)
+
+absl_cc_library(
+ NAME
random_distributions
SRCS
"discrete_distribution.cc"
@@ -57,14 +183,16 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::base_internal
+ absl::config
absl::core_headers
- absl::random_internal_distribution_impl
+ absl::random_internal_generate_real
absl::random_internal_distributions
absl::random_internal_fast_uniform_bits
absl::random_internal_fastmath
absl::random_internal_iostream_state_saver
absl::random_internal_traits
absl::random_internal_uniform_helper
+ absl::random_internal_wide_multiply
absl::strings
absl::span
absl::type_traits
@@ -79,9 +207,7 @@ absl_cc_library(
"seed_gen_exception.h"
COPTS
${ABSL_DEFAULT_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::config
@@ -137,9 +263,9 @@ absl_cc_test(
DEPS
absl::random_distributions
absl::random_random
- absl::base
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
+ absl::raw_logging_internal
absl::strings
absl::str_format
gmock
@@ -174,6 +300,7 @@ absl_cc_test(
${ABSL_DEFAULT_LINKOPTS}
absl::random_distributions
absl::random_random
+ absl::raw_logging_internal
gmock
gtest_main
)
@@ -187,12 +314,12 @@ absl_cc_test(
${ABSL_TEST_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
- absl::base
absl::core_headers
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
absl::random_random
+ absl::raw_logging_internal
absl::strings
absl::str_format
gmock
@@ -209,11 +336,11 @@ absl_cc_test(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::base
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
absl::random_random
+ absl::raw_logging_internal
absl::strings
gmock
gtest_main
@@ -231,11 +358,11 @@ absl_cc_test(
DEPS
absl::random_distributions
absl::random_random
- absl::base
absl::core_headers
absl::flat_hash_map
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
+ absl::raw_logging_internal
absl::strings
absl::str_format
gmock
@@ -252,12 +379,12 @@ absl_cc_test(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::base
absl::core_headers
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
absl::random_random
+ absl::raw_logging_internal
absl::strings
absl::str_format
gmock
@@ -274,12 +401,12 @@ absl_cc_test(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::base
absl::core_headers
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
absl::random_random
+ absl::raw_logging_internal
absl::strings
absl::str_format
gmock
@@ -296,11 +423,11 @@ absl_cc_test(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::base
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
absl::random_random
+ absl::raw_logging_internal
absl::strings
gmock
gtest_main
@@ -316,7 +443,6 @@ absl_cc_test(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::base
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
@@ -336,11 +462,11 @@ absl_cc_test(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::base
absl::random_distributions
absl::random_internal_distribution_test_util
absl::random_internal_sequence_urbg
absl::random_random
+ absl::raw_logging_internal
absl::strings
gmock
gtest_main
@@ -401,6 +527,8 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::config
)
# Internal-only target, do not depend on directly.
@@ -434,6 +562,8 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::config
)
# Internal-only target, do not depend on directly.
@@ -448,11 +578,12 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
+ $<$<BOOL:${MINGW}>:"bcrypt">
DEPS
- absl::base
absl::core_headers
absl::optional
absl::random_internal_fast_uniform_bits
+ absl::raw_logging_internal
absl::span
absl::strings
)
@@ -478,6 +609,7 @@ absl_cc_library(
absl::random_internal_seed_material
absl::random_internal_traits
absl::random_seed_gen_exception
+ absl::raw_logging_internal
absl::span
)
@@ -491,6 +623,8 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::config
TESTONLY
)
@@ -504,6 +638,8 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::config
TESTONLY
)
@@ -543,19 +679,34 @@ absl_cc_library(
# Internal-only target, do not depend on directly.
absl_cc_library(
NAME
- random_internal_distribution_impl
+ random_internal_generate_real
HDRS
- "internal/distribution_impl.h"
+ "internal/generate_real.h"
COPTS
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::bits
- absl::config
- absl::int128
absl::random_internal_fastmath
absl::random_internal_traits
+ absl::type_traits
+)
+
+# Internal-only target, do not depend on directly.
+absl_cc_library(
+ NAME
+ random_internal_wide_multiply
+ HDRS
+ "internal/wide_multiply.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::bits
+ absl::config
+ absl::int128
)
# Internal-only target, do not depend on directly.
@@ -624,6 +775,7 @@ absl_cc_library(
DEPS
absl::random_internal_iostream_state_saver
absl::random_internal_randen
+ absl::raw_logging_internal
absl::type_traits
)
@@ -639,6 +791,8 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::config
)
# Internal-only target, do not depend on directly.
@@ -654,7 +808,6 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::base
absl::random_internal_platform
absl::random_internal_randen_hwaes
absl::random_internal_randen_slow
@@ -674,6 +827,7 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::random_internal_platform
+ absl::config
)
# Internal-only target, do not depend on directly.
@@ -693,6 +847,7 @@ absl_cc_library(
DEPS
absl::random_internal_platform
absl::random_internal_randen_hwaes_impl
+ absl::config
)
# Internal-only target, do not depend on directly.
@@ -709,6 +864,7 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::random_internal_platform
+ absl::config
)
# Internal-only target, do not depend on directly.
@@ -726,8 +882,9 @@ absl_cc_library(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::base
+ absl::config
absl::core_headers
+ absl::raw_logging_internal
absl::strings
absl::str_format
absl::span
@@ -751,9 +908,9 @@ absl_cc_test(
# Internal-only target, do not depend on directly.
absl_cc_test(
NAME
- random_internal_distribution_impl_test
+ random_internal_generate_real_test
SRCS
- "internal/distribution_impl_test.cc"
+ "internal/generate_real_test.cc"
COPTS
${ABSL_TEST_COPTS}
LINKOPTS
@@ -761,8 +918,7 @@ absl_cc_test(
DEPS
absl::bits
absl::flags
- absl::int128
- absl::random_internal_distribution_impl
+ absl::random_internal_generate_real
gtest_main
)
@@ -941,9 +1097,9 @@ absl_cc_test(
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
- absl::base
absl::random_internal_explicit_seed_seq
absl::random_internal_randen_engine
+ absl::raw_logging_internal
absl::strings
absl::time
gmock
@@ -995,7 +1151,7 @@ absl_cc_test(
absl::random_internal_platform
absl::random_internal_randen_hwaes
absl::random_internal_randen_hwaes_impl
- absl::base
+ absl::raw_logging_internal
absl::str_format
gmock
gtest
@@ -1013,7 +1169,6 @@ absl_cc_library(
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::core_headers
- absl::random_internal_distribution_impl
absl::random_internal_fast_uniform_bits
absl::random_internal_iostream_state_saver
absl::random_internal_traits
@@ -1026,9 +1181,28 @@ absl_cc_test(
random_internal_iostream_state_saver_test
SRCS
"internal/iostream_state_saver_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
LINKOPTS
${ABSL_DEFAULT_LINKOPTS}
DEPS
absl::random_internal_iostream_state_saver
gtest_main
)
+
+# Internal-only target, do not depend on directly.
+absl_cc_test(
+ NAME
+ random_internal_wide_multiply_test
+ SRCS
+ internal/wide_multiply_test.cc
+ COPTS
+ ${ABSL_TEST_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::random_internal_wide_multiply
+ absl::bits
+ absl::int128
+ gtest_main
+)
diff --git a/absl/random/bernoulli_distribution.h b/absl/random/bernoulli_distribution.h
index 0afc2c14..25bd0d5c 100644
--- a/absl/random/bernoulli_distribution.h
+++ b/absl/random/bernoulli_distribution.h
@@ -24,7 +24,7 @@
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// absl::bernoulli_distribution is a drop in replacement for
// std::bernoulli_distribution. It guarantees that (given a perfect
@@ -194,7 +194,7 @@ bool bernoulli_distribution::Generate(double p,
}
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_
diff --git a/absl/random/beta_distribution.h b/absl/random/beta_distribution.h
index ff1eba80..c154066f 100644
--- a/absl/random/beta_distribution.h
+++ b/absl/random/beta_distribution.h
@@ -22,13 +22,14 @@
#include <ostream>
#include <type_traits>
-#include "absl/random/internal/distribution_impl.h"
+#include "absl/meta/type_traits.h"
#include "absl/random/internal/fast_uniform_bits.h"
#include "absl/random/internal/fastmath.h"
+#include "absl/random/internal/generate_real.h"
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// absl::beta_distribution:
// Generate a floating-point variate conforming to a Beta distribution:
@@ -130,7 +131,7 @@ class beta_distribution {
private:
friend class beta_distribution;
-#ifdef COMPILER_MSVC
+#ifdef _MSC_VER
// MSVC does not have constexpr implementations for std::log and std::exp
// so they are computed at runtime.
#define ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR
@@ -276,15 +277,21 @@ typename beta_distribution<RealType>::result_type
beta_distribution<RealType>::AlgorithmJoehnk(
URBG& g, // NOLINT(runtime/references)
const param_type& p) {
+ using random_internal::GeneratePositiveTag;
+ using random_internal::GenerateRealFromBits;
+ using real_type =
+ absl::conditional_t<std::is_same<RealType, float>::value, float, double>;
+
// Based on Joehnk, M. D. Erzeugung von betaverteilten und gammaverteilten
// Zufallszahlen. Metrika 8.1 (1964): 5-15.
// This method is described in Knuth, Vol 2 (Third Edition), pp 134.
- using RandU64ToReal = typename random_internal::RandU64ToReal<result_type>;
- using random_internal::PositiveValueT;
+
result_type u, v, x, y, z;
for (;;) {
- u = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g));
- v = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g));
+ u = GenerateRealFromBits<real_type, GeneratePositiveTag, false>(
+ fast_u64_(g));
+ v = GenerateRealFromBits<real_type, GeneratePositiveTag, false>(
+ fast_u64_(g));
// Direct method. std::pow is slow for float, so rely on the optimizer to
// remove the std::pow() path for that case.
@@ -328,12 +335,14 @@ typename beta_distribution<RealType>::result_type
beta_distribution<RealType>::AlgorithmCheng(
URBG& g, // NOLINT(runtime/references)
const param_type& p) {
+ using random_internal::GeneratePositiveTag;
+ using random_internal::GenerateRealFromBits;
+ using real_type =
+ absl::conditional_t<std::is_same<RealType, float>::value, float, double>;
+
// Based on Cheng, Russell CH. Generating beta variates with nonintegral
// shape parameters. Communications of the ACM 21.4 (1978): 317-322.
// (https://dl.acm.org/citation.cfm?id=359482).
- using RandU64ToReal = typename random_internal::RandU64ToReal<result_type>;
- using random_internal::PositiveValueT;
-
static constexpr result_type kLogFour =
result_type(1.3862943611198906188344642429163531361); // log(4)
static constexpr result_type kS =
@@ -342,8 +351,10 @@ beta_distribution<RealType>::AlgorithmCheng(
const bool use_algorithm_ba = (p.method_ == param_type::CHENG_BA);
result_type u1, u2, v, w, z, r, s, t, bw_inv, lhs;
for (;;) {
- u1 = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g));
- u2 = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g));
+ u1 = GenerateRealFromBits<real_type, GeneratePositiveTag, false>(
+ fast_u64_(g));
+ u2 = GenerateRealFromBits<real_type, GeneratePositiveTag, false>(
+ fast_u64_(g));
v = p.y_ * std::log(u1 / (1 - u1));
w = p.a_ * std::exp(v);
bw_inv = result_type(1) / (p.b_ + w);
@@ -410,7 +421,7 @@ std::basic_istream<CharT, Traits>& operator>>(
return is;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_BETA_DISTRIBUTION_H_
diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc
index 966ad08b..d0111b3e 100644
--- a/absl/random/beta_distribution_test.cc
+++ b/absl/random/beta_distribution_test.cc
@@ -92,7 +92,7 @@ TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) {
for (TypeParam alpha : kValues) {
for (TypeParam beta : kValues) {
ABSL_INTERNAL_LOG(
- INFO, absl::StrFormat("Smoke test for Beta(%f, %f)", alpha, beta));
+ INFO, absl::StrFormat("Smoke test for Beta(%a, %a)", alpha, beta));
param_type param(alpha, beta);
absl::beta_distribution<TypeParam> before(alpha, beta);
diff --git a/absl/random/bit_gen_ref.h b/absl/random/bit_gen_ref.h
new file mode 100644
index 00000000..e8771162
--- /dev/null
+++ b/absl/random/bit_gen_ref.h
@@ -0,0 +1,153 @@
+//
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: bit_gen_ref.h
+// -----------------------------------------------------------------------------
+//
+// This header defines a bit generator "reference" class, for use in interfaces
+// that take both Abseil (e.g. `absl::BitGen`) and standard library (e.g.
+// `std::mt19937`) bit generators.
+
+#ifndef ABSL_RANDOM_BIT_GEN_REF_H_
+#define ABSL_RANDOM_BIT_GEN_REF_H_
+
+#include "absl/base/macros.h"
+#include "absl/meta/type_traits.h"
+#include "absl/random/internal/distribution_caller.h"
+#include "absl/random/internal/fast_uniform_bits.h"
+#include "absl/random/internal/mocking_bit_gen_base.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace random_internal {
+
+template <typename URBG, typename = void, typename = void, typename = void>
+struct is_urbg : std::false_type {};
+
+template <typename URBG>
+struct is_urbg<
+ URBG,
+ absl::enable_if_t<std::is_same<
+ typename URBG::result_type,
+ typename std::decay<decltype((URBG::min)())>::type>::value>,
+ absl::enable_if_t<std::is_same<
+ typename URBG::result_type,
+ typename std::decay<decltype((URBG::max)())>::type>::value>,
+ absl::enable_if_t<std::is_same<
+ typename URBG::result_type,
+ typename std::decay<decltype(std::declval<URBG>()())>::type>::value>>
+ : std::true_type {};
+
+} // namespace random_internal
+
+// -----------------------------------------------------------------------------
+// absl::BitGenRef
+// -----------------------------------------------------------------------------
+//
+// `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic
+// non-owning "reference" interface for use in place of any specific uniform
+// random bit generator (URBG). This class may be used for both Abseil
+// (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g
+// `std::mt19937`, `std::minstd_rand`) bit generators.
+//
+// Like other reference classes, `absl::BitGenRef` does not own the
+// underlying bit generator, and the underlying instance must outlive the
+// `absl::BitGenRef`.
+//
+// `absl::BitGenRef` is particularly useful when used with an
+// `absl::MockingBitGen` to test specific paths in functions which use random
+// values.
+//
+// Example:
+// void TakesBitGenRef(absl::BitGenRef gen) {
+// int x = absl::Uniform<int>(gen, 0, 1000);
+// }
+//
+class BitGenRef {
+ public:
+ using result_type = uint64_t;
+
+ BitGenRef(const absl::BitGenRef&) = default;
+ BitGenRef(absl::BitGenRef&&) = default;
+ BitGenRef& operator=(const absl::BitGenRef&) = default;
+ BitGenRef& operator=(absl::BitGenRef&&) = default;
+
+ template <typename URBG,
+ typename absl::enable_if_t<
+ (!std::is_same<URBG, BitGenRef>::value &&
+ random_internal::is_urbg<URBG>::value)>* = nullptr>
+ BitGenRef(URBG& gen) // NOLINT
+ : mocked_gen_ptr_(MakeMockPointer(&gen)),
+ t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
+ generate_impl_fn_(ImplFn<URBG>) {
+ }
+
+ static constexpr result_type(min)() {
+ return (std::numeric_limits<result_type>::min)();
+ }
+
+ static constexpr result_type(max)() {
+ return (std::numeric_limits<result_type>::max)();
+ }
+
+ result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); }
+
+ private:
+ friend struct absl::random_internal::DistributionCaller<absl::BitGenRef>;
+ using impl_fn = result_type (*)(uintptr_t);
+ using mocker_base_t = absl::random_internal::MockingBitGenBase;
+
+ // Convert an arbitrary URBG pointer into either a valid mocker_base_t
+ // pointer or a nullptr.
+ static inline mocker_base_t* MakeMockPointer(mocker_base_t* t) { return t; }
+ static inline mocker_base_t* MakeMockPointer(void*) { return nullptr; }
+
+ template <typename URBG>
+ static result_type ImplFn(uintptr_t ptr) {
+ // Ensure that the return values from operator() fill the entire
+ // range promised by result_type, min() and max().
+ absl::random_internal::FastUniformBits<result_type> fast_uniform_bits;
+ return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr));
+ }
+
+ mocker_base_t* mocked_gen_ptr_;
+ uintptr_t t_erased_gen_ptr_;
+ impl_fn generate_impl_fn_;
+};
+
+namespace random_internal {
+
+template <>
+struct DistributionCaller<absl::BitGenRef> {
+ template <typename DistrT, typename FormatT, typename... Args>
+ static typename DistrT::result_type Call(absl::BitGenRef* gen_ref,
+ Args&&... args) {
+ auto* mock_ptr = gen_ref->mocked_gen_ptr_;
+ if (mock_ptr == nullptr) {
+ DistrT dist(std::forward<Args>(args)...);
+ return dist(*gen_ref);
+ } else {
+ return mock_ptr->template Call<DistrT, FormatT>(
+ std::forward<Args>(args)...);
+ }
+ }
+};
+
+} // namespace random_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_RANDOM_BIT_GEN_REF_H_
diff --git a/absl/random/bit_gen_ref_test.cc b/absl/random/bit_gen_ref_test.cc
new file mode 100644
index 00000000..ca0e4d70
--- /dev/null
+++ b/absl/random/bit_gen_ref_test.cc
@@ -0,0 +1,101 @@
+//
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "absl/random/bit_gen_ref.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/random/internal/sequence_urbg.h"
+#include "absl/random/random.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+class ConstBitGen : public absl::random_internal::MockingBitGenBase {
+ bool CallImpl(const std::type_info&, void*, void* result) override {
+ *static_cast<int*>(result) = 42;
+ return true;
+ }
+};
+
+namespace random_internal {
+template <>
+struct DistributionCaller<ConstBitGen> {
+ template <typename DistrT, typename FormatT, typename... Args>
+ static typename DistrT::result_type Call(ConstBitGen* gen, Args&&... args) {
+ return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...);
+ }
+};
+} // namespace random_internal
+
+namespace {
+int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); }
+
+template <typename T>
+class BitGenRefTest : public testing::Test {};
+
+using BitGenTypes =
+ ::testing::Types<absl::BitGen, absl::InsecureBitGen, std::mt19937,
+ std::mt19937_64, std::minstd_rand>;
+TYPED_TEST_SUITE(BitGenRefTest, BitGenTypes);
+
+TYPED_TEST(BitGenRefTest, BasicTest) {
+ TypeParam gen;
+ auto x = FnTest(gen);
+ EXPECT_NEAR(x, 4, 3);
+}
+
+TYPED_TEST(BitGenRefTest, Copyable) {
+ TypeParam gen;
+ absl::BitGenRef gen_ref(gen);
+ FnTest(gen_ref); // Copy
+}
+
+TEST(BitGenRefTest, PassThroughEquivalence) {
+ // sequence_urbg returns 64-bit results.
+ absl::random_internal::sequence_urbg urbg(
+ {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
+ 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
+ 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
+ 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
+
+ std::vector<uint64_t> output(12);
+
+ {
+ absl::BitGenRef view(urbg);
+ for (auto& v : output) {
+ v = view();
+ }
+ }
+
+ std::vector<uint64_t> expected(
+ {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
+ 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
+ 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
+ 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
+
+ EXPECT_THAT(output, testing::Eq(expected));
+}
+
+TEST(BitGenRefTest, MockingBitGenBaseOverrides) {
+ ConstBitGen const_gen;
+ EXPECT_EQ(FnTest(const_gen), 42);
+
+ absl::BitGenRef gen_ref(const_gen);
+ EXPECT_EQ(FnTest(gen_ref), 42); // Copy
+}
+} // namespace
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/random/discrete_distribution.cc b/absl/random/discrete_distribution.cc
index 13a2dbe4..081accee 100644
--- a/absl/random/discrete_distribution.cc
+++ b/absl/random/discrete_distribution.cc
@@ -15,7 +15,7 @@
#include "absl/random/discrete_distribution.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// Initializes the distribution table for Walker's Aliasing algorithm, described
@@ -94,5 +94,5 @@ std::vector<std::pair<double, size_t>> InitDiscreteDistribution(
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/discrete_distribution.h b/absl/random/discrete_distribution.h
index 5866fb23..171aa11a 100644
--- a/absl/random/discrete_distribution.h
+++ b/absl/random/discrete_distribution.h
@@ -29,7 +29,7 @@
#include "absl/random/uniform_int_distribution.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// absl::discrete_distribution
//
@@ -241,7 +241,7 @@ std::basic_istream<CharT, Traits>& operator>>(
return is;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_
diff --git a/absl/random/distribution_format_traits.h b/absl/random/distribution_format_traits.h
index 838271c7..22b358cc 100644
--- a/absl/random/distribution_format_traits.h
+++ b/absl/random/distribution_format_traits.h
@@ -36,7 +36,13 @@
#include "absl/types/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
+
+struct IntervalClosedClosedTag;
+struct IntervalClosedOpenTag;
+struct IntervalOpenClosedTag;
+struct IntervalOpenOpenTag;
+
namespace random_internal {
// ScalarTypeName defines a preferred hierarchy of preferred type names for
@@ -244,8 +250,29 @@ struct DistributionFormatTraits<absl::log_uniform_int_distribution<R>> {
}
};
+template <typename NumType>
+struct UniformDistributionWrapper;
+
+template <typename NumType>
+struct DistributionFormatTraits<UniformDistributionWrapper<NumType>> {
+ using distribution_t = UniformDistributionWrapper<NumType>;
+ using result_t = NumType;
+
+ static constexpr const char* Name() { return "Uniform"; }
+
+ static std::string FunctionName() {
+ return absl::StrCat(Name(), "<", ScalarTypeName<NumType>(), ">");
+ }
+ static std::string FormatArgs(const distribution_t& d) {
+ return absl::StrCat((d.min)(), ", ", (d.max)());
+ }
+ static std::string FormatResults(absl::Span<const result_t> results) {
+ return absl::StrJoin(results, ", ");
+ }
+};
+
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_
diff --git a/absl/random/distributions.h b/absl/random/distributions.h
index f2ebfc2d..d026d92b 100644
--- a/absl/random/distributions.h
+++ b/absl/random/distributions.h
@@ -67,20 +67,15 @@
#include "absl/random/zipf_distribution.h"
namespace absl {
-inline namespace lts_2019_08_08 {
-
-ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalClosedClosedT,
- IntervalClosedClosed, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalClosedClosedT,
- IntervalClosed, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalClosedOpenT,
- IntervalClosedOpen, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenOpenT,
- IntervalOpenOpen, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenOpenT,
- IntervalOpen, {});
-ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenClosedT,
- IntervalOpenClosed, {});
+ABSL_NAMESPACE_BEGIN
+
+ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedClosedTag, IntervalClosedClosed,
+ {});
+ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedClosedTag, IntervalClosed, {});
+ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalClosedOpenTag, IntervalClosedOpen, {});
+ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenOpenTag, IntervalOpenOpen, {});
+ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenOpenTag, IntervalOpen, {});
+ABSL_INTERNAL_INLINE_CONSTEXPR(IntervalOpenClosedTag, IntervalOpenClosed, {});
// -----------------------------------------------------------------------------
// absl::Uniform<T>(tag, bitgen, lo, hi)
@@ -102,7 +97,7 @@ ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenClosedT,
// the return type based on the provided endpoint arguments {A lo, B hi}.
// Given these endpoints, one of {A, B} will be chosen as the return type, if
// a type can be implicitly converted into the other in a lossless way. The
-// lack of any such implcit conversion between {A, B} will produce a
+// lack of any such implicit conversion between {A, B} will produce a
// compile-time error
//
// See https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)
@@ -130,7 +125,15 @@ Uniform(TagType tag,
URBG&& urbg, // NOLINT(runtime/references)
R lo, R hi) {
using gen_t = absl::decay_t<URBG>;
- return random_internal::UniformImpl<R, TagType, gen_t>(tag, urbg, lo, hi);
+ using distribution_t = random_internal::UniformDistributionWrapper<R>;
+ using format_t = random_internal::DistributionFormatTraits<distribution_t>;
+
+ auto a = random_internal::uniform_lower_bound(tag, lo, hi);
+ auto b = random_internal::uniform_upper_bound(tag, lo, hi);
+ if (a > b) return a;
+
+ return random_internal::DistributionCaller<gen_t>::template Call<
+ distribution_t, format_t>(&urbg, tag, lo, hi);
}
// absl::Uniform<T>(bitgen, lo, hi)
@@ -141,11 +144,17 @@ template <typename R = void, typename URBG>
typename absl::enable_if_t<!std::is_same<R, void>::value, R> //
Uniform(URBG&& urbg, // NOLINT(runtime/references)
R lo, R hi) {
- constexpr auto tag = absl::IntervalClosedOpen;
- using tag_t = decltype(tag);
using gen_t = absl::decay_t<URBG>;
+ using distribution_t = random_internal::UniformDistributionWrapper<R>;
+ using format_t = random_internal::DistributionFormatTraits<distribution_t>;
- return random_internal::UniformImpl<R, tag_t, gen_t>(tag, urbg, lo, hi);
+ constexpr auto tag = absl::IntervalClosedOpen;
+ auto a = random_internal::uniform_lower_bound(tag, lo, hi);
+ auto b = random_internal::uniform_upper_bound(tag, lo, hi);
+ if (a > b) return a;
+
+ return random_internal::DistributionCaller<gen_t>::template Call<
+ distribution_t, format_t>(&urbg, lo, hi);
}
// absl::Uniform(tag, bitgen, lo, hi)
@@ -162,9 +171,16 @@ Uniform(TagType tag,
A lo, B hi) {
using gen_t = absl::decay_t<URBG>;
using return_t = typename random_internal::uniform_inferred_return_t<A, B>;
+ using distribution_t = random_internal::UniformDistributionWrapper<return_t>;
+ using format_t = random_internal::DistributionFormatTraits<distribution_t>;
+
+ auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi);
+ auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi);
+ if (a > b) return a;
- return random_internal::UniformImpl<return_t, TagType, gen_t>(tag, urbg, lo,
- hi);
+ return random_internal::DistributionCaller<gen_t>::template Call<
+ distribution_t, format_t>(&urbg, tag, static_cast<return_t>(lo),
+ static_cast<return_t>(hi));
}
// absl::Uniform(bitgen, lo, hi)
@@ -177,13 +193,19 @@ typename absl::enable_if_t<std::is_same<R, void>::value,
random_internal::uniform_inferred_return_t<A, B>>
Uniform(URBG&& urbg, // NOLINT(runtime/references)
A lo, B hi) {
- constexpr auto tag = absl::IntervalClosedOpen;
- using tag_t = decltype(tag);
using gen_t = absl::decay_t<URBG>;
using return_t = typename random_internal::uniform_inferred_return_t<A, B>;
+ using distribution_t = random_internal::UniformDistributionWrapper<return_t>;
+ using format_t = random_internal::DistributionFormatTraits<distribution_t>;
- return random_internal::UniformImpl<return_t, tag_t, gen_t>(tag, urbg, lo,
- hi);
+ constexpr auto tag = absl::IntervalClosedOpen;
+ auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi);
+ auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi);
+ if (a > b) return a;
+
+ return random_internal::DistributionCaller<gen_t>::template Call<
+ distribution_t, format_t>(&urbg, static_cast<return_t>(lo),
+ static_cast<return_t>(hi));
}
// absl::Uniform<unsigned T>(bitgen)
@@ -193,13 +215,12 @@ Uniform(URBG&& urbg, // NOLINT(runtime/references)
template <typename R, typename URBG>
typename absl::enable_if_t<!std::is_signed<R>::value, R> //
Uniform(URBG&& urbg) { // NOLINT(runtime/references)
- constexpr auto tag = absl::IntervalClosedClosed;
- constexpr auto lo = std::numeric_limits<R>::lowest();
- constexpr auto hi = (std::numeric_limits<R>::max)();
- using tag_t = decltype(tag);
using gen_t = absl::decay_t<URBG>;
+ using distribution_t = random_internal::UniformDistributionWrapper<R>;
+ using format_t = random_internal::DistributionFormatTraits<distribution_t>;
- return random_internal::UniformImpl<R, tag_t, gen_t>(tag, urbg, lo, hi);
+ return random_internal::DistributionCaller<gen_t>::template Call<
+ distribution_t, format_t>(&urbg);
}
// -----------------------------------------------------------------------------
@@ -270,10 +291,10 @@ RealType Beta(URBG&& urbg, // NOLINT(runtime/references)
// absl::Exponential<T>(bitgen, lambda = 1)
// -----------------------------------------------------------------------------
//
-// `absl::Exponential` produces a floating point number for discrete
-// distributions of events occurring continuously and independently at a
-// constant average rate. `T` must be a floating point type, but may be inferred
-// from the type of `lambda`.
+// `absl::Exponential` produces a floating point number representing the
+// distance (time) between two consecutive events in a point process of events
+// occurring continuously and independently at a constant average rate. `T` must
+// be a floating point type, but may be inferred from the type of `lambda`.
//
// See https://en.wikipedia.org/wiki/Exponential_distribution.
//
@@ -438,7 +459,7 @@ IntType Zipf(URBG&& urbg, // NOLINT(runtime/references)
distribution_t, format_t>(&urbg, hi, q, v);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_DISTRIBUTIONS_H_
diff --git a/absl/random/distributions_test.cc b/absl/random/distributions_test.cc
index eb82868d..2d92723a 100644
--- a/absl/random/distributions_test.cc
+++ b/absl/random/distributions_test.cc
@@ -171,14 +171,11 @@ void CheckArgsInferType() {
"");
static_assert(
absl::conjunction<
+ std::is_same<Expect, decltype(InferredTaggedUniformReturnT<
+ absl::IntervalOpenOpenTag, A, B>(0))>,
std::is_same<Expect,
decltype(InferredTaggedUniformReturnT<
- absl::random_internal::IntervalOpenOpenT, A, B>(
- 0))>,
- std::is_same<Expect,
- decltype(InferredTaggedUniformReturnT<
- absl::random_internal::IntervalOpenOpenT, B, A>(
- 0))>>::value,
+ absl::IntervalOpenOpenTag, B, A>(0))>>::value,
"");
}
@@ -218,12 +215,10 @@ void CheckArgsReturnExpectedType() {
absl::conjunction<
std::is_same<Expect,
decltype(ExplicitTaggedUniformReturnT<
- absl::random_internal::IntervalOpenOpenT, A, B,
- Expect>(0))>,
- std::is_same<Expect,
- decltype(ExplicitTaggedUniformReturnT<
- absl::random_internal::IntervalOpenOpenT, B, A,
- Expect>(0))>>::value,
+ absl::IntervalOpenOpenTag, A, B, Expect>(0))>,
+ std::is_same<Expect, decltype(ExplicitTaggedUniformReturnT<
+ absl::IntervalOpenOpenTag, B, A,
+ Expect>(0))>>::value,
"");
}
diff --git a/absl/random/exponential_distribution.h b/absl/random/exponential_distribution.h
index ed5551ae..b5caf8a1 100644
--- a/absl/random/exponential_distribution.h
+++ b/absl/random/exponential_distribution.h
@@ -21,12 +21,13 @@
#include <limits>
#include <type_traits>
-#include "absl/random/internal/distribution_impl.h"
+#include "absl/meta/type_traits.h"
#include "absl/random/internal/fast_uniform_bits.h"
+#include "absl/random/internal/generate_real.h"
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// absl::exponential_distribution:
// Generates a number conforming to an exponential distribution and is
@@ -119,9 +120,14 @@ typename exponential_distribution<RealType>::result_type
exponential_distribution<RealType>::operator()(
URBG& g, // NOLINT(runtime/references)
const param_type& p) {
- using random_internal::NegativeValueT;
- const result_type u = random_internal::RandU64ToReal<
- result_type>::template Value<NegativeValueT, false>(fast_u64_(g));
+ using random_internal::GenerateNegativeTag;
+ using random_internal::GenerateRealFromBits;
+ using real_type =
+ absl::conditional_t<std::is_same<RealType, float>::value, float, double>;
+
+ const result_type u = GenerateRealFromBits<real_type, GenerateNegativeTag,
+ false>(fast_u64_(g)); // U(-1, 0)
+
// log1p(-x) is mathematically equivalent to log(1 - x) but has more
// accuracy for x near zero.
return p.neg_inv_lambda_ * std::log1p(u);
@@ -153,7 +159,7 @@ std::basic_istream<CharT, Traits>& operator>>(
return is;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_
diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc
index 6f8865c2..f3cfd764 100644
--- a/absl/random/exponential_distribution_test.cc
+++ b/absl/random/exponential_distribution_test.cc
@@ -46,7 +46,11 @@ using absl::random_internal::kChiSquared;
template <typename RealType>
class ExponentialDistributionTypedTest : public ::testing::Test {};
+#if defined(__EMSCRIPTEN__)
+using RealTypes = ::testing::Types<float, double>;
+#else
using RealTypes = ::testing::Types<float, double, long double>;
+#endif // defined(__EMSCRIPTEN__)
TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes);
TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) {
@@ -346,7 +350,7 @@ std::string ParamName(const ::testing::TestParamInfo<Param>& info) {
return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
}
-INSTANTIATE_TEST_CASE_P(, ExponentialDistributionTests,
+INSTANTIATE_TEST_CASE_P(All, ExponentialDistributionTests,
::testing::ValuesIn(GenParams()), ParamName);
// NOTE: absl::exponential_distribution is not guaranteed to be stable.
diff --git a/absl/random/gaussian_distribution.cc b/absl/random/gaussian_distribution.cc
index dbc2d848..c7a72cb2 100644
--- a/absl/random/gaussian_distribution.cc
+++ b/absl/random/gaussian_distribution.cc
@@ -4,7 +4,7 @@
#include "absl/random/gaussian_distribution.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
const gaussian_distribution_base::Tables
@@ -97,7 +97,7 @@ const gaussian_distribution_base::Tables
0.9362826816850632339, 0.9635996931270905952, 1}};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// clang-format on
diff --git a/absl/random/gaussian_distribution.h b/absl/random/gaussian_distribution.h
index 8ee95148..4b07a5c0 100644
--- a/absl/random/gaussian_distribution.h
+++ b/absl/random/gaussian_distribution.h
@@ -28,12 +28,13 @@
#include <limits>
#include <type_traits>
-#include "absl/random/internal/distribution_impl.h"
+#include "absl/base/config.h"
#include "absl/random/internal/fast_uniform_bits.h"
+#include "absl/random/internal/generate_real.h"
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// absl::gaussian_distribution_base implements the underlying ziggurat algorithm
@@ -43,7 +44,7 @@ namespace random_internal {
// The specific algorithm has some of the improvements suggested by the
// 2005 paper, "An Improved Ziggurat Method to Generate Normal Random Samples",
// Jurgen A Doornik. (https://www.doornik.com/research/ziggurat.pdf)
-class gaussian_distribution_base {
+class ABSL_DLL gaussian_distribution_base {
public:
template <typename URBG>
inline double zignor(URBG& g); // NOLINT(runtime/references)
@@ -208,12 +209,18 @@ namespace random_internal {
template <typename URBG>
inline double gaussian_distribution_base::zignor_fallback(URBG& g, bool neg) {
+ using random_internal::GeneratePositiveTag;
+ using random_internal::GenerateRealFromBits;
+
// This fallback path happens approximately 0.05% of the time.
double x, y;
do {
// kRInv = 1/r, U(0, 1)
- x = kRInv * std::log(RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)));
- y = -std::log(RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)));
+ x = kRInv *
+ std::log(GenerateRealFromBits<double, GeneratePositiveTag, false>(
+ fast_u64_(g)));
+ y = -std::log(
+ GenerateRealFromBits<double, GeneratePositiveTag, false>(fast_u64_(g)));
} while ((y + y) < (x * x));
return neg ? (x - kR) : (kR - x);
}
@@ -221,6 +228,10 @@ inline double gaussian_distribution_base::zignor_fallback(URBG& g, bool neg) {
template <typename URBG>
inline double gaussian_distribution_base::zignor(
URBG& g) { // NOLINT(runtime/references)
+ using random_internal::GeneratePositiveTag;
+ using random_internal::GenerateRealFromBits;
+ using random_internal::GenerateSignedTag;
+
while (true) {
// We use a single uint64_t to generate both a double and a strip.
// These bits are unused when the generated double is > 1/2^5.
@@ -228,7 +239,8 @@ inline double gaussian_distribution_base::zignor(
// values (those smaller than 1/2^5, which all end up on the left tail).
uint64_t bits = fast_u64_(g);
int i = static_cast<int>(bits & kMask); // pick a random strip
- double j = RandU64ToDouble<SignedValueT, false>(bits); // U(-1, 1)
+ double j = GenerateRealFromBits<double, GenerateSignedTag, false>(
+ bits); // U(-1, 1)
const double x = j * zg_.x[i];
// Retangular box. Handles >97% of all cases.
@@ -245,7 +257,8 @@ inline double gaussian_distribution_base::zignor(
}
// i > 0: Wedge samples using precomputed values.
- double v = RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)); // U(0, 1)
+ double v = GenerateRealFromBits<double, GeneratePositiveTag, false>(
+ fast_u64_(g)); // U(0, 1)
if ((zg_.f[i + 1] + v * (zg_.f[i] - zg_.f[i + 1])) <
std::exp(-0.5 * x * x)) {
return x;
@@ -256,7 +269,7 @@ inline double gaussian_distribution_base::zignor(
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_
diff --git a/absl/random/gaussian_distribution_test.cc b/absl/random/gaussian_distribution_test.cc
index 47c2989d..49c07513 100644
--- a/absl/random/gaussian_distribution_test.cc
+++ b/absl/random/gaussian_distribution_test.cc
@@ -394,7 +394,7 @@ std::string ParamName(const ::testing::TestParamInfo<Param>& info) {
return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
}
-INSTANTIATE_TEST_SUITE_P(, GaussianDistributionTests,
+INSTANTIATE_TEST_SUITE_P(All, GaussianDistributionTests,
::testing::ValuesIn(GenParams()), ParamName);
// NOTE: absl::gaussian_distribution is not guaranteed to be stable.
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel
index fd5471a6..d7ad4efe 100644
--- a/absl/random/internal/BUILD.bazel
+++ b/absl/random/internal/BUILD.bazel
@@ -1,3 +1,21 @@
+#
+# Copyright 2019 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
+
# Internal-only implementation classes for Abseil Random
load(
"//absl:copts/configure_copts.bzl",
@@ -33,24 +51,21 @@ cc_library(
visibility = [
"//absl/random:__pkg__",
],
+ deps = ["//absl/base:config"],
)
cc_library(
name = "distributions",
- hdrs = [
- "distributions.h",
- ],
+ hdrs = ["distributions.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":distribution_caller",
- ":fast_uniform_bits",
- ":fastmath",
":traits",
":uniform_helper",
+ "//absl/base",
"//absl/meta:type_traits",
"//absl/strings",
- "//absl/types:span",
],
)
@@ -64,6 +79,7 @@ cc_library(
visibility = [
"//absl/random:__pkg__",
],
+ deps = ["//absl/base:config"],
)
cc_library(
@@ -75,11 +91,14 @@ cc_library(
"seed_material.h",
],
copts = ABSL_DEFAULT_COPTS,
- linkopts = ABSL_DEFAULT_LINKOPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS + select({
+ "//absl:windows": ["-DEFAULTLIB:bcrypt.lib"],
+ "//conditions:default": [],
+ }),
deps = [
":fast_uniform_bits",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
"//absl/types:optional",
"//absl/types:span",
@@ -107,6 +126,7 @@ cc_library(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
+ "//absl/base:raw_logging_internal",
"//absl/random:seed_gen_exception",
"//absl/types:span",
],
@@ -120,6 +140,7 @@ cc_library(
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = ["//absl/base:config"],
)
cc_library(
@@ -130,6 +151,7 @@ cc_library(
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = ["//absl/base:config"],
)
cc_library(
@@ -160,9 +182,9 @@ cc_library(
)
cc_library(
- name = "distribution_impl",
+ name = "generate_real",
hdrs = [
- "distribution_impl.h",
+ "generate_real.h",
],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
@@ -170,8 +192,7 @@ cc_library(
":fastmath",
":traits",
"//absl/base:bits",
- "//absl/base:config",
- "//absl/numeric:int128",
+ "//absl/meta:type_traits",
],
)
@@ -186,6 +207,19 @@ cc_library(
)
cc_library(
+ name = "wide_multiply",
+ hdrs = ["wide_multiply.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":traits",
+ "//absl/base:bits",
+ "//absl/base:config",
+ "//absl/numeric:int128",
+ ],
+)
+
+cc_library(
name = "nonsecure_base",
hdrs = ["nonsecure_base.h"],
copts = ABSL_DEFAULT_COPTS,
@@ -239,6 +273,7 @@ cc_library(
"randen-keys.inc",
"platform.h",
],
+ deps = ["//absl/base:config"],
)
cc_library(
@@ -255,7 +290,7 @@ cc_library(
":platform",
":randen_hwaes",
":randen_slow",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
],
)
@@ -267,6 +302,8 @@ cc_library(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":platform",
+ "//absl/base:config",
+ "//absl/base:core_headers",
],
)
@@ -286,6 +323,7 @@ cc_library(
deps = [
":platform",
":randen_hwaes_impl",
+ "//absl/base:config",
],
)
@@ -305,7 +343,11 @@ cc_library(
# anyway and thus there wouldn't be any gain from using it as a module.
features = ["-header_modules"],
linkopts = ABSL_DEFAULT_LINKOPTS,
- deps = [":platform"],
+ deps = [
+ ":platform",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ ],
)
cc_binary(
@@ -334,8 +376,9 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- "//absl/base",
+ "//absl/base:config",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
"//absl/strings:str_format",
"//absl/types:span",
@@ -367,16 +410,17 @@ cc_test(
)
cc_test(
- name = "distribution_impl_test",
+ name = "generate_real_test",
size = "small",
- srcs = ["distribution_impl_test.cc"],
+ srcs = [
+ "generate_real_test.cc",
+ ],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":distribution_impl",
+ ":generate_real",
"//absl/base:bits",
"//absl/flags:flag",
- "//absl/numeric:int128",
"@com_google_googletest//:gtest_main",
],
)
@@ -459,6 +503,29 @@ cc_test(
],
)
+cc_library(
+ name = "mocking_bit_gen_base",
+ hdrs = ["mocking_bit_gen_base.h"],
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/random",
+ "//absl/strings",
+ ],
+)
+
+cc_library(
+ name = "mock_overload_set",
+ testonly = 1,
+ hdrs = ["mock_overload_set.h"],
+ visibility = [
+ "//absl/random:__pkg__",
+ ],
+ deps = [
+ "//absl/random:mocking_bit_gen",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
cc_test(
name = "nonsecure_base_test",
size = "small",
@@ -531,7 +598,7 @@ cc_test(
deps = [
":explicit_seed_seq",
":randen_engine",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
"//absl/time",
"@com_google_googletest//:gtest_main",
@@ -574,12 +641,26 @@ cc_test(
":platform",
":randen_hwaes",
":randen_hwaes_impl", # build_cleaner: keep
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/strings:str_format",
"@com_google_googletest//:gtest",
],
)
+cc_test(
+ name = "wide_multiply_test",
+ size = "small",
+ srcs = ["wide_multiply_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":wide_multiply",
+ "//absl/base:bits",
+ "//absl/numeric:int128",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
cc_library(
name = "nanobenchmark",
srcs = ["nanobenchmark.cc"],
@@ -588,7 +669,8 @@ cc_library(
deps = [
":platform",
":randen_engine",
- "//absl/base",
+ "//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
],
)
@@ -598,11 +680,6 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- ":distribution_impl",
- ":fast_uniform_bits",
- ":iostream_state_saver",
- ":traits",
- "//absl/base:core_headers",
"//absl/meta:type_traits",
],
)
@@ -620,7 +697,7 @@ cc_test(
],
deps = [
":nanobenchmark",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
],
)
@@ -641,7 +718,7 @@ cc_test(
":randen_hwaes",
":randen_hwaes_impl",
":randen_slow",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/strings",
],
)
diff --git a/absl/random/internal/chi_square.cc b/absl/random/internal/chi_square.cc
index 45671a3e..640d48ce 100644
--- a/absl/random/internal/chi_square.cc
+++ b/absl/random/internal/chi_square.cc
@@ -19,7 +19,7 @@
#include "absl/random/internal/distribution_test_util.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
namespace {
@@ -228,5 +228,5 @@ double ChiSquarePValue(double chi_square, int dof) {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/chi_square.h b/absl/random/internal/chi_square.h
index 002b4c95..07f4fbe5 100644
--- a/absl/random/internal/chi_square.h
+++ b/absl/random/internal/chi_square.h
@@ -26,8 +26,10 @@
#include <cassert>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
constexpr const char kChiSquared[] = "chi-squared";
@@ -81,7 +83,7 @@ double ChiSquareValue(int dof, double p);
double ChiSquarePValue(double chi_square, int dof);
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
diff --git a/absl/random/internal/distribution_caller.h b/absl/random/internal/distribution_caller.h
index dd06c985..02603cf8 100644
--- a/absl/random/internal/distribution_caller.h
+++ b/absl/random/internal/distribution_caller.h
@@ -19,8 +19,10 @@
#include <utility>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// DistributionCaller provides an opportunity to overload the general
@@ -52,7 +54,7 @@ struct DistributionCaller {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
diff --git a/absl/random/internal/distribution_impl.h b/absl/random/internal/distribution_impl.h
deleted file mode 100644
index a8e5d61e..00000000
--- a/absl/random/internal/distribution_impl.h
+++ /dev/null
@@ -1,262 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_
-#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_
-
-// This file contains some implementation details which are used by one or more
-// of the absl random number distributions.
-
-#include <cfloat>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <limits>
-#include <type_traits>
-
-#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64)
-#include <intrin.h> // NOLINT(build/include_order)
-#pragma intrinsic(_umul128)
-#define ABSL_INTERNAL_USE_UMUL128 1
-#endif
-
-#include "absl/base/config.h"
-#include "absl/base/internal/bits.h"
-#include "absl/numeric/int128.h"
-#include "absl/random/internal/fastmath.h"
-#include "absl/random/internal/traits.h"
-
-namespace absl {
-inline namespace lts_2019_08_08 {
-namespace random_internal {
-
-// Creates a double from `bits`, with the template fields controlling the
-// output.
-//
-// RandU64To is both more efficient and generates more unique values in the
-// result interval than known implementations of std::generate_canonical().
-//
-// The `Signed` parameter controls whether positive, negative, or both are
-// returned (thus affecting the output interval).
-// When Signed == SignedValueT, range is U(-1, 1)
-// When Signed == NegativeValueT, range is U(-1, 0)
-// When Signed == PositiveValueT, range is U(0, 1)
-//
-// When the `IncludeZero` parameter is true, the function may return 0 for some
-// inputs, otherwise it never returns 0.
-//
-// The `ExponentBias` parameter determines the scale of the output range by
-// adjusting the exponent.
-//
-// When a value in U(0,1) is required, use:
-// RandU64ToDouble<PositiveValueT, true, 0>();
-//
-// When a value in U(-1,1) is required, use:
-// RandU64ToDouble<SignedValueT, false, 0>() => U(-1, 1)
-// This generates more distinct values than the mathematically equivalent
-// expression `U(0, 1) * 2.0 - 1.0`, and is preferable.
-//
-// Scaling the result by powers of 2 (and avoiding a multiply) is also possible:
-// RandU64ToDouble<PositiveValueT, false, 1>(); => U(0, 2)
-// RandU64ToDouble<PositiveValueT, false, -1>(); => U(0, 0.5)
-//
-
-// Tristate types controlling the output.
-struct PositiveValueT {};
-struct NegativeValueT {};
-struct SignedValueT {};
-
-// RandU64ToDouble is the double-result variant of RandU64To, described above.
-template <typename Signed, bool IncludeZero, int ExponentBias = 0>
-inline double RandU64ToDouble(uint64_t bits) {
- static_assert(std::is_same<Signed, PositiveValueT>::value ||
- std::is_same<Signed, NegativeValueT>::value ||
- std::is_same<Signed, SignedValueT>::value,
- "");
-
- // Maybe use the left-most bit for a sign bit.
- uint64_t sign = std::is_same<Signed, NegativeValueT>::value
- ? 0x8000000000000000ull
- : 0; // Sign bits.
-
- if (std::is_same<Signed, SignedValueT>::value) {
- sign = bits & 0x8000000000000000ull;
- bits = bits & 0x7FFFFFFFFFFFFFFFull;
- }
- if (IncludeZero) {
- if (bits == 0u) return 0;
- }
-
- // Number of leading zeros is mapped to the exponent: 2^-clz
- int clz = base_internal::CountLeadingZeros64(bits);
- // Shift number left to erase leading zeros.
- bits <<= IncludeZero ? clz : (clz & 63);
-
- // Shift number right to remove bits that overflow double mantissa. The
- // direction of the shift depends on `clz`.
- bits >>= (64 - DBL_MANT_DIG);
-
- // Compute IEEE 754 double exponent.
- // In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the
- // exponent to account for that.
- const uint64_t exp =
- (std::is_same<Signed, SignedValueT>::value ? 1023U : 1022U) +
- static_cast<uint64_t>(ExponentBias - clz);
- constexpr int kExp = DBL_MANT_DIG - 1;
- // Construct IEEE 754 double from exponent and mantissa.
- const uint64_t val = sign | (exp << kExp) | (bits & ((1ULL << kExp) - 1U));
-
- double res;
- static_assert(sizeof(res) == sizeof(val), "double is not 64 bit");
- // Memcpy value from "val" to "res" to avoid aliasing problems. Assumes that
- // endian-ness is same for double and uint64_t.
- std::memcpy(&res, &val, sizeof(res));
-
- return res;
-}
-
-// RandU64ToFloat is the float-result variant of RandU64To, described above.
-template <typename Signed, bool IncludeZero, int ExponentBias = 0>
-inline float RandU64ToFloat(uint64_t bits) {
- static_assert(std::is_same<Signed, PositiveValueT>::value ||
- std::is_same<Signed, NegativeValueT>::value ||
- std::is_same<Signed, SignedValueT>::value,
- "");
-
- // Maybe use the left-most bit for a sign bit.
- uint64_t sign = std::is_same<Signed, NegativeValueT>::value
- ? 0x80000000ul
- : 0; // Sign bits.
-
- if (std::is_same<Signed, SignedValueT>::value) {
- uint64_t a = bits & 0x8000000000000000ull;
- sign = static_cast<uint32_t>(a >> 32);
- bits = bits & 0x7FFFFFFFFFFFFFFFull;
- }
- if (IncludeZero) {
- if (bits == 0u) return 0;
- }
-
- // Number of leading zeros is mapped to the exponent: 2^-clz
- int clz = base_internal::CountLeadingZeros64(bits);
- // Shift number left to erase leading zeros.
- bits <<= IncludeZero ? clz : (clz & 63);
- // Shift number right to remove bits that overflow double mantissa. The
- // direction of the shift depends on `clz`.
- bits >>= (64 - FLT_MANT_DIG);
-
- // Construct IEEE 754 float exponent.
- // In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the
- // exponent to account for that.
- const uint32_t exp =
- (std::is_same<Signed, SignedValueT>::value ? 127U : 126U) +
- static_cast<uint32_t>(ExponentBias - clz);
- constexpr int kExp = FLT_MANT_DIG - 1;
- const uint32_t val = sign | (exp << kExp) | (bits & ((1U << kExp) - 1U));
-
- float res;
- static_assert(sizeof(res) == sizeof(val), "float is not 32 bit");
- // Assumes that endian-ness is same for float and uint32_t.
- std::memcpy(&res, &val, sizeof(res));
-
- return res;
-}
-
-template <typename Result>
-struct RandU64ToReal {
- template <typename Signed, bool IncludeZero, int ExponentBias = 0>
- static inline Result Value(uint64_t bits) {
- return RandU64ToDouble<Signed, IncludeZero, ExponentBias>(bits);
- }
-};
-
-template <>
-struct RandU64ToReal<float> {
- template <typename Signed, bool IncludeZero, int ExponentBias = 0>
- static inline float Value(uint64_t bits) {
- return RandU64ToFloat<Signed, IncludeZero, ExponentBias>(bits);
- }
-};
-
-inline uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) {
-#if defined(ABSL_HAVE_INTRINSIC_INT128)
- return uint128(static_cast<__uint128_t>(a) * b);
-#elif defined(ABSL_INTERNAL_USE_UMUL128)
- // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC.
- uint64_t high = 0;
- const uint64_t low = _umul128(a, b, &high);
- return absl::MakeUint128(high, low);
-#else
- // uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit
- // multiply. However there are many cases where that is not necessary, and it
- // is only necessary to support a 64-bit x 64-bit = 128-bit multiply. This is
- // for those cases.
- const uint64_t a00 = static_cast<uint32_t>(a);
- const uint64_t a32 = a >> 32;
- const uint64_t b00 = static_cast<uint32_t>(b);
- const uint64_t b32 = b >> 32;
-
- const uint64_t c00 = a00 * b00;
- const uint64_t c32a = a00 * b32;
- const uint64_t c32b = a32 * b00;
- const uint64_t c64 = a32 * b32;
-
- const uint32_t carry =
- static_cast<uint32_t>(((c00 >> 32) + static_cast<uint32_t>(c32a) +
- static_cast<uint32_t>(c32b)) >>
- 32);
-
- return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry,
- c00 + (c32a << 32) + (c32b << 32));
-#endif
-}
-
-// wide_multiply<T> multiplies two N-bit values to a 2N-bit result.
-template <typename UIntType>
-struct wide_multiply {
- static constexpr size_t kN = std::numeric_limits<UIntType>::digits;
- using input_type = UIntType;
- using result_type = typename random_internal::unsigned_bits<kN * 2>::type;
-
- static result_type multiply(input_type a, input_type b) {
- return static_cast<result_type>(a) * b;
- }
-
- static input_type hi(result_type r) { return r >> kN; }
- static input_type lo(result_type r) { return r; }
-
- static_assert(std::is_unsigned<UIntType>::value,
- "Class-template wide_multiply<> argument must be unsigned.");
-};
-
-#ifndef ABSL_HAVE_INTRINSIC_INT128
-template <>
-struct wide_multiply<uint64_t> {
- using input_type = uint64_t;
- using result_type = uint128;
-
- static result_type multiply(uint64_t a, uint64_t b) {
- return MultiplyU64ToU128(a, b);
- }
-
- static uint64_t hi(result_type r) { return Uint128High64(r); }
- static uint64_t lo(result_type r) { return Uint128Low64(r); }
-};
-#endif
-
-} // namespace random_internal
-} // inline namespace lts_2019_08_08
-} // namespace absl
-
-#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_
diff --git a/absl/random/internal/distribution_test_util.cc b/absl/random/internal/distribution_test_util.cc
index 4fb4149d..e9005658 100644
--- a/absl/random/internal/distribution_test_util.cc
+++ b/absl/random/internal/distribution_test_util.cc
@@ -25,7 +25,7 @@
#include "absl/strings/str_format.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
namespace {
@@ -414,5 +414,5 @@ double MaxErrorTolerance(double acceptance_probability) {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/distribution_test_util.h b/absl/random/internal/distribution_test_util.h
index 56dc86ac..6d94cf6c 100644
--- a/absl/random/internal/distribution_test_util.h
+++ b/absl/random/internal/distribution_test_util.h
@@ -26,7 +26,7 @@
// non-test code.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// http://webspace.ship.edu/pgmarr/Geo441/Lectures/Lec%205%20-%20Normality%20Testing.pdf
@@ -107,7 +107,7 @@ double BetaIncomplete(double x, double p, double q);
double BetaIncompleteInv(double p, double q, double alpha);
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
diff --git a/absl/random/internal/distributions.h b/absl/random/internal/distributions.h
index 98d4f313..d7e3c016 100644
--- a/absl/random/internal/distributions.h
+++ b/absl/random/internal/distributions.h
@@ -23,40 +23,8 @@
#include "absl/random/internal/uniform_helper.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
-template <typename D>
-struct DistributionFormatTraits;
-
-// UniformImpl implements the core logic of the Uniform<T> call, which is to
-// select the correct distribution type, compute the bounds based on the
-// interval tag, and then generate a value.
-template <typename NumType, typename TagType, typename URBG>
-NumType UniformImpl(TagType tag,
- URBG& urbg, // NOLINT(runtime/references)
- NumType lo, NumType hi) {
- static_assert(
- std::is_arithmetic<NumType>::value,
- "absl::Uniform<T>() must use an integer or real parameter type.");
-
- using distribution_t =
- typename std::conditional<std::is_integral<NumType>::value,
- absl::uniform_int_distribution<NumType>,
- absl::uniform_real_distribution<NumType>>::type;
- using format_t = random_internal::DistributionFormatTraits<distribution_t>;
-
- auto a = random_internal::uniform_lower_bound<NumType>(tag, lo, hi);
- auto b = random_internal::uniform_upper_bound<NumType>(tag, lo, hi);
- // TODO(lar): it doesn't make a lot of sense to ask for a random number in an
- // empty range. Right now we just return a boundary--even though that
- // boundary is not an acceptable value! Is there something better we can do
- // here?
-
- using gen_t = absl::decay_t<URBG>;
- if (a > b) return a;
- return DistributionCaller<gen_t>::template Call<distribution_t, format_t>(
- &urbg, a, b);
-}
// In the absence of an explicitly provided return-type, the template
// "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on
@@ -78,7 +46,7 @@ using uniform_inferred_return_t =
is_widening_convertible<A, B>::value, B, A>::type>;
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_
diff --git a/absl/random/internal/explicit_seed_seq.h b/absl/random/internal/explicit_seed_seq.h
index 66c35f2e..6a743eaf 100644
--- a/absl/random/internal/explicit_seed_seq.h
+++ b/absl/random/internal/explicit_seed_seq.h
@@ -22,8 +22,10 @@
#include <iterator>
#include <vector>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// This class conforms to the C++ Standard "Seed Sequence" concept
@@ -83,7 +85,7 @@ class ExplicitSeedSeq {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
diff --git a/absl/random/internal/fast_uniform_bits.h b/absl/random/internal/fast_uniform_bits.h
index 5aa9de01..f13c8729 100644
--- a/absl/random/internal/fast_uniform_bits.h
+++ b/absl/random/internal/fast_uniform_bits.h
@@ -20,8 +20,10 @@
#include <limits>
#include <type_traits>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// Returns true if the input value is zero or a power of two. Useful for
// determining if the range of output values in a URBG
@@ -256,7 +258,7 @@ FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references)
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_
diff --git a/absl/random/internal/fast_uniform_bits_test.cc b/absl/random/internal/fast_uniform_bits_test.cc
index 35c96226..f5b837e5 100644
--- a/absl/random/internal/fast_uniform_bits_test.cc
+++ b/absl/random/internal/fast_uniform_bits_test.cc
@@ -19,7 +19,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
namespace {
@@ -270,5 +270,5 @@ TEST(FastUniformBitsTest, URBG32bitRegression) {
} // namespace
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/fastmath.h b/absl/random/internal/fastmath.h
index 1dc2cd7b..6baeb5a7 100644
--- a/absl/random/internal/fastmath.h
+++ b/absl/random/internal/fastmath.h
@@ -25,7 +25,7 @@
#include "absl/base/internal/bits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// Returns the position of the first bit set.
@@ -68,7 +68,7 @@ inline constexpr uint64_t rotr(uint64_t value, uint8_t bits) {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_FASTMATH_H_
diff --git a/absl/random/internal/gaussian_distribution_gentables.cc b/absl/random/internal/gaussian_distribution_gentables.cc
index 6f9a28be..a2bf0394 100644
--- a/absl/random/internal/gaussian_distribution_gentables.cc
+++ b/absl/random/internal/gaussian_distribution_gentables.cc
@@ -27,7 +27,7 @@
#include "absl/base/macros.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
namespace {
@@ -136,7 +136,7 @@ void TableGenerator::Print(std::ostream* os) {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
int main(int, char**) {
diff --git a/absl/random/internal/generate_real.h b/absl/random/internal/generate_real.h
new file mode 100644
index 00000000..20f6d208
--- /dev/null
+++ b/absl/random/internal/generate_real.h
@@ -0,0 +1,146 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
+#define ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
+
+// This file contains some implementation details which are used by one or more
+// of the absl random number distributions.
+
+#include <cstdint>
+#include <cstring>
+#include <limits>
+#include <type_traits>
+
+#include "absl/base/internal/bits.h"
+#include "absl/meta/type_traits.h"
+#include "absl/random/internal/fastmath.h"
+#include "absl/random/internal/traits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace random_internal {
+
+// Tristate tag types controlling the output of GenerateRealFromBits.
+struct GeneratePositiveTag {};
+struct GenerateNegativeTag {};
+struct GenerateSignedTag {};
+
+// GenerateRealFromBits generates a single real value from a single 64-bit
+// `bits` with template fields controlling the output.
+//
+// The `SignedTag` parameter controls whether positive, negative,
+// or either signed/unsigned may be returned.
+// When SignedTag == GeneratePositiveTag, range is U(0, 1)
+// When SignedTag == GenerateNegativeTag, range is U(-1, 0)
+// When SignedTag == GenerateSignedTag, range is U(-1, 1)
+//
+// When the `IncludeZero` parameter is true, the function may return 0 for some
+// inputs, otherwise it never returns 0.
+//
+// When a value in U(0,1) is required, use:
+// Uniform64ToReal<double, PositiveValueT, true>;
+//
+// When a value in U(-1,1) is required, use:
+// Uniform64ToReal<double, SignedValueT, false>;
+//
+// This generates more distinct values than the mathematical equivalent
+// `U(0, 1) * 2.0 - 1.0`.
+//
+// Scaling the result by powers of 2 (and avoiding a multiply) is also possible:
+// GenerateRealFromBits<double>(..., -1); => U(0, 0.5)
+// GenerateRealFromBits<double>(..., 1); => U(0, 2)
+//
+template <typename RealType, // Real type, either float or double.
+ typename SignedTag = GeneratePositiveTag, // Whether a positive,
+ // negative, or signed
+ // value is generated.
+ bool IncludeZero = true>
+inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) {
+ using real_type = RealType;
+ using uint_type = absl::conditional_t<std::is_same<real_type, float>::value,
+ uint32_t, uint64_t>;
+
+ static_assert(
+ (std::is_same<double, real_type>::value ||
+ std::is_same<float, real_type>::value),
+ "GenerateRealFromBits must be parameterized by either float or double.");
+
+ static_assert(sizeof(uint_type) == sizeof(real_type),
+ "Mismatched unsinged and real types.");
+
+ static_assert((std::numeric_limits<real_type>::is_iec559 &&
+ std::numeric_limits<real_type>::radix == 2),
+ "RealType representation is not IEEE 754 binary.");
+
+ static_assert((std::is_same<SignedTag, GeneratePositiveTag>::value ||
+ std::is_same<SignedTag, GenerateNegativeTag>::value ||
+ std::is_same<SignedTag, GenerateSignedTag>::value),
+ "");
+
+ static constexpr int kExp = std::numeric_limits<real_type>::digits - 1;
+ static constexpr uint_type kMask = (static_cast<uint_type>(1) << kExp) - 1u;
+ static constexpr int kUintBits = sizeof(uint_type) * 8;
+
+ int exp = exp_bias + int{std::numeric_limits<real_type>::max_exponent - 2};
+
+ // Determine the sign bit.
+ // Depending on the SignedTag, this may use the left-most bit
+ // or it may be a constant value.
+ uint_type sign = std::is_same<SignedTag, GenerateNegativeTag>::value
+ ? (static_cast<uint_type>(1) << (kUintBits - 1))
+ : 0;
+ if (std::is_same<SignedTag, GenerateSignedTag>::value) {
+ if (std::is_same<uint_type, uint64_t>::value) {
+ sign = bits & uint64_t{0x8000000000000000};
+ }
+ if (std::is_same<uint_type, uint32_t>::value) {
+ const uint64_t tmp = bits & uint64_t{0x8000000000000000};
+ sign = static_cast<uint32_t>(tmp >> 32);
+ }
+ // adjust the bits and the exponent to account for removing
+ // the leading bit.
+ bits = bits & uint64_t{0x7FFFFFFFFFFFFFFF};
+ exp++;
+ }
+ if (IncludeZero) {
+ if (bits == 0u) return 0;
+ }
+
+ // Number of leading zeros is mapped to the exponent: 2^-clz
+ // bits is 0..01xxxxxx. After shifting, we're left with 1xxx...0..0
+ int clz = base_internal::CountLeadingZeros64(bits);
+ bits <<= (IncludeZero ? clz : (clz & 63)); // remove 0-bits.
+ exp -= clz; // set the exponent.
+ bits >>= (63 - kExp);
+
+ // Construct the 32-bit or 64-bit IEEE 754 floating-point value from
+ // the individual fields: sign, exp, mantissa(bits).
+ uint_type val =
+ (std::is_same<SignedTag, GeneratePositiveTag>::value ? 0u : sign) |
+ (static_cast<uint_type>(exp) << kExp) |
+ (static_cast<uint_type>(bits) & kMask);
+
+ // bit_cast to the output-type
+ real_type result;
+ memcpy(static_cast<void*>(&result), static_cast<const void*>(&val),
+ sizeof(result));
+ return result;
+}
+
+} // namespace random_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
diff --git a/absl/random/internal/distribution_impl_test.cc b/absl/random/internal/generate_real_test.cc
index 09e7a318..aa02f0c2 100644
--- a/absl/random/internal/distribution_impl_test.cc
+++ b/absl/random/internal/generate_real_test.cc
@@ -12,57 +12,74 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "absl/random/internal/distribution_impl.h"
+#include "absl/random/internal/generate_real.h"
+
+#include <cfloat>
+#include <cstddef>
+#include <cstdint>
+#include <string>
#include "gtest/gtest.h"
#include "absl/base/internal/bits.h"
#include "absl/flags/flag.h"
-#include "absl/numeric/int128.h"
ABSL_FLAG(int64_t, absl_random_test_trials, 50000,
"Number of trials for the probability tests.");
-using absl::random_internal::NegativeValueT;
-using absl::random_internal::PositiveValueT;
-using absl::random_internal::RandU64ToDouble;
-using absl::random_internal::RandU64ToFloat;
-using absl::random_internal::SignedValueT;
+using absl::random_internal::GenerateNegativeTag;
+using absl::random_internal::GeneratePositiveTag;
+using absl::random_internal::GenerateRealFromBits;
+using absl::random_internal::GenerateSignedTag;
namespace {
-TEST(DistributionImplTest, U64ToFloat_Positive_NoZero_Test) {
+TEST(GenerateRealTest, U64ToFloat_Positive_NoZero_Test) {
auto ToFloat = [](uint64_t a) {
- return RandU64ToFloat<PositiveValueT, false>(a);
+ return GenerateRealFromBits<float, GeneratePositiveTag, false>(a);
};
EXPECT_EQ(ToFloat(0x0000000000000000), 2.710505431e-20f);
EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f);
EXPECT_EQ(ToFloat(0x8000000000000000), 0.5);
+ EXPECT_EQ(ToFloat(0x8000000000000001), 0.5);
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f);
}
-TEST(DistributionImplTest, U64ToFloat_Positive_Zero_Test) {
+TEST(GenerateRealTest, U64ToFloat_Positive_Zero_Test) {
auto ToFloat = [](uint64_t a) {
- return RandU64ToFloat<PositiveValueT, true>(a);
+ return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
};
EXPECT_EQ(ToFloat(0x0000000000000000), 0.0);
EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f);
EXPECT_EQ(ToFloat(0x8000000000000000), 0.5);
+ EXPECT_EQ(ToFloat(0x8000000000000001), 0.5);
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f);
}
-TEST(DistributionImplTest, U64ToFloat_Negative_NoZero_Test) {
+TEST(GenerateRealTest, U64ToFloat_Negative_NoZero_Test) {
auto ToFloat = [](uint64_t a) {
- return RandU64ToFloat<NegativeValueT, false>(a);
+ return GenerateRealFromBits<float, GenerateNegativeTag, false>(a);
};
EXPECT_EQ(ToFloat(0x0000000000000000), -2.710505431e-20f);
EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f);
EXPECT_EQ(ToFloat(0x8000000000000000), -0.5);
+ EXPECT_EQ(ToFloat(0x8000000000000001), -0.5);
+ EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
+}
+
+TEST(GenerateRealTest, U64ToFloat_Negative_Zero_Test) {
+ auto ToFloat = [](uint64_t a) {
+ return GenerateRealFromBits<float, GenerateNegativeTag, true>(a);
+ };
+ EXPECT_EQ(ToFloat(0x0000000000000000), 0.0);
+ EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f);
+ EXPECT_EQ(ToFloat(0x8000000000000000), -0.5);
+ EXPECT_EQ(ToFloat(0x8000000000000001), -0.5);
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
}
-TEST(DistributionImplTest, U64ToFloat_Signed_NoZero_Test) {
+TEST(GenerateRealTest, U64ToFloat_Signed_NoZero_Test) {
auto ToFloat = [](uint64_t a) {
- return RandU64ToFloat<SignedValueT, false>(a);
+ return GenerateRealFromBits<float, GenerateSignedTag, false>(a);
};
EXPECT_EQ(ToFloat(0x0000000000000000), 5.421010862e-20f);
EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f);
@@ -72,9 +89,9 @@ TEST(DistributionImplTest, U64ToFloat_Signed_NoZero_Test) {
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
}
-TEST(DistributionImplTest, U64ToFloat_Signed_Zero_Test) {
+TEST(GenerateRealTest, U64ToFloat_Signed_Zero_Test) {
auto ToFloat = [](uint64_t a) {
- return RandU64ToFloat<SignedValueT, true>(a);
+ return GenerateRealFromBits<float, GenerateSignedTag, true>(a);
};
EXPECT_EQ(ToFloat(0x0000000000000000), 0);
EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f);
@@ -84,9 +101,9 @@ TEST(DistributionImplTest, U64ToFloat_Signed_Zero_Test) {
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f);
}
-TEST(DistributionImplTest, U64ToFloat_Signed_Bias_Test) {
+TEST(GenerateRealTest, U64ToFloat_Signed_Bias_Test) {
auto ToFloat = [](uint64_t a) {
- return RandU64ToFloat<SignedValueT, true, 1>(a);
+ return GenerateRealFromBits<float, GenerateSignedTag, true>(a, 1);
};
EXPECT_EQ(ToFloat(0x0000000000000000), 0);
EXPECT_EQ(ToFloat(0x0000000000000001), 2 * 1.084202172e-19f);
@@ -96,9 +113,9 @@ TEST(DistributionImplTest, U64ToFloat_Signed_Bias_Test) {
EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 2 * -0.9999999404f);
}
-TEST(DistributionImplTest, U64ToFloatTest) {
+TEST(GenerateRealTest, U64ToFloatTest) {
auto ToFloat = [](uint64_t a) -> float {
- return RandU64ToFloat<PositiveValueT, true>(a);
+ return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
};
EXPECT_EQ(ToFloat(0x0000000000000000), 0.0f);
@@ -150,44 +167,60 @@ TEST(DistributionImplTest, U64ToFloatTest) {
}
}
-TEST(DistributionImplTest, U64ToDouble_Positive_NoZero_Test) {
+TEST(GenerateRealTest, U64ToDouble_Positive_NoZero_Test) {
auto ToDouble = [](uint64_t a) {
- return RandU64ToDouble<PositiveValueT, false>(a);
+ return GenerateRealFromBits<double, GeneratePositiveTag, false>(a);
};
EXPECT_EQ(ToDouble(0x0000000000000000), 2.710505431213761085e-20);
EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20);
EXPECT_EQ(ToDouble(0x0000000000000002), 1.084202172485504434e-19);
EXPECT_EQ(ToDouble(0x8000000000000000), 0.5);
+ EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978);
}
-TEST(DistributionImplTest, U64ToDouble_Positive_Zero_Test) {
+TEST(GenerateRealTest, U64ToDouble_Positive_Zero_Test) {
auto ToDouble = [](uint64_t a) {
- return RandU64ToDouble<PositiveValueT, true>(a);
+ return GenerateRealFromBits<double, GeneratePositiveTag, true>(a);
};
EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20);
EXPECT_EQ(ToDouble(0x8000000000000000), 0.5);
+ EXPECT_EQ(ToDouble(0x8000000000000001), 0.5);
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978);
}
-TEST(DistributionImplTest, U64ToDouble_Negative_NoZero_Test) {
+TEST(GenerateRealTest, U64ToDouble_Negative_NoZero_Test) {
auto ToDouble = [](uint64_t a) {
- return RandU64ToDouble<NegativeValueT, false>(a);
+ return GenerateRealFromBits<double, GenerateNegativeTag, false>(a);
};
EXPECT_EQ(ToDouble(0x0000000000000000), -2.710505431213761085e-20);
EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20);
EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19);
EXPECT_EQ(ToDouble(0x8000000000000000), -0.5);
+ EXPECT_EQ(ToDouble(0x8000000000000001), -0.5);
+ EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
+}
+
+TEST(GenerateRealTest, U64ToDouble_Negative_Zero_Test) {
+ auto ToDouble = [](uint64_t a) {
+ return GenerateRealFromBits<double, GenerateNegativeTag, true>(a);
+ };
+
+ EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
+ EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20);
+ EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19);
+ EXPECT_EQ(ToDouble(0x8000000000000000), -0.5);
+ EXPECT_EQ(ToDouble(0x8000000000000001), -0.5);
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
}
-TEST(DistributionImplTest, U64ToDouble_Signed_NoZero_Test) {
+TEST(GenerateRealTest, U64ToDouble_Signed_NoZero_Test) {
auto ToDouble = [](uint64_t a) {
- return RandU64ToDouble<SignedValueT, false>(a);
+ return GenerateRealFromBits<double, GenerateSignedTag, false>(a);
};
EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20);
@@ -198,9 +231,9 @@ TEST(DistributionImplTest, U64ToDouble_Signed_NoZero_Test) {
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
}
-TEST(DistributionImplTest, U64ToDouble_Signed_Zero_Test) {
+TEST(GenerateRealTest, U64ToDouble_Signed_Zero_Test) {
auto ToDouble = [](uint64_t a) {
- return RandU64ToDouble<SignedValueT, true>(a);
+ return GenerateRealFromBits<double, GenerateSignedTag, true>(a);
};
EXPECT_EQ(ToDouble(0x0000000000000000), 0);
EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19);
@@ -210,9 +243,9 @@ TEST(DistributionImplTest, U64ToDouble_Signed_Zero_Test) {
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978);
}
-TEST(DistributionImplTest, U64ToDouble_Signed_Bias_Test) {
+TEST(GenerateRealTest, U64ToDouble_GenerateSignedTag_Bias_Test) {
auto ToDouble = [](uint64_t a) {
- return RandU64ToDouble<SignedValueT, true, -1>(a);
+ return GenerateRealFromBits<double, GenerateSignedTag, true>(a, -1);
};
EXPECT_EQ(ToDouble(0x0000000000000000), 0);
EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19 / 2);
@@ -222,9 +255,9 @@ TEST(DistributionImplTest, U64ToDouble_Signed_Bias_Test) {
EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978 / 2);
}
-TEST(DistributionImplTest, U64ToDoubleTest) {
+TEST(GenerateRealTest, U64ToDoubleTest) {
auto ToDouble = [](uint64_t a) {
- return RandU64ToDouble<PositiveValueT, true>(a);
+ return GenerateRealFromBits<double, GeneratePositiveTag, true>(a);
};
EXPECT_EQ(ToDouble(0x0000000000000000), 0.0);
@@ -296,9 +329,9 @@ TEST(DistributionImplTest, U64ToDoubleTest) {
}
}
-TEST(DistributionImplTest, U64ToDoubleSignedTest) {
+TEST(GenerateRealTest, U64ToDoubleSignedTest) {
auto ToDouble = [](uint64_t a) {
- return RandU64ToDouble<SignedValueT, false>(a);
+ return GenerateRealFromBits<double, GenerateSignedTag, false>(a);
};
EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20);
@@ -379,10 +412,10 @@ TEST(DistributionImplTest, U64ToDoubleSignedTest) {
}
}
-TEST(DistributionImplTest, ExhaustiveFloat) {
+TEST(GenerateRealTest, ExhaustiveFloat) {
using absl::base_internal::CountLeadingZeros64;
auto ToFloat = [](uint64_t a) {
- return RandU64ToFloat<PositiveValueT, true>(a);
+ return GenerateRealFromBits<float, GeneratePositiveTag, true>(a);
};
// Rely on RandU64ToFloat generating values from greatest to least when
@@ -461,46 +494,4 @@ TEST(DistributionImplTest, ExhaustiveFloat) {
}
}
-TEST(DistributionImplTest, MultiplyU64ToU128Test) {
- using absl::random_internal::MultiplyU64ToU128;
- constexpr uint64_t k1 = 1;
- constexpr uint64_t kMax = ~static_cast<uint64_t>(0);
-
- EXPECT_EQ(absl::uint128(0), MultiplyU64ToU128(0, 0));
-
- // Max uint64
- EXPECT_EQ(MultiplyU64ToU128(kMax, kMax),
- absl::MakeUint128(0xfffffffffffffffe, 0x0000000000000001));
- EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(kMax, 1));
- EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(1, kMax));
- for (int i = 0; i < 64; ++i) {
- EXPECT_EQ(absl::MakeUint128(0, kMax) << i,
- MultiplyU64ToU128(kMax, k1 << i));
- EXPECT_EQ(absl::MakeUint128(0, kMax) << i,
- MultiplyU64ToU128(k1 << i, kMax));
- }
-
- // 1-bit x 1-bit.
- for (int i = 0; i < 64; ++i) {
- for (int j = 0; j < 64; ++j) {
- EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j),
- MultiplyU64ToU128(k1 << i, k1 << j));
- EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j),
- MultiplyU64ToU128(k1 << i, k1 << j));
- }
- }
-
- // Verified multiplies
- EXPECT_EQ(MultiplyU64ToU128(0xffffeeeeddddcccc, 0xbbbbaaaa99998888),
- absl::MakeUint128(0xbbbb9e2692c5dddc, 0xc28f7531048d2c60));
- EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfedcba9876543210),
- absl::MakeUint128(0x0121fa00ad77d742, 0x2236d88fe5618cf0));
- EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfdb97531eca86420),
- absl::MakeUint128(0x0120ae99d26725fc, 0xce197f0ecac319e0));
- EXPECT_EQ(MultiplyU64ToU128(0x97a87f4f261ba3f2, 0xfedcba9876543210),
- absl::MakeUint128(0x96fbf1a8ae78d0ba, 0x5a6dd4b71f278320));
- EXPECT_EQ(MultiplyU64ToU128(0xfedcba9876543210, 0xfdb97531eca86420),
- absl::MakeUint128(0xfc98c6981a413e22, 0x342d0bbf48948200));
-}
-
} // namespace
diff --git a/absl/random/internal/iostream_state_saver.h b/absl/random/internal/iostream_state_saver.h
index b9adca86..7378829a 100644
--- a/absl/random/internal/iostream_state_saver.h
+++ b/absl/random/internal/iostream_state_saver.h
@@ -24,7 +24,7 @@
#include "absl/numeric/int128.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// The null_state_saver does nothing.
@@ -239,7 +239,7 @@ inline FloatType read_floating_point(IStream& is) {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_
diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc
index 2ecbaac1..7bb8ad95 100644
--- a/absl/random/internal/iostream_state_saver_test.cc
+++ b/absl/random/internal/iostream_state_saver_test.cc
@@ -196,8 +196,8 @@ TEST(IOStreamStateSaver, RoundTripFloats) {
EXPECT_EQ(-d, StreamRoundTrip<double>(-d));
// Avoid undefined behavior (overflow/underflow).
- if (d <= std::numeric_limits<int64_t>::max() &&
- d >= std::numeric_limits<int64_t>::lowest()) {
+ if (f <= static_cast<float>(std::numeric_limits<int64_t>::max()) &&
+ f >= static_cast<float>(std::numeric_limits<int64_t>::lowest())) {
int64_t x = static_cast<int64_t>(f);
EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
}
@@ -264,14 +264,15 @@ TEST(IOStreamStateSaver, RoundTripDoubles) {
}
// Avoid undefined behavior (overflow/underflow).
- if (d <= std::numeric_limits<int64_t>::max() &&
- d >= std::numeric_limits<int64_t>::lowest()) {
+ if (d <= static_cast<double>(std::numeric_limits<int64_t>::max()) &&
+ d >= static_cast<double>(std::numeric_limits<int64_t>::lowest())) {
int64_t x = static_cast<int64_t>(d);
EXPECT_EQ(x, StreamRoundTrip<int64_t>(x));
}
}
}
+#if !defined(__EMSCRIPTEN__)
TEST(IOStreamStateSaver, RoundTripLongDoubles) {
// Technically, C++ only guarantees that long double is at least as large as a
// double. Practically it varies from 64-bits to 128-bits.
@@ -349,6 +350,7 @@ TEST(IOStreamStateSaver, RoundTripLongDoubles) {
}
}
}
+#endif // !defined(__EMSCRIPTEN__)
TEST(StrToDTest, DoubleMin) {
const char kV[] = "2.22507385850720138e-308";
diff --git a/absl/random/internal/mock_overload_set.h b/absl/random/internal/mock_overload_set.h
new file mode 100644
index 00000000..c2a30d89
--- /dev/null
+++ b/absl/random/internal/mock_overload_set.h
@@ -0,0 +1,91 @@
+//
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_
+#define ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_
+
+#include <type_traits>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/random/mocking_bit_gen.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace random_internal {
+
+template <typename DistrT, typename Fn>
+struct MockSingleOverload;
+
+// MockSingleOverload
+//
+// MockSingleOverload hooks in to gMock's `ON_CALL` and `EXPECT_CALL` macros.
+// EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to
+// `mock_single_overload.gmock_Call(...)`. Because expectations are stored on
+// the MockingBitGen (an argument passed inside `Call(...)`), this forwards to
+// arguments to Mocking::Register.
+template <typename DistrT, typename Ret, typename... Args>
+struct MockSingleOverload<DistrT, Ret(MockingBitGen&, Args...)> {
+ static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
+ "Overload signature must have return type matching the "
+ "distributions result type.");
+ auto gmock_Call(
+ absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
+ const ::testing::Matcher<Args>&... args)
+ -> decltype(gen.Register<DistrT, Args...>(args...)) {
+ return gen.Register<DistrT, Args...>(args...);
+ }
+};
+
+template <typename DistrT, typename Ret, typename Arg, typename... Args>
+struct MockSingleOverload<DistrT, Ret(Arg, MockingBitGen&, Args...)> {
+ static_assert(std::is_same<typename DistrT::result_type, Ret>::value,
+ "Overload signature must have return type matching the "
+ "distributions result type.");
+ auto gmock_Call(
+ const ::testing::Matcher<Arg>& arg,
+ absl::MockingBitGen& gen, // NOLINT(google-runtime-references)
+ const ::testing::Matcher<Args>&... args)
+ -> decltype(gen.Register<DistrT, Arg, Args...>(arg, args...)) {
+ return gen.Register<DistrT, Arg, Args...>(arg, args...);
+ }
+};
+
+// MockOverloadSet
+//
+// MockOverloadSet takes a distribution and a collection of signatures and
+// performs overload resolution amongst all the overloads. This makes
+// `EXPECT_CALL(mock_overload_set, Call(...))` expand and do overload resolution
+// correctly.
+template <typename DistrT, typename... Signatures>
+struct MockOverloadSet;
+
+template <typename DistrT, typename Sig>
+struct MockOverloadSet<DistrT, Sig> : public MockSingleOverload<DistrT, Sig> {
+ using MockSingleOverload<DistrT, Sig>::gmock_Call;
+};
+
+template <typename DistrT, typename FirstSig, typename... Rest>
+struct MockOverloadSet<DistrT, FirstSig, Rest...>
+ : public MockSingleOverload<DistrT, FirstSig>,
+ public MockOverloadSet<DistrT, Rest...> {
+ using MockSingleOverload<DistrT, FirstSig>::gmock_Call;
+ using MockOverloadSet<DistrT, Rest...>::gmock_Call;
+};
+
+} // namespace random_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+#endif // ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_
diff --git a/absl/random/internal/mocking_bit_gen_base.h b/absl/random/internal/mocking_bit_gen_base.h
new file mode 100644
index 00000000..eeeae9d2
--- /dev/null
+++ b/absl/random/internal/mocking_bit_gen_base.h
@@ -0,0 +1,120 @@
+//
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_
+#define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_
+
+#include <atomic>
+#include <deque>
+#include <string>
+#include <typeinfo>
+
+#include "absl/random/random.h"
+#include "absl/strings/str_cat.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace random_internal {
+
+// MockingBitGenExpectationFormatter is invoked to format unsatisfied mocks
+// and remaining results into a description string.
+template <typename DistrT, typename FormatT>
+struct MockingBitGenExpectationFormatter {
+ std::string operator()(absl::string_view args) {
+ return absl::StrCat(FormatT::FunctionName(), "(", args, ")");
+ }
+};
+
+// MockingBitGenCallFormatter is invoked to format each distribution call
+// into a description string for the mock log.
+template <typename DistrT, typename FormatT>
+struct MockingBitGenCallFormatter {
+ std::string operator()(const DistrT& dist,
+ const typename DistrT::result_type& result) {
+ return absl::StrCat(
+ FormatT::FunctionName(), "(", FormatT::FormatArgs(dist), ") => {",
+ FormatT::FormatResults(absl::MakeSpan(&result, 1)), "}");
+ }
+};
+
+class MockingBitGenBase {
+ template <typename>
+ friend struct DistributionCaller;
+ using generator_type = absl::BitGen;
+
+ public:
+ // URBG interface
+ using result_type = generator_type::result_type;
+ static constexpr result_type(min)() { return (generator_type::min)(); }
+ static constexpr result_type(max)() { return (generator_type::max)(); }
+ result_type operator()() { return gen_(); }
+
+ MockingBitGenBase() : gen_(), observed_call_log_() {}
+ virtual ~MockingBitGenBase() = default;
+
+ protected:
+ const std::deque<std::string>& observed_call_log() {
+ return observed_call_log_;
+ }
+
+ // CallImpl is the type-erased virtual dispatch.
+ // The type of dist is always distribution<T>,
+ // The type of result is always distribution<T>::result_type.
+ virtual bool CallImpl(const std::type_info& distr_type, void* dist_args,
+ void* result) = 0;
+
+ template <typename DistrT, typename ArgTupleT>
+ static const std::type_info& GetTypeId() {
+ return typeid(std::pair<absl::decay_t<DistrT>, absl::decay_t<ArgTupleT>>);
+ }
+
+ // Call the generating distribution function.
+ // Invoked by DistributionCaller<>::Call<DistT, FormatT>.
+ // DistT is the distribution type.
+ // FormatT is the distribution formatter traits type.
+ template <typename DistrT, typename FormatT, typename... Args>
+ typename DistrT::result_type Call(Args&&... args) {
+ using distr_result_type = typename DistrT::result_type;
+ using ArgTupleT = std::tuple<absl::decay_t<Args>...>;
+
+ ArgTupleT arg_tuple(std::forward<Args>(args)...);
+ auto dist = absl::make_from_tuple<DistrT>(arg_tuple);
+
+ distr_result_type result{};
+ bool found_match =
+ CallImpl(GetTypeId<DistrT, ArgTupleT>(), &arg_tuple, &result);
+
+ if (!found_match) {
+ result = dist(gen_);
+ }
+
+ // TODO(asoffer): Forwarding the args through means we no longer need to
+ // extract them from the from the distribution in formatter traits. We can
+ // just StrJoin them.
+ observed_call_log_.push_back(
+ MockingBitGenCallFormatter<DistrT, FormatT>{}(dist, result));
+ return result;
+ }
+
+ private:
+ generator_type gen_;
+ std::deque<std::string> observed_call_log_;
+}; // namespace random_internal
+
+} // namespace random_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_
diff --git a/absl/random/internal/nanobenchmark.cc b/absl/random/internal/nanobenchmark.cc
index 4d26469b..8fee77fc 100644
--- a/absl/random/internal/nanobenchmark.cc
+++ b/absl/random/internal/nanobenchmark.cc
@@ -27,6 +27,7 @@
#include <utility>
#include <vector>
+#include "absl/base/attributes.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/random/internal/platform.h"
#include "absl/random/internal/randen_engine.h"
@@ -59,15 +60,6 @@
#include <time.h> // NOLINT
#endif
-// ABSL_HAVE_ATTRIBUTE
-#if !defined(ABSL_HAVE_ATTRIBUTE)
-#ifdef __has_attribute
-#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x)
-#else
-#define ABSL_HAVE_ATTRIBUTE(x) 0
-#endif
-#endif
-
// ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE prevents inlining of the method.
#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__))
#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __attribute__((noinline))
@@ -78,7 +70,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal_nanobenchmark {
namespace {
@@ -808,5 +800,5 @@ size_t Measure(const Func func, const void* arg, const FuncInput* inputs,
}
} // namespace random_internal_nanobenchmark
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/nanobenchmark.h b/absl/random/internal/nanobenchmark.h
index 424d2ba2..a5097ba2 100644
--- a/absl/random/internal/nanobenchmark.h
+++ b/absl/random/internal/nanobenchmark.h
@@ -50,8 +50,10 @@
#include <stddef.h>
#include <stdint.h>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal_nanobenchmark {
// Input influencing the function being measured (e.g. number of bytes to copy).
@@ -164,7 +166,7 @@ static inline size_t MeasureClosure(const Closure& closure,
}
} // namespace random_internal_nanobenchmark
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_
diff --git a/absl/random/internal/nanobenchmark_test.cc b/absl/random/internal/nanobenchmark_test.cc
index f96e0f5d..ab824ef5 100644
--- a/absl/random/internal/nanobenchmark_test.cc
+++ b/absl/random/internal/nanobenchmark_test.cc
@@ -18,7 +18,7 @@
#include "absl/strings/numbers.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal_nanobenchmark {
namespace {
@@ -68,7 +68,7 @@ void RunAll(const int argc, char* argv[]) {
} // namespace
} // namespace random_internal_nanobenchmark
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
int main(int argc, char* argv[]) {
diff --git a/absl/random/internal/nonsecure_base.h b/absl/random/internal/nonsecure_base.h
index c8af51cf..730fa2ea 100644
--- a/absl/random/internal/nonsecure_base.h
+++ b/absl/random/internal/nonsecure_base.h
@@ -33,7 +33,7 @@
#include "absl/types/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// Each instance of NonsecureURBGBase<URBG> will be seeded by variates produced
@@ -144,7 +144,7 @@ class NonsecureURBGBase {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_
diff --git a/absl/random/internal/nonsecure_base_test.cc b/absl/random/internal/nonsecure_base_test.cc
index d9de9901..698027fc 100644
--- a/absl/random/internal/nonsecure_base_test.cc
+++ b/absl/random/internal/nonsecure_base_test.cc
@@ -154,9 +154,10 @@ TEST(NonsecureURBGBase, CompatibleWithDistributionUtils) {
TEST(NonsecureURBGBase, CompatibleWithStdDistributions) {
ExampleNonsecureURBG rbg;
- std::uniform_int_distribution<uint32_t>(0, 100)(rbg);
- std::uniform_real_distribution<float>()(rbg);
- std::bernoulli_distribution(0.2)(rbg);
+ // Cast to void to suppress [[nodiscard]] warnings
+ static_cast<void>(std::uniform_int_distribution<uint32_t>(0, 100)(rbg));
+ static_cast<void>(std::uniform_real_distribution<float>()(rbg));
+ static_cast<void>(std::bernoulli_distribution(0.2)(rbg));
}
TEST(NonsecureURBGBase, ConsecutiveDefaultInstancesYieldUniqueVariates) {
diff --git a/absl/random/internal/pcg_engine.h b/absl/random/internal/pcg_engine.h
index 607ac34b..53c23fe1 100644
--- a/absl/random/internal/pcg_engine.h
+++ b/absl/random/internal/pcg_engine.h
@@ -24,7 +24,7 @@
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// pcg_engine is a simplified implementation of Melissa O'Neil's PCG engine in
@@ -301,7 +301,7 @@ using pcg32_2018_engine = pcg_engine<
random_internal::pcg_xsh_rr_64_32>;
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_
diff --git a/absl/random/internal/platform.h b/absl/random/internal/platform.h
index a5a42cbb..bbdb4e62 100644
--- a/absl/random/internal/platform.h
+++ b/absl/random/internal/platform.h
@@ -162,7 +162,8 @@
// iOS does not support dispatch, even on x86, since applications
// should be bundled as fat binaries, with a different build tailored for
// each specific supported platform/architecture.
-#if defined(__APPLE__) && (TARGET_OS_IPHONE || TARGET_OS_IPHONE_SIMULATOR)
+#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \
+ (defined(TARGET_OS_IPHONE_SIMULATOR) && TARGET_OS_IPHONE_SIMULATOR)
#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH
#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0
#endif
diff --git a/absl/random/internal/pool_urbg.cc b/absl/random/internal/pool_urbg.cc
index 304d9b16..5bee5307 100644
--- a/absl/random/internal/pool_urbg.cc
+++ b/absl/random/internal/pool_urbg.cc
@@ -37,7 +37,7 @@ using absl::base_internal::SpinLock;
using absl::base_internal::SpinLockHolder;
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
namespace {
@@ -60,13 +60,13 @@ class RandenPoolEntry {
}
// Copy bytes into out.
- void Fill(uint8_t* out, size_t bytes) LOCKS_EXCLUDED(mu_);
+ void Fill(uint8_t* out, size_t bytes) ABSL_LOCKS_EXCLUDED(mu_);
// Returns random bits from the buffer in units of T.
template <typename T>
- inline T Generate() LOCKS_EXCLUDED(mu_);
+ inline T Generate() ABSL_LOCKS_EXCLUDED(mu_);
- inline void MaybeRefill() EXCLUSIVE_LOCKS_REQUIRED(mu_) {
+ inline void MaybeRefill() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
if (next_ >= kState) {
next_ = kCapacity;
impl_.Generate(state_);
@@ -75,10 +75,10 @@ class RandenPoolEntry {
private:
// Randen URBG state.
- uint32_t state_[kState] GUARDED_BY(mu_); // First to satisfy alignment.
+ uint32_t state_[kState] ABSL_GUARDED_BY(mu_); // First to satisfy alignment.
SpinLock mu_;
const Randen impl_;
- size_t next_ GUARDED_BY(mu_);
+ size_t next_ ABSL_GUARDED_BY(mu_);
};
template <>
@@ -250,5 +250,5 @@ template class RandenPool<uint32_t>;
template class RandenPool<uint64_t>;
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/pool_urbg.h b/absl/random/internal/pool_urbg.h
index eac75e2c..05721929 100644
--- a/absl/random/internal/pool_urbg.h
+++ b/absl/random/internal/pool_urbg.h
@@ -22,7 +22,7 @@
#include "absl/types/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// RandenPool is a thread-safe random number generator [random.req.urbg] that
@@ -125,7 +125,7 @@ class PoolURBG {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_POOL_URBG_H_
diff --git a/absl/random/internal/randen.cc b/absl/random/internal/randen.cc
index 3b087605..78a1e00c 100644
--- a/absl/random/internal/randen.cc
+++ b/absl/random/internal/randen.cc
@@ -41,7 +41,7 @@
// structured/low-entropy counters to digits of Pi.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
namespace {
@@ -87,5 +87,5 @@ Randen::Randen() {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/randen.h b/absl/random/internal/randen.h
index ef375f90..c2834aaf 100644
--- a/absl/random/internal/randen.h
+++ b/absl/random/internal/randen.h
@@ -23,7 +23,7 @@
#include "absl/random/internal/randen_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// RANDen = RANDom generator or beetroots in Swiss German.
@@ -96,7 +96,7 @@ class Randen {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_RANDEN_H_
diff --git a/absl/random/internal/randen_detect.cc b/absl/random/internal/randen_detect.cc
index 610ae319..d63230c2 100644
--- a/absl/random/internal/randen_detect.cc
+++ b/absl/random/internal/randen_detect.cc
@@ -95,7 +95,7 @@ static uint32_t GetAuxval(uint32_t hwcap_type) {
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// The default return at the end of the function might be unreachable depending
@@ -217,5 +217,5 @@ bool CPUSupportsRandenHwAes() {
#endif
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/randen_detect.h b/absl/random/internal/randen_detect.h
index cb777550..f283f432 100644
--- a/absl/random/internal/randen_detect.h
+++ b/absl/random/internal/randen_detect.h
@@ -15,8 +15,10 @@
#ifndef ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_
#define ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// Returns whether the current CPU supports RandenHwAes implementation.
@@ -25,7 +27,7 @@ namespace random_internal {
bool CPUSupportsRandenHwAes();
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_
diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h
index e721559e..6b337313 100644
--- a/absl/random/internal/randen_engine.h
+++ b/absl/random/internal/randen_engine.h
@@ -28,7 +28,7 @@
#include "absl/random/internal/randen.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// Deterministic pseudorandom byte generator with backtracking resistance
@@ -224,7 +224,7 @@ class alignas(16) randen_engine {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_
diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc
index d7eed8b2..e23844f1 100644
--- a/absl/random/internal/randen_hwaes.cc
+++ b/absl/random/internal/randen_hwaes.cc
@@ -22,39 +22,9 @@
#include <cstdint>
#include <cstring>
+#include "absl/base/attributes.h"
#include "absl/random/internal/platform.h"
-// ABSL_HAVE_ATTRIBUTE
-#if !defined(ABSL_HAVE_ATTRIBUTE)
-#ifdef __has_attribute
-#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x)
-#else
-#define ABSL_HAVE_ATTRIBUTE(x) 0
-#endif
-#endif
-
-#if ABSL_HAVE_ATTRIBUTE(always_inline) || \
- (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \
- __attribute__((always_inline))
-#elif defined(_MSC_VER)
-// We can achieve something similar to attribute((always_inline)) with MSVC by
-// using the __forceinline keyword, however this is not perfect. MSVC is
-// much less aggressive about inlining, and even with the __forceinline keyword.
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __forceinline
-#else
-#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE
-#endif
-
-// ABSL_ATTRIBUTE_FLATTEN enables much more aggressive inlining within
-// the indicated function.
-#undef ABSL_ATTRIBUTE_FLATTEN
-#if ABSL_HAVE_ATTRIBUTE(flatten) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_ATTRIBUTE_FLATTEN __attribute__((flatten))
-#else
-#define ABSL_ATTRIBUTE_FLATTEN
-#endif
-
// ABSL_RANDEN_HWAES_IMPL indicates whether this file will contain
// a hardware accelerated implementation of randen, or whether it
// will contain stubs that exit the process.
@@ -105,7 +75,7 @@
#include <cstdlib>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// No accelerated implementation.
@@ -137,7 +107,7 @@ void RandenHwAes::Generate(const void*, void*) {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#else // defined(ABSL_RANDEN_HWAES_IMPL)
@@ -148,18 +118,6 @@ void RandenHwAes::Generate(const void*, void*) {
#include "absl/random/internal/randen_traits.h"
-// ABSL_FUNCTION_ALIGN32 defines a 32-byte alignment attribute
-// for the functions in this file.
-//
-// NOTE: Determine whether we actually have any wins from ALIGN32
-// using microbenchmarks. If not, remove.
-#undef ABSL_FUNCTION_ALIGN32
-#if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__))
-#define ABSL_FUNCTION_ALIGN32 __attribute__((aligned(32)))
-#else
-#define ABSL_FUNCTION_ALIGN32
-#endif
-
// TARGET_CRYPTO defines a crypto attribute for each architecture.
//
// NOTE: Evaluate whether we should eliminate ABSL_TARGET_CRYPTO.
@@ -193,8 +151,7 @@ using Vector128 = __vector unsigned long long; // NOLINT(runtime/int)
namespace {
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-ReverseBytes(const Vector128& v) {
+inline ABSL_TARGET_CRYPTO Vector128 ReverseBytes(const Vector128& v) {
// Reverses the bytes of the vector.
const __vector unsigned char perm = {15, 14, 13, 12, 11, 10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 0};
@@ -204,26 +161,23 @@ ReverseBytes(const Vector128& v) {
// WARNING: these load/store in native byte order. It is OK to load and then
// store an unchanged vector, but interpreting the bits as a number or input
// to AES will have undefined results.
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) {
+inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
return vec_vsx_ld(0, reinterpret_cast<const Vector128*>(from));
}
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void
-Vector128Store(const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) {
+inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
vec_vsx_st(v, 0, reinterpret_cast<Vector128*>(to));
}
// One round of AES. "round_key" is a public constant for breaking the
// symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-AesRound(const Vector128& state, const Vector128& round_key) {
+inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
+ const Vector128& round_key) {
return Vector128(__builtin_crypto_vcipher(state, round_key));
}
// Enables native loads in the round loop by pre-swapping.
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void
-SwapEndian(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) {
+inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t* state) {
using absl::random_internal::RandenTraits;
constexpr size_t kLanes = 2;
constexpr size_t kFeistelBlocks = RandenTraits::kFeistelBlocks;
@@ -275,20 +229,18 @@ using Vector128 = uint8x16_t;
namespace {
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) {
+inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
return vld1q_u8(reinterpret_cast<const uint8_t*>(from));
}
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void
-Vector128Store(const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) {
+inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
vst1q_u8(reinterpret_cast<uint8_t*>(to), v);
}
// One round of AES. "round_key" is a public constant for breaking the
// symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-AesRound(const Vector128& state, const Vector128& round_key) {
+inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
+ const Vector128& round_key) {
// It is important to always use the full round function - omitting the
// final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
// and does not help because we never decrypt.
@@ -299,8 +251,7 @@ AesRound(const Vector128& state, const Vector128& round_key) {
return vaesmcq_u8(vaeseq_u8(state, uint8x16_t{})) ^ round_key;
}
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void
-SwapEndian(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT) {}
+inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t*) {}
} // namespace
@@ -315,16 +266,11 @@ namespace {
class Vector128 {
public:
// Convert from/to intrinsics.
- inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE explicit Vector128(
- const __m128i& Vector128)
- : data_(Vector128) {}
+ inline explicit Vector128(const __m128i& Vector128) : data_(Vector128) {}
- inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __m128i data() const {
- return data_;
- }
+ inline __m128i data() const { return data_; }
- inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128& operator^=(
- const Vector128& other) {
+ inline Vector128& operator^=(const Vector128& other) {
data_ = _mm_xor_si128(data_, other.data());
return *this;
}
@@ -333,29 +279,25 @@ class Vector128 {
__m128i data_;
};
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) {
+inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) {
return Vector128(_mm_load_si128(reinterpret_cast<const __m128i*>(from)));
}
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void
-Vector128Store(const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) {
- _mm_store_si128(reinterpret_cast<__m128i * ABSL_RANDOM_INTERNAL_RESTRICT>(to),
- v.data());
+inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) {
+ _mm_store_si128(reinterpret_cast<__m128i*>(to), v.data());
}
// One round of AES. "round_key" is a public constant for breaking the
// symmetry of AES (ensures previously equal columns differ afterwards).
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128
-AesRound(const Vector128& state, const Vector128& round_key) {
+inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state,
+ const Vector128& round_key) {
// It is important to always use the full round function - omitting the
// final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf]
// and does not help because we never decrypt.
return Vector128(_mm_aesenc_si128(state.data(), round_key.data()));
}
-inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void
-SwapEndian(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT) {}
+inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t*) {}
} // namespace
@@ -452,8 +394,7 @@ constexpr size_t kLanes = 2;
// Block shuffles applies a shuffle to the entire state between AES rounds.
// Improved odd-even shuffle from "New criterion for diffusion property".
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO void
-BlockShuffle(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) {
+inline ABSL_TARGET_CRYPTO void BlockShuffle(uint64_t* state) {
static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks.");
constexpr size_t shuffle[kFeistelBlocks] = {7, 2, 13, 4, 11, 8, 3, 6,
@@ -501,10 +442,8 @@ BlockShuffle(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) {
// per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in
// parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel
// XORs are 'free' (included in the second AES instruction).
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO const
- u64x2*
- FeistelRound(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state,
- const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
+inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound(
+ uint64_t* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) {
static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks.");
// MSVC does a horrible job at unrolling loops.
@@ -563,9 +502,8 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO const
// Indistinguishable from ideal by chosen-ciphertext adversaries using less than
// 2^64 queries if the round function is a PRF. This is similar to the b=8 case
// of Simpira v2, but more efficient than its generic construction for b=16.
-inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO void
-Permute(const void* ABSL_RANDOM_INTERNAL_RESTRICT keys,
- uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) {
+inline ABSL_TARGET_CRYPTO void Permute(
+ const void* ABSL_RANDOM_INTERNAL_RESTRICT keys, uint64_t* state) {
const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys128 =
static_cast<const u64x2*>(keys);
@@ -582,25 +520,22 @@ Permute(const void* ABSL_RANDOM_INTERNAL_RESTRICT keys,
} // namespace
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
bool HasRandenHwAesImplementation() { return true; }
-const void* ABSL_TARGET_CRYPTO ABSL_FUNCTION_ALIGN32 ABSL_ATTRIBUTE_FLATTEN
-RandenHwAes::GetKeys() {
+const void* ABSL_TARGET_CRYPTO RandenHwAes::GetKeys() {
// Round keys for one AES per Feistel round and branch.
// The canonical implementation uses first digits of Pi.
return round_keys;
}
// NOLINTNEXTLINE
-void ABSL_TARGET_CRYPTO ABSL_FUNCTION_ALIGN32 ABSL_ATTRIBUTE_FLATTEN
-RandenHwAes::Absorb(const void* seed_void, void* state_void) {
- uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state =
- reinterpret_cast<uint64_t*>(state_void);
- const uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT seed =
- reinterpret_cast<const uint64_t*>(seed_void);
+void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void,
+ void* state_void) {
+ auto* state = static_cast<uint64_t*>(state_void);
+ const auto* seed = static_cast<const uint64_t*>(seed_void);
constexpr size_t kCapacityBlocks = kCapacityBytes / sizeof(Vector128);
constexpr size_t kStateBlocks = kStateBytes / sizeof(Vector128);
@@ -672,12 +607,11 @@ RandenHwAes::Absorb(const void* seed_void, void* state_void) {
}
// NOLINTNEXTLINE
-void ABSL_TARGET_CRYPTO ABSL_FUNCTION_ALIGN32 ABSL_ATTRIBUTE_FLATTEN
-RandenHwAes::Generate(const void* keys, void* state_void) {
+void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys,
+ void* state_void) {
static_assert(kCapacityBytes == sizeof(Vector128), "Capacity mismatch");
- uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state =
- reinterpret_cast<uint64_t*>(state_void);
+ auto* state = static_cast<uint64_t*>(state_void);
const Vector128 prev_inner = Vector128Load(state);
@@ -698,7 +632,7 @@ RandenHwAes::Generate(const void* keys, void* state_void) {
#endif
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // (ABSL_RANDEN_HWAES_IMPL)
diff --git a/absl/random/internal/randen_hwaes.h b/absl/random/internal/randen_hwaes.h
index 848bcead..bce36b52 100644
--- a/absl/random/internal/randen_hwaes.h
+++ b/absl/random/internal/randen_hwaes.h
@@ -15,13 +15,15 @@
#ifndef ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_
#define ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_
+#include "absl/base/config.h"
+
// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate
// symbols from arbitrary system and other headers, since it may be built
// with different flags from other targets, using different levels of
// optimization, potentially introducing ODR violations.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// RANDen = RANDom generator or beetroots in Swiss German.
@@ -42,7 +44,7 @@ class RandenHwAes {
bool HasRandenHwAesImplementation();
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_
diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc
index e2d44f88..8d074582 100644
--- a/absl/random/internal/randen_slow.cc
+++ b/absl/random/internal/randen_slow.cc
@@ -18,17 +18,9 @@
#include <cstdint>
#include <cstring>
+#include "absl/base/attributes.h"
#include "absl/random/internal/platform.h"
-// ABSL_HAVE_ATTRIBUTE
-#if !defined(ABSL_HAVE_ATTRIBUTE)
-#ifdef __has_attribute
-#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x)
-#else
-#define ABSL_HAVE_ATTRIBUTE(x) 0
-#endif
-#endif
-
#if ABSL_HAVE_ATTRIBUTE(always_inline) || \
(defined(__GNUC__) && !defined(__clang__))
#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \
@@ -470,7 +462,7 @@ inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute(
} // namespace
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
const void* RandenSlow::GetKeys() {
@@ -510,5 +502,5 @@ void RandenSlow::Generate(const void* keys, void* state_void) {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/randen_slow.h b/absl/random/internal/randen_slow.h
index 2133b370..72f92b54 100644
--- a/absl/random/internal/randen_slow.h
+++ b/absl/random/internal/randen_slow.h
@@ -17,8 +17,10 @@
#include <cstddef>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// RANDen = RANDom generator or beetroots in Swiss German.
@@ -39,7 +41,7 @@ class RandenSlow {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_
diff --git a/absl/random/internal/randen_traits.h b/absl/random/internal/randen_traits.h
index d2562586..2b8bbe73 100644
--- a/absl/random/internal/randen_traits.h
+++ b/absl/random/internal/randen_traits.h
@@ -22,8 +22,10 @@
#include <cstddef>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// RANDen = RANDom generator or beetroots in Swiss German.
@@ -55,7 +57,7 @@ struct RandenTraits {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_
diff --git a/absl/random/internal/salted_seed_seq.h b/absl/random/internal/salted_seed_seq.h
index 08bf369e..5953a090 100644
--- a/absl/random/internal/salted_seed_seq.h
+++ b/absl/random/internal/salted_seed_seq.h
@@ -30,7 +30,7 @@
#include "absl/types/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// This class conforms to the C++ Standard "Seed Sequence" concept
@@ -161,7 +161,7 @@ SaltedSeedSeq<typename std::decay<SSeq>::type> MakeSaltedSeedSeq(SSeq&& seq) {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_
diff --git a/absl/random/internal/seed_material.cc b/absl/random/internal/seed_material.cc
index dae7007f..4d38a574 100644
--- a/absl/random/internal/seed_material.cc
+++ b/absl/random/internal/seed_material.cc
@@ -45,6 +45,9 @@
#define ABSL_RANDOM_USE_BCRYPT 1
#pragma comment(lib, "bcrypt.lib")
+#elif defined(__Fuchsia__)
+#include <zircon/syscalls.h>
+
#endif
#if defined(ABSL_RANDOM_USE_BCRYPT)
@@ -58,7 +61,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
namespace {
@@ -108,6 +111,15 @@ bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
return true;
}
+#elif defined(__Fuchsia__)
+
+bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
+ auto buffer = reinterpret_cast<uint8_t*>(values.data());
+ size_t buffer_size = sizeof(uint32_t) * values.size();
+ zx_cprng_draw(buffer, buffer_size);
+ return true;
+}
+
#else
// On *nix, read entropy from /dev/urandom.
@@ -203,5 +215,5 @@ absl::optional<uint32_t> GetSaltMaterial() {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/internal/seed_material.h b/absl/random/internal/seed_material.h
index 41387fe3..4be10e92 100644
--- a/absl/random/internal/seed_material.h
+++ b/absl/random/internal/seed_material.h
@@ -27,7 +27,7 @@
#include "absl/types/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// Returns the number of 32-bit blocks needed to contain the given number of
@@ -98,7 +98,7 @@ void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence,
absl::optional<uint32_t> GetSaltMaterial();
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_
diff --git a/absl/random/internal/sequence_urbg.h b/absl/random/internal/sequence_urbg.h
index cec0bf9b..bc96a12c 100644
--- a/absl/random/internal/sequence_urbg.h
+++ b/absl/random/internal/sequence_urbg.h
@@ -21,8 +21,10 @@
#include <type_traits>
#include <vector>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// `sequence_urbg` is a simple random number generator which meets the
@@ -52,7 +54,7 @@ class sequence_urbg {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_
diff --git a/absl/random/internal/traits.h b/absl/random/internal/traits.h
index 9f7d126c..75772bd9 100644
--- a/absl/random/internal/traits.h
+++ b/absl/random/internal/traits.h
@@ -22,7 +22,7 @@
#include "absl/base/config.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace random_internal {
// random_internal::is_widening_convertible<A, B>
@@ -95,7 +95,7 @@ struct make_unsigned_bits {
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_TRAITS_H_
diff --git a/absl/random/internal/uniform_helper.h b/absl/random/internal/uniform_helper.h
index 6af053ef..663107cb 100644
--- a/absl/random/internal/uniform_helper.h
+++ b/absl/random/internal/uniform_helper.h
@@ -22,7 +22,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
template <typename IntType>
class uniform_int_distribution;
@@ -31,15 +31,33 @@ class uniform_real_distribution;
// Interval tag types which specify whether the interval is open or closed
// on either boundary.
+
namespace random_internal {
-struct IntervalClosedClosedT {};
-struct IntervalClosedOpenT {};
-struct IntervalOpenClosedT {};
-struct IntervalOpenOpenT {};
+template <typename T>
+struct TagTypeCompare {};
+
+template <typename T>
+constexpr bool operator==(TagTypeCompare<T>, TagTypeCompare<T>) {
+ // Tags are mono-states. They always compare equal.
+ return true;
+}
+template <typename T>
+constexpr bool operator!=(TagTypeCompare<T>, TagTypeCompare<T>) {
+ return false;
+}
+
} // namespace random_internal
-namespace random_internal {
+struct IntervalClosedClosedTag
+ : public random_internal::TagTypeCompare<IntervalClosedClosedTag> {};
+struct IntervalClosedOpenTag
+ : public random_internal::TagTypeCompare<IntervalClosedOpenTag> {};
+struct IntervalOpenClosedTag
+ : public random_internal::TagTypeCompare<IntervalOpenClosedTag> {};
+struct IntervalOpenOpenTag
+ : public random_internal::TagTypeCompare<IntervalOpenOpenTag> {};
+namespace random_internal {
// The functions
// uniform_lower_bound(tag, a, b)
// and
@@ -60,8 +78,8 @@ template <typename IntType, typename Tag>
typename absl::enable_if_t<
absl::conjunction<
std::is_integral<IntType>,
- absl::disjunction<std::is_same<Tag, IntervalOpenClosedT>,
- std::is_same<Tag, IntervalOpenOpenT>>>::value,
+ absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>,
+ std::is_same<Tag, IntervalOpenOpenTag>>>::value,
IntType>
uniform_lower_bound(Tag, IntType a, IntType) {
return a + 1;
@@ -71,8 +89,8 @@ template <typename FloatType, typename Tag>
typename absl::enable_if_t<
absl::conjunction<
std::is_floating_point<FloatType>,
- absl::disjunction<std::is_same<Tag, IntervalOpenClosedT>,
- std::is_same<Tag, IntervalOpenOpenT>>>::value,
+ absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>,
+ std::is_same<Tag, IntervalOpenOpenTag>>>::value,
FloatType>
uniform_lower_bound(Tag, FloatType a, FloatType b) {
return std::nextafter(a, b);
@@ -80,8 +98,8 @@ uniform_lower_bound(Tag, FloatType a, FloatType b) {
template <typename NumType, typename Tag>
typename absl::enable_if_t<
- absl::disjunction<std::is_same<Tag, IntervalClosedClosedT>,
- std::is_same<Tag, IntervalClosedOpenT>>::value,
+ absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
+ std::is_same<Tag, IntervalClosedOpenTag>>::value,
NumType>
uniform_lower_bound(Tag, NumType a, NumType) {
return a;
@@ -91,8 +109,8 @@ template <typename IntType, typename Tag>
typename absl::enable_if_t<
absl::conjunction<
std::is_integral<IntType>,
- absl::disjunction<std::is_same<Tag, IntervalClosedOpenT>,
- std::is_same<Tag, IntervalOpenOpenT>>>::value,
+ absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>,
+ std::is_same<Tag, IntervalOpenOpenTag>>>::value,
IntType>
uniform_upper_bound(Tag, IntType, IntType b) {
return b - 1;
@@ -102,8 +120,8 @@ template <typename FloatType, typename Tag>
typename absl::enable_if_t<
absl::conjunction<
std::is_floating_point<FloatType>,
- absl::disjunction<std::is_same<Tag, IntervalClosedOpenT>,
- std::is_same<Tag, IntervalOpenOpenT>>>::value,
+ absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>,
+ std::is_same<Tag, IntervalOpenOpenTag>>>::value,
FloatType>
uniform_upper_bound(Tag, FloatType, FloatType b) {
return b;
@@ -113,8 +131,8 @@ template <typename IntType, typename Tag>
typename absl::enable_if_t<
absl::conjunction<
std::is_integral<IntType>,
- absl::disjunction<std::is_same<Tag, IntervalClosedClosedT>,
- std::is_same<Tag, IntervalOpenClosedT>>>::value,
+ absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
+ std::is_same<Tag, IntervalOpenClosedTag>>>::value,
IntType>
uniform_upper_bound(Tag, IntType, IntType b) {
return b;
@@ -124,8 +142,8 @@ template <typename FloatType, typename Tag>
typename absl::enable_if_t<
absl::conjunction<
std::is_floating_point<FloatType>,
- absl::disjunction<std::is_same<Tag, IntervalClosedClosedT>,
- std::is_same<Tag, IntervalOpenClosedT>>>::value,
+ absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
+ std::is_same<Tag, IntervalOpenClosedTag>>>::value,
FloatType>
uniform_upper_bound(Tag, FloatType, FloatType b) {
return std::nextafter(b, (std::numeric_limits<FloatType>::max)());
@@ -137,16 +155,26 @@ using UniformDistribution =
absl::uniform_int_distribution<NumType>,
absl::uniform_real_distribution<NumType>>::type;
-template <typename TagType, typename NumType>
+template <typename NumType>
struct UniformDistributionWrapper : public UniformDistribution<NumType> {
- explicit UniformDistributionWrapper(NumType lo, NumType hi)
+ template <typename TagType>
+ explicit UniformDistributionWrapper(TagType, NumType lo, NumType hi)
: UniformDistribution<NumType>(
uniform_lower_bound<NumType>(TagType{}, lo, hi),
uniform_upper_bound<NumType>(TagType{}, lo, hi)) {}
+
+ explicit UniformDistributionWrapper(NumType lo, NumType hi)
+ : UniformDistribution<NumType>(
+ uniform_lower_bound<NumType>(IntervalClosedOpenTag(), lo, hi),
+ uniform_upper_bound<NumType>(IntervalClosedOpenTag(), lo, hi)) {}
+
+ explicit UniformDistributionWrapper()
+ : UniformDistribution<NumType>(std::numeric_limits<NumType>::lowest(),
+ (std::numeric_limits<NumType>::max)()) {}
};
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_
diff --git a/absl/random/internal/wide_multiply.h b/absl/random/internal/wide_multiply.h
new file mode 100644
index 00000000..6e4cf1be
--- /dev/null
+++ b/absl/random/internal/wide_multiply.h
@@ -0,0 +1,111 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_
+#define ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_
+
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+
+#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64)
+#include <intrin.h> // NOLINT(build/include_order)
+#pragma intrinsic(_umul128)
+#define ABSL_INTERNAL_USE_UMUL128 1
+#endif
+
+#include "absl/base/config.h"
+#include "absl/base/internal/bits.h"
+#include "absl/numeric/int128.h"
+#include "absl/random/internal/traits.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace random_internal {
+
+// Helper object to multiply two 64-bit values to a 128-bit value.
+// MultiplyU64ToU128 multiplies two 64-bit values to a 128-bit value.
+// If an intrinsic is available, it is used, otherwise use native 32-bit
+// multiplies to construct the result.
+inline uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) {
+#if defined(ABSL_HAVE_INTRINSIC_INT128)
+ return uint128(static_cast<__uint128_t>(a) * b);
+#elif defined(ABSL_INTERNAL_USE_UMUL128)
+ // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC.
+ uint64_t high = 0;
+ const uint64_t low = _umul128(a, b, &high);
+ return absl::MakeUint128(high, low);
+#else
+ // uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit
+ // multiply. However there are many cases where that is not necessary, and it
+ // is only necessary to support a 64-bit x 64-bit = 128-bit multiply. This is
+ // for those cases.
+ const uint64_t a00 = static_cast<uint32_t>(a);
+ const uint64_t a32 = a >> 32;
+ const uint64_t b00 = static_cast<uint32_t>(b);
+ const uint64_t b32 = b >> 32;
+
+ const uint64_t c00 = a00 * b00;
+ const uint64_t c32a = a00 * b32;
+ const uint64_t c32b = a32 * b00;
+ const uint64_t c64 = a32 * b32;
+
+ const uint32_t carry =
+ static_cast<uint32_t>(((c00 >> 32) + static_cast<uint32_t>(c32a) +
+ static_cast<uint32_t>(c32b)) >>
+ 32);
+
+ return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry,
+ c00 + (c32a << 32) + (c32b << 32));
+#endif
+}
+
+// wide_multiply<T> multiplies two N-bit values to a 2N-bit result.
+template <typename UIntType>
+struct wide_multiply {
+ static constexpr size_t kN = std::numeric_limits<UIntType>::digits;
+ using input_type = UIntType;
+ using result_type = typename random_internal::unsigned_bits<kN * 2>::type;
+
+ static result_type multiply(input_type a, input_type b) {
+ return static_cast<result_type>(a) * b;
+ }
+
+ static input_type hi(result_type r) { return r >> kN; }
+ static input_type lo(result_type r) { return r; }
+
+ static_assert(std::is_unsigned<UIntType>::value,
+ "Class-template wide_multiply<> argument must be unsigned.");
+};
+
+#ifndef ABSL_HAVE_INTRINSIC_INT128
+template <>
+struct wide_multiply<uint64_t> {
+ using input_type = uint64_t;
+ using result_type = uint128;
+
+ static result_type multiply(uint64_t a, uint64_t b) {
+ return MultiplyU64ToU128(a, b);
+ }
+
+ static uint64_t hi(result_type r) { return Uint128High64(r); }
+ static uint64_t lo(result_type r) { return Uint128Low64(r); }
+};
+#endif
+
+} // namespace random_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_
diff --git a/absl/random/internal/wide_multiply_test.cc b/absl/random/internal/wide_multiply_test.cc
new file mode 100644
index 00000000..922603f2
--- /dev/null
+++ b/absl/random/internal/wide_multiply_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/random/internal/wide_multiply.h"
+
+#include "gtest/gtest.h"
+#include "absl/base/internal/bits.h"
+#include "absl/numeric/int128.h"
+
+using absl::random_internal::MultiplyU64ToU128;
+
+namespace {
+
+TEST(WideMultiplyTest, MultiplyU64ToU128Test) {
+ constexpr uint64_t k1 = 1;
+ constexpr uint64_t kMax = ~static_cast<uint64_t>(0);
+
+ EXPECT_EQ(absl::uint128(0), MultiplyU64ToU128(0, 0));
+
+ // Max uint64
+ EXPECT_EQ(MultiplyU64ToU128(kMax, kMax),
+ absl::MakeUint128(0xfffffffffffffffe, 0x0000000000000001));
+ EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(kMax, 1));
+ EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(1, kMax));
+ for (int i = 0; i < 64; ++i) {
+ EXPECT_EQ(absl::MakeUint128(0, kMax) << i,
+ MultiplyU64ToU128(kMax, k1 << i));
+ EXPECT_EQ(absl::MakeUint128(0, kMax) << i,
+ MultiplyU64ToU128(k1 << i, kMax));
+ }
+
+ // 1-bit x 1-bit.
+ for (int i = 0; i < 64; ++i) {
+ for (int j = 0; j < 64; ++j) {
+ EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j),
+ MultiplyU64ToU128(k1 << i, k1 << j));
+ EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j),
+ MultiplyU64ToU128(k1 << i, k1 << j));
+ }
+ }
+
+ // Verified multiplies
+ EXPECT_EQ(MultiplyU64ToU128(0xffffeeeeddddcccc, 0xbbbbaaaa99998888),
+ absl::MakeUint128(0xbbbb9e2692c5dddc, 0xc28f7531048d2c60));
+ EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfedcba9876543210),
+ absl::MakeUint128(0x0121fa00ad77d742, 0x2236d88fe5618cf0));
+ EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfdb97531eca86420),
+ absl::MakeUint128(0x0120ae99d26725fc, 0xce197f0ecac319e0));
+ EXPECT_EQ(MultiplyU64ToU128(0x97a87f4f261ba3f2, 0xfedcba9876543210),
+ absl::MakeUint128(0x96fbf1a8ae78d0ba, 0x5a6dd4b71f278320));
+ EXPECT_EQ(MultiplyU64ToU128(0xfedcba9876543210, 0xfdb97531eca86420),
+ absl::MakeUint128(0xfc98c6981a413e22, 0x342d0bbf48948200));
+}
+
+} // namespace
diff --git a/absl/random/log_uniform_int_distribution.h b/absl/random/log_uniform_int_distribution.h
index a12fa4cb..960816e2 100644
--- a/absl/random/log_uniform_int_distribution.h
+++ b/absl/random/log_uniform_int_distribution.h
@@ -23,14 +23,14 @@
#include <ostream>
#include <type_traits>
-#include "absl/random/internal/distribution_impl.h"
#include "absl/random/internal/fastmath.h"
+#include "absl/random/internal/generate_real.h"
#include "absl/random/internal/iostream_state_saver.h"
#include "absl/random/internal/traits.h"
#include "absl/random/uniform_int_distribution.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// log_uniform_int_distribution:
//
@@ -193,13 +193,15 @@ log_uniform_int_distribution<IntType>::Generate(
const double r = std::pow(p.base(), d);
const double s = (r * p.base()) - 1.0;
- base_e = (r > (std::numeric_limits<unsigned_type>::max)())
- ? (std::numeric_limits<unsigned_type>::max)()
- : static_cast<unsigned_type>(r);
+ base_e =
+ (r > static_cast<double>((std::numeric_limits<unsigned_type>::max)()))
+ ? (std::numeric_limits<unsigned_type>::max)()
+ : static_cast<unsigned_type>(r);
- top_e = (s > (std::numeric_limits<unsigned_type>::max)())
- ? (std::numeric_limits<unsigned_type>::max)()
- : static_cast<unsigned_type>(s);
+ top_e =
+ (s > static_cast<double>((std::numeric_limits<unsigned_type>::max)()))
+ ? (std::numeric_limits<unsigned_type>::max)()
+ : static_cast<unsigned_type>(s);
}
const unsigned_type lo = (base_e >= p.range()) ? p.range() : base_e;
@@ -246,7 +248,7 @@ std::basic_istream<CharT, Traits>& operator>>(
return is;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_
diff --git a/absl/random/log_uniform_int_distribution_test.cc b/absl/random/log_uniform_int_distribution_test.cc
index 0ff4c32d..5270531d 100644
--- a/absl/random/log_uniform_int_distribution_test.cc
+++ b/absl/random/log_uniform_int_distribution_test.cc
@@ -243,7 +243,7 @@ std::string ParamName(
return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
}
-INSTANTIATE_TEST_SUITE_P(, LogUniformIntChiSquaredTest,
+INSTANTIATE_TEST_SUITE_P(All, LogUniformIntChiSquaredTest,
::testing::ValuesIn(GenParams()), ParamName);
// NOTE: absl::log_uniform_int_distribution is not guaranteed to be stable.
diff --git a/absl/random/mock_distributions.h b/absl/random/mock_distributions.h
new file mode 100644
index 00000000..d36d5ba0
--- /dev/null
+++ b/absl/random/mock_distributions.h
@@ -0,0 +1,261 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// File: mock_distributions.h
+// -----------------------------------------------------------------------------
+//
+// This file contains mock distribution functions for use alongside an
+// `absl::MockingBitGen` object within the Googletest testing framework. Such
+// mocks are useful to provide deterministic values as return values within
+// (otherwise random) Abseil distribution functions.
+//
+// The return type of each function is a mock expectation object which
+// is used to set the match result.
+//
+// More information about the Googletest testing framework is available at
+// https://github.com/google/googletest
+//
+// Example:
+//
+// absl::MockingBitGen mock;
+// EXPECT_CALL(absl::MockUniform<int>(), Call(mock, 1, 1000))
+// .WillRepeatedly(testing::ReturnRoundRobin({20, 40}));
+//
+// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 20);
+// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 40);
+// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 20);
+// EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000), 40);
+
+#ifndef ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_
+#define ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_
+
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/meta/type_traits.h"
+#include "absl/random/distributions.h"
+#include "absl/random/internal/mock_overload_set.h"
+#include "absl/random/mocking_bit_gen.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// -----------------------------------------------------------------------------
+// absl::MockUniform
+// -----------------------------------------------------------------------------
+//
+// Matches calls to absl::Uniform.
+//
+// `absl::MockUniform` is a class template used in conjunction with Googletest's
+// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an
+// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the
+// same way one would define mocks on a Googletest `MockFunction()`.
+//
+// Example:
+//
+// absl::MockingBitGen mock;
+// EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(mock))
+// .WillOnce(Return(123456));
+// auto x = absl::Uniform<uint32_t>(mock);
+// assert(x == 123456)
+//
+template <typename R>
+using MockUniform = random_internal::MockOverloadSet<
+ random_internal::UniformDistributionWrapper<R>,
+ R(IntervalClosedOpenTag, MockingBitGen&, R, R),
+ R(IntervalClosedClosedTag, MockingBitGen&, R, R),
+ R(IntervalOpenOpenTag, MockingBitGen&, R, R),
+ R(IntervalOpenClosedTag, MockingBitGen&, R, R), R(MockingBitGen&, R, R),
+ R(MockingBitGen&)>;
+
+// -----------------------------------------------------------------------------
+// absl::MockBernoulli
+// -----------------------------------------------------------------------------
+//
+// Matches calls to absl::Bernoulli.
+//
+// `absl::MockBernoulli` is a class used in conjunction with Googletest's
+// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an
+// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the
+// same way one would define mocks on a Googletest `MockFunction()`.
+//
+// Example:
+//
+// absl::MockingBitGen mock;
+// EXPECT_CALL(absl::MockBernoulli(), Call(mock, testing::_))
+// .WillOnce(Return(false));
+// assert(absl::Bernoulli(mock, 0.5) == false);
+//
+using MockBernoulli =
+ random_internal::MockOverloadSet<absl::bernoulli_distribution,
+ bool(MockingBitGen&, double)>;
+
+// -----------------------------------------------------------------------------
+// absl::MockBeta
+// -----------------------------------------------------------------------------
+//
+// Matches calls to absl::Beta.
+//
+// `absl::MockBeta` is a class used in conjunction with Googletest's `ON_CALL()`
+// and `EXPECT_CALL()` macros. To use it, default-construct an instance of it
+// inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the same way one
+// would define mocks on a Googletest `MockFunction()`.
+//
+// Example:
+//
+// absl::MockingBitGen mock;
+// EXPECT_CALL(absl::MockBeta(), Call(mock, 3.0, 2.0))
+// .WillOnce(Return(0.567));
+// auto x = absl::Beta<double>(mock, 3.0, 2.0);
+// assert(x == 0.567);
+//
+template <typename RealType>
+using MockBeta =
+ random_internal::MockOverloadSet<absl::beta_distribution<RealType>,
+ RealType(MockingBitGen&, RealType,
+ RealType)>;
+
+// -----------------------------------------------------------------------------
+// absl::MockExponential
+// -----------------------------------------------------------------------------
+//
+// Matches calls to absl::Exponential.
+//
+// `absl::MockExponential` is a class template used in conjunction with
+// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it,
+// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`,
+// and use `Call(...)` the same way one would define mocks on a
+// Googletest `MockFunction()`.
+//
+// Example:
+//
+// absl::MockingBitGen mock;
+// EXPECT_CALL(absl::MockExponential<double>(), Call(mock, 0.5))
+// .WillOnce(Return(12.3456789));
+// auto x = absl::Exponential<double>(mock, 0.5);
+// assert(x == 12.3456789)
+//
+template <typename RealType>
+using MockExponential =
+ random_internal::MockOverloadSet<absl::exponential_distribution<RealType>,
+ RealType(MockingBitGen&, RealType)>;
+
+// -----------------------------------------------------------------------------
+// absl::MockGaussian
+// -----------------------------------------------------------------------------
+//
+// Matches calls to absl::Gaussian.
+//
+// `absl::MockGaussian` is a class template used in conjunction with
+// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it,
+// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`,
+// and use `Call(...)` the same way one would define mocks on a
+// Googletest `MockFunction()`.
+//
+// Example:
+//
+// absl::MockingBitGen mock;
+// EXPECT_CALL(absl::MockGaussian<double>(), Call(mock, 16.3, 3.3))
+// .WillOnce(Return(12.3456789));
+// auto x = absl::Gaussian<double>(mock, 16.3, 3.3);
+// assert(x == 12.3456789)
+//
+template <typename RealType>
+using MockGaussian =
+ random_internal::MockOverloadSet<absl::gaussian_distribution<RealType>,
+ RealType(MockingBitGen&, RealType,
+ RealType)>;
+
+// -----------------------------------------------------------------------------
+// absl::MockLogUniform
+// -----------------------------------------------------------------------------
+//
+// Matches calls to absl::LogUniform.
+//
+// `absl::MockLogUniform` is a class template used in conjunction with
+// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it,
+// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`,
+// and use `Call(...)` the same way one would define mocks on a
+// Googletest `MockFunction()`.
+//
+// Example:
+//
+// absl::MockingBitGen mock;
+// EXPECT_CALL(absl::MockLogUniform<int>(), Call(mock, 10, 10000, 10))
+// .WillOnce(Return(1221));
+// auto x = absl::LogUniform<int>(mock, 10, 10000, 10);
+// assert(x == 1221)
+//
+template <typename IntType>
+using MockLogUniform = random_internal::MockOverloadSet<
+ absl::log_uniform_int_distribution<IntType>,
+ IntType(MockingBitGen&, IntType, IntType, IntType)>;
+
+// -----------------------------------------------------------------------------
+// absl::MockPoisson
+// -----------------------------------------------------------------------------
+//
+// Matches calls to absl::Poisson.
+//
+// `absl::MockPoisson` is a class template used in conjunction with Googletest's
+// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an
+// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the
+// same way one would define mocks on a Googletest `MockFunction()`.
+//
+// Example:
+//
+// absl::MockingBitGen mock;
+// EXPECT_CALL(absl::MockPoisson<int>(), Call(mock, 2.0))
+// .WillOnce(Return(1221));
+// auto x = absl::Poisson<int>(mock, 2.0);
+// assert(x == 1221)
+//
+template <typename IntType>
+using MockPoisson =
+ random_internal::MockOverloadSet<absl::poisson_distribution<IntType>,
+ IntType(MockingBitGen&, double)>;
+
+// -----------------------------------------------------------------------------
+// absl::MockZipf
+// -----------------------------------------------------------------------------
+//
+// Matches calls to absl::Zipf.
+//
+// `absl::MockZipf` is a class template used in conjunction with Googletest's
+// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an
+// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the
+// same way one would define mocks on a Googletest `MockFunction()`.
+//
+// Example:
+//
+// absl::MockingBitGen mock;
+// EXPECT_CALL(absl::MockZipf<int>(), Call(mock, 1000000, 2.0, 1.0))
+// .WillOnce(Return(1221));
+// auto x = absl::Zipf<int>(mock, 1000000, 2.0, 1.0);
+// assert(x == 1221)
+//
+template <typename IntType>
+using MockZipf =
+ random_internal::MockOverloadSet<absl::zipf_distribution<IntType>,
+ IntType(MockingBitGen&, IntType, double,
+ double)>;
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_
diff --git a/absl/random/mock_distributions_test.cc b/absl/random/mock_distributions_test.cc
new file mode 100644
index 00000000..de23bafe
--- /dev/null
+++ b/absl/random/mock_distributions_test.cc
@@ -0,0 +1,72 @@
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/random/mock_distributions.h"
+
+#include "gtest/gtest.h"
+#include "absl/random/mocking_bit_gen.h"
+#include "absl/random/random.h"
+
+namespace {
+using ::testing::Return;
+
+TEST(MockDistributions, Examples) {
+ absl::MockingBitGen gen;
+
+ EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20);
+ EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
+ .WillOnce(Return(20));
+ EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20);
+
+ EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
+ EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0))
+ .WillOnce(Return(5.0));
+ EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
+
+ EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42);
+ EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
+ .WillOnce(Return(42));
+ EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42);
+
+ EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500);
+ EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(500));
+ EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500);
+
+ EXPECT_NE(absl::Bernoulli(gen, 0.000001), true);
+ EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001))
+ .WillOnce(Return(true));
+ EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true);
+
+ EXPECT_NE(absl::Beta<double>(gen, 3.0, 2.0), 0.567);
+ EXPECT_CALL(absl::MockBeta<double>(), Call(gen, 3.0, 2.0))
+ .WillOnce(Return(0.567));
+ EXPECT_EQ(absl::Beta<double>(gen, 3.0, 2.0), 0.567);
+
+ EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
+ EXPECT_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0))
+ .WillOnce(Return(1221));
+ EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
+
+ EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
+ EXPECT_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0))
+ .WillOnce(Return(0.001));
+ EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
+
+ EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040);
+ EXPECT_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2))
+ .WillOnce(Return(2040));
+ EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040);
+}
+
+} // namespace
diff --git a/absl/random/internal/named_generator.cc b/absl/random/mocking_bit_gen.cc
index b168a25b..6bb1e414 100644
--- a/absl/random/internal/named_generator.cc
+++ b/absl/random/mocking_bit_gen.cc
@@ -1,3 +1,4 @@
+//
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,20 +12,19 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+//
+#include "absl/random/mocking_bit_gen.h"
-#include <cstddef>
-#include <iostream>
-
-#include "absl/random/random.h"
+#include <string>
-// This program is used in integration tests.
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+MockingBitGen::~MockingBitGen() {
-int main() {
- auto seed_seq = absl::MakeTaggedSeedSeq("TEST_GENERATOR", std::cerr);
- absl::BitGen rng(seed_seq);
- constexpr size_t kSequenceLength = 8;
- for (size_t i = 0; i < kSequenceLength; i++) {
- std::cout << rng() << "\n";
+ for (const auto& del : deleters_) {
+ del();
}
- return 0;
}
+
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h
new file mode 100644
index 00000000..36cef911
--- /dev/null
+++ b/absl/random/mocking_bit_gen.h
@@ -0,0 +1,196 @@
+// 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.
+//
+// -----------------------------------------------------------------------------
+// mocking_bit_gen.h
+// -----------------------------------------------------------------------------
+//
+// This file includes an `absl::MockingBitGen` class to use as a mock within the
+// Googletest testing framework. Such a mock is useful to provide deterministic
+// values as return values within (otherwise random) Abseil distribution
+// functions. Such determinism within a mock is useful within testing frameworks
+// to test otherwise indeterminate APIs.
+//
+// More information about the Googletest testing framework is available at
+// https://github.com/google/googletest
+
+#ifndef ABSL_RANDOM_MOCKING_BIT_GEN_H_
+#define ABSL_RANDOM_MOCKING_BIT_GEN_H_
+
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <typeindex>
+#include <typeinfo>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/meta/type_traits.h"
+#include "absl/random/distributions.h"
+#include "absl/random/internal/distribution_caller.h"
+#include "absl/random/internal/mocking_bit_gen_base.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_join.h"
+#include "absl/types/span.h"
+#include "absl/types/variant.h"
+#include "absl/utility/utility.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+namespace random_internal {
+
+template <typename, typename>
+struct MockSingleOverload;
+
+} // namespace random_internal
+
+// MockingBitGen
+//
+// `absl::MockingBitGen` is a mock Uniform Random Bit Generator (URBG) class
+// which can act in place of an `absl::BitGen` URBG within tests using the
+// Googletest testing framework.
+//
+// Usage:
+//
+// Use an `absl::MockingBitGen` along with a mock distribution object (within
+// mock_distributions.h) inside Googletest constructs such as ON_CALL(),
+// EXPECT_TRUE(), etc. to produce deterministic results conforming to the
+// distribution's API contract.
+//
+// Example:
+//
+// // Mock a call to an `absl::Bernoulli` distribution using Googletest
+// absl::MockingBitGen bitgen;
+//
+// ON_CALL(absl::MockBernoulli(), Call(bitgen, 0.5))
+// .WillByDefault(testing::Return(true));
+// EXPECT_TRUE(absl::Bernoulli(bitgen, 0.5));
+//
+// // Mock a call to an `absl::Uniform` distribution within Googletest
+// absl::MockingBitGen bitgen;
+//
+// ON_CALL(absl::MockUniform<int>(), Call(bitgen, testing::_, testing::_))
+// .WillByDefault([] (int low, int high) {
+// return (low + high) / 2;
+// });
+//
+// EXPECT_EQ(absl::Uniform<int>(gen, 0, 10), 5);
+// EXPECT_EQ(absl::Uniform<int>(gen, 30, 40), 35);
+//
+// At this time, only mock distributions supplied within the Abseil random
+// library are officially supported.
+//
+class MockingBitGen : public absl::random_internal::MockingBitGenBase {
+ public:
+ MockingBitGen() {}
+
+ ~MockingBitGen() override;
+
+ private:
+ template <typename DistrT, typename... Args>
+ using MockFnType =
+ ::testing::MockFunction<typename DistrT::result_type(Args...)>;
+
+ // MockingBitGen::Register
+ //
+ // Register<DistrT, FormatT, ArgTupleT> is the main extension point for
+ // extending the MockingBitGen framework. It provides a mechanism to install a
+ // mock expectation for the distribution `distr_t` onto the MockingBitGen
+ // context.
+ //
+ // The returned MockFunction<...> type can be used to setup additional
+ // distribution parameters of the expectation.
+ template <typename DistrT, typename... Args, typename... Ms>
+ decltype(std::declval<MockFnType<DistrT, Args...>>().gmock_Call(
+ std::declval<Ms>()...))
+ Register(Ms&&... matchers) {
+ auto& mock =
+ mocks_[std::type_index(GetTypeId<DistrT, std::tuple<Args...>>())];
+
+ if (!mock.mock_fn) {
+ auto* mock_fn = new MockFnType<DistrT, Args...>;
+ mock.mock_fn = mock_fn;
+ mock.match_impl = &MatchImpl<DistrT, Args...>;
+ deleters_.emplace_back([mock_fn] { delete mock_fn; });
+ }
+
+ return static_cast<MockFnType<DistrT, Args...>*>(mock.mock_fn)
+ ->gmock_Call(std::forward<Ms>(matchers)...);
+ }
+
+ mutable std::vector<std::function<void()>> deleters_;
+
+ using match_impl_fn = void (*)(void* mock_fn, void* t_erased_dist_args,
+ void* t_erased_result);
+ struct MockData {
+ void* mock_fn = nullptr;
+ match_impl_fn match_impl = nullptr;
+ };
+
+ mutable absl::flat_hash_map<std::type_index, MockData> mocks_;
+
+ template <typename DistrT, typename... Args>
+ static void MatchImpl(void* mock_fn, void* dist_args, void* result) {
+ using result_type = typename DistrT::result_type;
+ *static_cast<result_type*>(result) = absl::apply(
+ [mock_fn](Args... args) -> result_type {
+ return (*static_cast<MockFnType<DistrT, Args...>*>(mock_fn))
+ .Call(std::move(args)...);
+ },
+ *static_cast<std::tuple<Args...>*>(dist_args));
+ }
+
+ // Looks for an appropriate mock - Returns the mocked result if one is found.
+ // Otherwise, returns a random value generated by the underlying URBG.
+ bool CallImpl(const std::type_info& key_type, void* dist_args,
+ void* result) override {
+ // Trigger a mock, if there exists one that matches `param`.
+ auto it = mocks_.find(std::type_index(key_type));
+ if (it == mocks_.end()) return false;
+ auto* mock_data = static_cast<MockData*>(&it->second);
+ mock_data->match_impl(mock_data->mock_fn, dist_args, result);
+ return true;
+ }
+
+ template <typename, typename>
+ friend struct ::absl::random_internal::MockSingleOverload;
+ friend struct ::absl::random_internal::DistributionCaller<
+ absl::MockingBitGen>;
+};
+
+// -----------------------------------------------------------------------------
+// Implementation Details Only Below
+// -----------------------------------------------------------------------------
+
+namespace random_internal {
+
+template <>
+struct DistributionCaller<absl::MockingBitGen> {
+ template <typename DistrT, typename FormatT, typename... Args>
+ static typename DistrT::result_type Call(absl::MockingBitGen* gen,
+ Args&&... args) {
+ return gen->template Call<DistrT, FormatT>(std::forward<Args>(args)...);
+ }
+};
+
+} // namespace random_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_RANDOM_MOCKING_BIT_GEN_H_
diff --git a/absl/random/mocking_bit_gen_test.cc b/absl/random/mocking_bit_gen_test.cc
new file mode 100644
index 00000000..f0ffc9ac
--- /dev/null
+++ b/absl/random/mocking_bit_gen_test.cc
@@ -0,0 +1,347 @@
+//
+// Copyright 2018 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "absl/random/mocking_bit_gen.h"
+
+#include <numeric>
+#include <random>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest-spi.h"
+#include "gtest/gtest.h"
+#include "absl/random/bit_gen_ref.h"
+#include "absl/random/mock_distributions.h"
+#include "absl/random/random.h"
+
+namespace {
+using ::testing::Ne;
+using ::testing::Return;
+
+TEST(BasicMocking, AllDistributionsAreOverridable) {
+ absl::MockingBitGen gen;
+
+ EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20);
+ EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
+ .WillOnce(Return(20));
+ EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20);
+
+ EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
+ EXPECT_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0))
+ .WillOnce(Return(5.0));
+ EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
+
+ EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42);
+ EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
+ .WillOnce(Return(42));
+ EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42);
+
+ EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500);
+ EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(500));
+ EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500);
+
+ EXPECT_NE(absl::Bernoulli(gen, 0.000001), true);
+ EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001))
+ .WillOnce(Return(true));
+ EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true);
+
+ EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
+ EXPECT_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0))
+ .WillOnce(Return(1221));
+ EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
+
+ EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
+ EXPECT_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0))
+ .WillOnce(Return(0.001));
+ EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
+
+ EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 500000);
+ EXPECT_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2))
+ .WillOnce(Return(500000));
+ EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 500000);
+}
+
+TEST(BasicMocking, OnDistribution) {
+ absl::MockingBitGen gen;
+
+ EXPECT_NE(absl::Uniform<int>(gen, 1, 1000000), 20);
+ ON_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
+ .WillByDefault(Return(20));
+ EXPECT_EQ(absl::Uniform<int>(gen, 1, 1000000), 20);
+
+ EXPECT_NE(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
+ ON_CALL(absl::MockUniform<double>(), Call(gen, 0.0, 100.0))
+ .WillByDefault(Return(5.0));
+ EXPECT_EQ(absl::Uniform<double>(gen, 0.0, 100.0), 5.0);
+
+ EXPECT_NE(absl::Exponential<double>(gen, 1.0), 42);
+ ON_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
+ .WillByDefault(Return(42));
+ EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 42);
+
+ EXPECT_NE(absl::Poisson<int>(gen, 1.0), 500);
+ ON_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillByDefault(Return(500));
+ EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 500);
+
+ EXPECT_NE(absl::Bernoulli(gen, 0.000001), true);
+ ON_CALL(absl::MockBernoulli(), Call(gen, 0.000001))
+ .WillByDefault(Return(true));
+ EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true);
+
+ EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
+ ON_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0))
+ .WillByDefault(Return(1221));
+ EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
+
+ EXPECT_NE(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
+ ON_CALL(absl::MockGaussian<double>(), Call(gen, 0.0, 1.0))
+ .WillByDefault(Return(0.001));
+ EXPECT_EQ(absl::Gaussian<double>(gen, 0.0, 1.0), 0.001);
+
+ EXPECT_NE(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040);
+ ON_CALL(absl::MockLogUniform<int>(), Call(gen, 0, 1000000, 2))
+ .WillByDefault(Return(2040));
+ EXPECT_EQ(absl::LogUniform<int>(gen, 0, 1000000, 2), 2040);
+}
+
+TEST(BasicMocking, GMockMatchers) {
+ absl::MockingBitGen gen;
+
+ EXPECT_NE(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
+ ON_CALL(absl::MockZipf<int>(), Call(gen, 1000000, 2.0, 1.0))
+ .WillByDefault(Return(1221));
+ EXPECT_EQ(absl::Zipf<int>(gen, 1000000, 2.0, 1.0), 1221);
+}
+
+TEST(BasicMocking, OverridesWithMultipleGMockExpectations) {
+ absl::MockingBitGen gen;
+
+ EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 10000))
+ .WillOnce(Return(20))
+ .WillOnce(Return(40))
+ .WillOnce(Return(60));
+ EXPECT_EQ(absl::Uniform(gen, 1, 10000), 20);
+ EXPECT_EQ(absl::Uniform(gen, 1, 10000), 40);
+ EXPECT_EQ(absl::Uniform(gen, 1, 10000), 60);
+}
+
+TEST(BasicMocking, DefaultArgument) {
+ absl::MockingBitGen gen;
+
+ ON_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
+ .WillByDefault(Return(200));
+
+ EXPECT_EQ(absl::Exponential<double>(gen), 200);
+ EXPECT_EQ(absl::Exponential<double>(gen, 1.0), 200);
+}
+
+TEST(BasicMocking, MultipleGenerators) {
+ auto get_value = [](absl::BitGenRef gen_ref) {
+ return absl::Uniform(gen_ref, 1, 1000000);
+ };
+ absl::MockingBitGen unmocked_generator;
+ absl::MockingBitGen mocked_with_3;
+ absl::MockingBitGen mocked_with_11;
+
+ EXPECT_CALL(absl::MockUniform<int>(), Call(mocked_with_3, 1, 1000000))
+ .WillOnce(Return(3))
+ .WillRepeatedly(Return(17));
+ EXPECT_CALL(absl::MockUniform<int>(), Call(mocked_with_11, 1, 1000000))
+ .WillOnce(Return(11))
+ .WillRepeatedly(Return(17));
+
+ // Ensure that unmocked generator generates neither value.
+ int unmocked_value = get_value(unmocked_generator);
+ EXPECT_NE(unmocked_value, 3);
+ EXPECT_NE(unmocked_value, 11);
+ // Mocked generators should generate their mocked values.
+ EXPECT_EQ(get_value(mocked_with_3), 3);
+ EXPECT_EQ(get_value(mocked_with_11), 11);
+ // Ensure that the mocks have expired.
+ EXPECT_NE(get_value(mocked_with_3), 3);
+ EXPECT_NE(get_value(mocked_with_11), 11);
+}
+
+TEST(BasicMocking, MocksNotTrigeredForIncorrectTypes) {
+ absl::MockingBitGen gen;
+ EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(gen)).WillOnce(Return(42));
+
+ EXPECT_NE(absl::Uniform<uint16_t>(gen), 42); // Not mocked
+ EXPECT_EQ(absl::Uniform<uint32_t>(gen), 42); // Mock triggered
+}
+
+TEST(BasicMocking, FailsOnUnsatisfiedMocks) {
+ EXPECT_NONFATAL_FAILURE(
+ []() {
+ absl::MockingBitGen gen;
+ EXPECT_CALL(absl::MockExponential<double>(), Call(gen, 1.0))
+ .WillOnce(Return(3.0));
+ // Does not call absl::Exponential().
+ }(),
+ "unsatisfied and active");
+}
+
+TEST(OnUniform, RespectsUniformIntervalSemantics) {
+ absl::MockingBitGen gen;
+
+ EXPECT_CALL(absl::MockUniform<int>(),
+ Call(absl::IntervalClosed, gen, 1, 1000000))
+ .WillOnce(Return(301));
+ EXPECT_NE(absl::Uniform(gen, 1, 1000000), 301); // Not mocked
+ EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 1, 1000000), 301);
+}
+
+TEST(OnUniform, RespectsNoArgUnsignedShorthand) {
+ absl::MockingBitGen gen;
+ EXPECT_CALL(absl::MockUniform<uint32_t>(), Call(gen)).WillOnce(Return(42));
+ EXPECT_EQ(absl::Uniform<uint32_t>(gen), 42);
+}
+
+TEST(RepeatedlyModifier, ForceSnakeEyesForManyDice) {
+ auto roll_some_dice = [](absl::BitGenRef gen_ref) {
+ std::vector<int> results(16);
+ for (auto& r : results) {
+ r = absl::Uniform(absl::IntervalClosed, gen_ref, 1, 6);
+ }
+ return results;
+ };
+ std::vector<int> results;
+ absl::MockingBitGen gen;
+
+ // Without any mocked calls, not all dice roll a "6".
+ results = roll_some_dice(gen);
+ EXPECT_LT(std::accumulate(std::begin(results), std::end(results), 0),
+ results.size() * 6);
+
+ // Verify that we can force all "6"-rolls, with mocking.
+ ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalClosed, gen, 1, 6))
+ .WillByDefault(Return(6));
+ results = roll_some_dice(gen);
+ EXPECT_EQ(std::accumulate(std::begin(results), std::end(results), 0),
+ results.size() * 6);
+}
+
+TEST(WillOnce, DistinctCounters) {
+ absl::MockingBitGen gen;
+ EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
+ .Times(3)
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1000001, 2000000))
+ .Times(3)
+ .WillRepeatedly(Return(1));
+ EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1);
+ EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0);
+ EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1);
+ EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0);
+ EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1);
+ EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0);
+}
+
+TEST(TimesModifier, ModifierSaturatesAndExpires) {
+ EXPECT_NONFATAL_FAILURE(
+ []() {
+ absl::MockingBitGen gen;
+ EXPECT_CALL(absl::MockUniform<int>(), Call(gen, 1, 1000000))
+ .Times(3)
+ .WillRepeatedly(Return(15))
+ .RetiresOnSaturation();
+
+ EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15);
+ EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15);
+ EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15);
+ // Times(3) has expired - Should get a different value now.
+
+ EXPECT_NE(absl::Uniform(gen, 1, 1000000), 15);
+ }(),
+ "");
+}
+
+TEST(TimesModifier, Times0) {
+ absl::MockingBitGen gen;
+ EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.0)).Times(0);
+ EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).Times(0);
+}
+
+TEST(AnythingMatcher, MatchesAnyArgument) {
+ using testing::_;
+
+ {
+ absl::MockingBitGen gen;
+ ON_CALL(absl::MockUniform<int>(), Call(absl::IntervalClosed, gen, _, 1000))
+ .WillByDefault(Return(11));
+ ON_CALL(absl::MockUniform<int>(),
+ Call(absl::IntervalClosed, gen, _, Ne(1000)))
+ .WillByDefault(Return(99));
+
+ EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000000), 99);
+ EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000), 11);
+ }
+
+ {
+ absl::MockingBitGen gen;
+ ON_CALL(absl::MockUniform<int>(), Call(gen, 1, _))
+ .WillByDefault(Return(25));
+ ON_CALL(absl::MockUniform<int>(), Call(gen, Ne(1), _))
+ .WillByDefault(Return(99));
+ EXPECT_EQ(absl::Uniform(gen, 3, 1000000), 99);
+ EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 25);
+ }
+
+ {
+ absl::MockingBitGen gen;
+ ON_CALL(absl::MockUniform<int>(), Call(gen, _, _))
+ .WillByDefault(Return(145));
+ EXPECT_EQ(absl::Uniform(gen, 1, 1000), 145);
+ EXPECT_EQ(absl::Uniform(gen, 10, 1000), 145);
+ EXPECT_EQ(absl::Uniform(gen, 100, 1000), 145);
+ }
+}
+
+TEST(AnythingMatcher, WithWillByDefault) {
+ using testing::_;
+ absl::MockingBitGen gen;
+ std::vector<int> values = {11, 22, 33, 44, 55, 66, 77, 88, 99, 1010};
+
+ ON_CALL(absl::MockUniform<size_t>(), Call(gen, 0, _))
+ .WillByDefault(Return(0));
+ for (int i = 0; i < 100; i++) {
+ auto& elem = values[absl::Uniform(gen, 0u, values.size())];
+ EXPECT_EQ(elem, 11);
+ }
+}
+
+TEST(BasicMocking, WillByDefaultWithArgs) {
+ using testing::_;
+
+ absl::MockingBitGen gen;
+ ON_CALL(absl::MockPoisson<int>(), Call(gen, _))
+ .WillByDefault(
+ [](double lambda) { return static_cast<int>(lambda * 10); });
+ EXPECT_EQ(absl::Poisson<int>(gen, 1.7), 17);
+ EXPECT_EQ(absl::Poisson<int>(gen, 0.03), 0);
+}
+
+TEST(MockingBitGen, InSequenceSucceedsInOrder) {
+ absl::MockingBitGen gen;
+
+ testing::InSequence seq;
+
+ EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 1.0)).WillOnce(Return(3));
+ EXPECT_CALL(absl::MockPoisson<int>(), Call(gen, 2.0)).WillOnce(Return(4));
+
+ EXPECT_EQ(absl::Poisson<int>(gen, 1.0), 3);
+ EXPECT_EQ(absl::Poisson<int>(gen, 2.0), 4);
+}
+
+} // namespace
diff --git a/absl/random/poisson_distribution.h b/absl/random/poisson_distribution.h
index 66c75091..cb5f5d5d 100644
--- a/absl/random/poisson_distribution.h
+++ b/absl/random/poisson_distribution.h
@@ -22,13 +22,13 @@
#include <ostream>
#include <type_traits>
-#include "absl/random/internal/distribution_impl.h"
#include "absl/random/internal/fast_uniform_bits.h"
#include "absl/random/internal/fastmath.h"
+#include "absl/random/internal/generate_real.h"
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// absl::poisson_distribution:
// Generates discrete variates conforming to a Poisson distribution.
@@ -165,9 +165,9 @@ typename poisson_distribution<IntType>::result_type
poisson_distribution<IntType>::operator()(
URBG& g, // NOLINT(runtime/references)
const param_type& p) {
- using random_internal::PositiveValueT;
- using random_internal::RandU64ToDouble;
- using random_internal::SignedValueT;
+ using random_internal::GeneratePositiveTag;
+ using random_internal::GenerateRealFromBits;
+ using random_internal::GenerateSignedTag;
if (p.split_ != 0) {
// Use Knuth's algorithm with range splitting to avoid floating-point
@@ -187,7 +187,8 @@ poisson_distribution<IntType>::operator()(
for (int split = p.split_; split > 0; --split) {
double r = 1.0;
do {
- r *= RandU64ToDouble<PositiveValueT, true>(fast_u64_(g));
+ r *= GenerateRealFromBits<double, GeneratePositiveTag, true>(
+ fast_u64_(g)); // U(-1, 0)
++n;
} while (r > p.emu_);
--n;
@@ -206,10 +207,11 @@ poisson_distribution<IntType>::operator()(
// and k = max(f).
const double a = p.mean_ + 0.5;
for (;;) {
- const double u =
- RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)); // (0, 1)
- const double v =
- RandU64ToDouble<SignedValueT, false>(fast_u64_(g)); // (-1, 1)
+ const double u = GenerateRealFromBits<double, GeneratePositiveTag, false>(
+ fast_u64_(g)); // U(0, 1)
+ const double v = GenerateRealFromBits<double, GenerateSignedTag, false>(
+ fast_u64_(g)); // U(-1, 1)
+
const double x = std::floor(p.s_ * v / u + a);
if (x < 0) continue; // f(negative) = 0
const double rhs = x * p.lmu_;
@@ -250,7 +252,7 @@ std::basic_istream<CharT, Traits>& operator>>(
return is;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_POISSON_DISTRIBUTION_H_
diff --git a/absl/random/poisson_distribution_test.cc b/absl/random/poisson_distribution_test.cc
index 6d68999a..9d215fbc 100644
--- a/absl/random/poisson_distribution_test.cc
+++ b/absl/random/poisson_distribution_test.cc
@@ -339,7 +339,7 @@ std::string ZParamName(const ::testing::TestParamInfo<ZParam>& info) {
return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}});
}
-INSTANTIATE_TEST_SUITE_P(, PoissonDistributionZTest,
+INSTANTIATE_TEST_SUITE_P(All, PoissonDistributionZTest,
::testing::ValuesIn(GetZParams()), ZParamName);
// The PoissonDistributionChiSquaredTest class provides a basic test framework
@@ -468,7 +468,7 @@ TEST_P(PoissonDistributionChiSquaredTest, AbslPoissonDistribution) {
EXPECT_LE(failures, 4);
}
-INSTANTIATE_TEST_SUITE_P(, PoissonDistributionChiSquaredTest,
+INSTANTIATE_TEST_SUITE_P(All, PoissonDistributionChiSquaredTest,
::testing::Values(0.5, 1.0, 2.0, 10.0, 50.0, 51.0,
200.0));
diff --git a/absl/random/random.h b/absl/random/random.h
index 72a4cf5b..c8f326e6 100644
--- a/absl/random/random.h
+++ b/absl/random/random.h
@@ -41,7 +41,7 @@
#include "absl/random/seed_sequences.h" // IWYU pragma: export
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// absl::BitGen
@@ -183,7 +183,7 @@ using InsecureBitGen =
// discards the intermediate results.
// ---------------------------------------------------------------------------
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_RANDOM_H_
diff --git a/absl/random/seed_gen_exception.cc b/absl/random/seed_gen_exception.cc
index 5f01a30c..fdcb54a8 100644
--- a/absl/random/seed_gen_exception.cc
+++ b/absl/random/seed_gen_exception.cc
@@ -19,7 +19,7 @@
#include "absl/base/config.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
static constexpr const char kExceptionMessage[] =
"Failed generating seed-material for URBG.";
@@ -42,5 +42,5 @@ void ThrowSeedGenException() {
}
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/seed_gen_exception.h b/absl/random/seed_gen_exception.h
index 52afe6cc..53539005 100644
--- a/absl/random/seed_gen_exception.h
+++ b/absl/random/seed_gen_exception.h
@@ -28,8 +28,10 @@
#include <exception>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
//------------------------------------------------------------------------------
// SeedGenException
@@ -47,7 +49,7 @@ namespace random_internal {
[[noreturn]] void ThrowSeedGenException();
} // namespace random_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_SEED_GEN_EXCEPTION_H_
diff --git a/absl/random/seed_sequences.cc b/absl/random/seed_sequences.cc
index fb7eb8d1..426eafd3 100644
--- a/absl/random/seed_sequences.cc
+++ b/absl/random/seed_sequences.cc
@@ -17,7 +17,7 @@
#include "absl/random/internal/pool_urbg.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
SeedSeq MakeSeedSeq() {
SeedSeq::result_type seed_material[8];
@@ -25,5 +25,5 @@ SeedSeq MakeSeedSeq() {
return SeedSeq(std::begin(seed_material), std::end(seed_material));
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/random/seed_sequences.h b/absl/random/seed_sequences.h
index 73d075c0..ff1340cc 100644
--- a/absl/random/seed_sequences.h
+++ b/absl/random/seed_sequences.h
@@ -34,7 +34,7 @@
#include "absl/types/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// absl::SeedSeq
@@ -104,7 +104,7 @@ SeedSeq CreateSeedSeqFrom(URBG* urbg) {
//
SeedSeq MakeSeedSeq();
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_SEED_SEQUENCES_H_
diff --git a/absl/random/uniform_int_distribution.h b/absl/random/uniform_int_distribution.h
index 95eb04a4..da66564a 100644
--- a/absl/random/uniform_int_distribution.h
+++ b/absl/random/uniform_int_distribution.h
@@ -34,13 +34,13 @@
#include <type_traits>
#include "absl/base/optimization.h"
-#include "absl/random/internal/distribution_impl.h"
#include "absl/random/internal/fast_uniform_bits.h"
#include "absl/random/internal/iostream_state_saver.h"
#include "absl/random/internal/traits.h"
+#include "absl/random/internal/wide_multiply.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// absl::uniform_int_distribution<T>
//
@@ -269,7 +269,7 @@ uniform_int_distribution<IntType>::Generate(
return helper::hi(product);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_UNIFORM_INT_DISTRIBUTION_H_
diff --git a/absl/random/uniform_real_distribution.h b/absl/random/uniform_real_distribution.h
index 0ea3163a..5ba17b23 100644
--- a/absl/random/uniform_real_distribution.h
+++ b/absl/random/uniform_real_distribution.h
@@ -39,12 +39,13 @@
#include <limits>
#include <type_traits>
-#include "absl/random/internal/distribution_impl.h"
+#include "absl/meta/type_traits.h"
#include "absl/random/internal/fast_uniform_bits.h"
+#include "absl/random/internal/generate_real.h"
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// absl::uniform_real_distribution<T>
//
@@ -57,7 +58,7 @@ inline namespace lts_2019_08_08 {
//
// // Use the distribution to produce a value between 0.0 (inclusive)
// // and 1.0 (exclusive).
-// int value = absl::uniform_real_distribution<double>(0, 1)(gen);
+// double value = absl::uniform_real_distribution<double>(0, 1)(gen);
//
template <typename RealType = double>
class uniform_real_distribution {
@@ -77,6 +78,7 @@ class uniform_real_distribution {
// is not possible, so value generation cannot use the full range of the
// real type.
assert(range_ <= (std::numeric_limits<result_type>::max)());
+ assert(std::isfinite(range_));
}
result_type a() const { return lo_; }
@@ -152,10 +154,15 @@ template <typename URBG>
typename uniform_real_distribution<RealType>::result_type
uniform_real_distribution<RealType>::operator()(
URBG& gen, const param_type& p) { // NOLINT(runtime/references)
- using random_internal::PositiveValueT;
+ using random_internal::GeneratePositiveTag;
+ using random_internal::GenerateRealFromBits;
+ using real_type =
+ absl::conditional_t<std::is_same<RealType, float>::value, float, double>;
+
while (true) {
- const result_type sample = random_internal::RandU64ToReal<
- result_type>::template Value<PositiveValueT, true>(fast_u64_(gen));
+ const result_type sample =
+ GenerateRealFromBits<real_type, GeneratePositiveTag, true>(
+ fast_u64_(gen));
const result_type res = p.a() + (sample * p.range_);
if (res < p.b() || p.range_ <= 0 || !std::isfinite(p.range_)) {
return res;
@@ -189,7 +196,7 @@ std::basic_istream<CharT, Traits>& operator>>(
}
return is;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_
diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc
index 597f0ee5..a56374a6 100644
--- a/absl/random/uniform_real_distribution_test.cc
+++ b/absl/random/uniform_real_distribution_test.cc
@@ -54,7 +54,12 @@ namespace {
template <typename RealType>
class UniformRealDistributionTest : public ::testing::Test {};
+#if defined(__EMSCRIPTEN__)
+using RealTypes = ::testing::Types<float, double>;
+#else
using RealTypes = ::testing::Types<float, double, long double>;
+#endif // defined(__EMSCRIPTEN__)
+
TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes);
TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) {
@@ -156,6 +161,10 @@ TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) {
}
}
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4756) // Constant arithmetic overflow.
+#endif
TYPED_TEST(UniformRealDistributionTest, ViolatesPreconditionsDeathTest) {
#if GTEST_HAS_DEATH_TEST
// Hi < Lo
@@ -190,6 +199,9 @@ TYPED_TEST(UniformRealDistributionTest, ViolatesPreconditionsDeathTest) {
}
#endif // NDEBUG
}
+#ifdef _MSC_VER
+#pragma warning(pop) // warning(disable:4756)
+#endif
TYPED_TEST(UniformRealDistributionTest, TestMoments) {
constexpr int kSize = 1000000;
diff --git a/absl/random/zipf_distribution.h b/absl/random/zipf_distribution.h
index bba98e88..22ebc756 100644
--- a/absl/random/zipf_distribution.h
+++ b/absl/random/zipf_distribution.h
@@ -26,7 +26,7 @@
#include "absl/random/uniform_real_distribution.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// absl::zipf_distribution produces random integer-values in the range [0, k],
// distributed according to the discrete probability function:
@@ -265,7 +265,7 @@ std::basic_istream<CharT, Traits>& operator>>(
return is;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_RANDOM_ZIPF_DISTRIBUTION_H_
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel
new file mode 100644
index 00000000..2b83077d
--- /dev/null
+++ b/absl/status/BUILD.bazel
@@ -0,0 +1,65 @@
+#
+# Copyright 2017 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This package contains `absl::Status`.
+# It will expand later to have utilities around `Status` like `StatusOr`,
+# `StatusBuilder` and macros.
+
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
+load(
+ "//absl:copts/configure_copts.bzl",
+ "ABSL_DEFAULT_COPTS",
+ "ABSL_TEST_COPTS",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+cc_library(
+ name = "status",
+ srcs = [
+ "status.cc",
+ "status_payload_printer.cc",
+ ],
+ hdrs = [
+ "status.h",
+ "status_payload_printer.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
+ "//absl/container:inlined_vector",
+ "//absl/debugging:stacktrace",
+ "//absl/debugging:symbolize",
+ "//absl/strings",
+ "//absl/strings:cord",
+ "//absl/strings:str_format",
+ "//absl/types:optional",
+ ],
+)
+
+cc_test(
+ name = "status_test",
+ srcs = ["status_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ deps = [
+ ":status",
+ "//absl/strings",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt
new file mode 100644
index 00000000..f05cee5e
--- /dev/null
+++ b/absl/status/CMakeLists.txt
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+absl_cc_library(
+ NAME
+ status
+ HDRS
+ "status.h"
+ SRCS
+ "status.cc"
+ "status_payload_printer.h"
+ "status_payload_printer.cc"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::config
+ absl::core_headers
+ absl::raw_logging_internal
+ absl::inlined_vector
+ absl::stacktrace
+ absl::symbolize
+ absl::strings
+ absl::cord
+ absl::str_format
+ absl::optional
+ PUBLIC
+)
+
+absl_cc_test(
+ NAME
+ status_test
+ SRCS
+ "status_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::status
+ absl::strings
+ gmock_main
+)
diff --git a/absl/status/status.cc b/absl/status/status.cc
new file mode 100644
index 00000000..bbc1895e
--- /dev/null
+++ b/absl/status/status.cc
@@ -0,0 +1,439 @@
+// 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/status/status.h"
+
+#include <cassert>
+
+#include "absl/base/internal/raw_logging.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/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_split.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// The implementation was intentionally kept same as util::error::Code_Name()
+// to ease the migration.
+std::string StatusCodeToString(StatusCode code) {
+ switch (code) {
+ case StatusCode::kOk:
+ return "OK";
+ case StatusCode::kCancelled:
+ return "CANCELLED";
+ case StatusCode::kUnknown:
+ return "UNKNOWN";
+ case StatusCode::kInvalidArgument:
+ return "INVALID_ARGUMENT";
+ case StatusCode::kDeadlineExceeded:
+ return "DEADLINE_EXCEEDED";
+ case StatusCode::kNotFound:
+ return "NOT_FOUND";
+ case StatusCode::kAlreadyExists:
+ return "ALREADY_EXISTS";
+ case StatusCode::kPermissionDenied:
+ return "PERMISSION_DENIED";
+ case StatusCode::kUnauthenticated:
+ return "UNAUTHENTICATED";
+ case StatusCode::kResourceExhausted:
+ return "RESOURCE_EXHAUSTED";
+ case StatusCode::kFailedPrecondition:
+ return "FAILED_PRECONDITION";
+ case StatusCode::kAborted:
+ return "ABORTED";
+ case StatusCode::kOutOfRange:
+ return "OUT_OF_RANGE";
+ case StatusCode::kUnimplemented:
+ return "UNIMPLEMENTED";
+ case StatusCode::kInternal:
+ return "INTERNAL";
+ case StatusCode::kUnavailable:
+ return "UNAVAILABLE";
+ case StatusCode::kDataLoss:
+ return "DATA_LOSS";
+ default:
+ return "";
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, StatusCode code) {
+ return os << StatusCodeToString(code);
+}
+
+namespace status_internal {
+
+static int FindPayloadIndexByUrl(const Payloads* payloads,
+ absl::string_view type_url) {
+ if (payloads == nullptr) return -1;
+
+ for (int i = 0; i < payloads->size(); ++i) {
+ if ((*payloads)[i].type_url == type_url) return i;
+ }
+
+ return -1;
+}
+
+// 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();
+ int index = status_internal::FindPayloadIndexByUrl(payloads, type_url);
+ if (index != -1) return (*payloads)[index].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>();
+ }
+
+ int index =
+ status_internal::FindPayloadIndexByUrl(rep->payloads.get(), type_url);
+ if (index != -1) {
+ (*rep->payloads)[index].payload = std::move(payload);
+ return;
+ }
+
+ rep->payloads->push_back({std::string(type_url), std::move(payload)});
+}
+
+bool Status::ErasePayload(absl::string_view type_url) {
+ int index = status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url);
+ if (index != -1) {
+ GetPayloads()->erase(GetPayloads()->begin() + index);
+ return true;
+ }
+
+ return false;
+}
+
+void Status::ForEachPayload(
+ const std::function<void(absl::string_view, const absl::Cord&)>& visitor)
+ const {
+ if (auto* payloads = GetPayloads()) {
+ bool in_reverse =
+ payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6;
+
+ for (int index = 0; index < payloads->size(); ++index) {
+ const auto& elem =
+ (*payloads)[in_reverse ? payloads->size() - 1 - index : index];
+
+#ifdef NDEBUG
+ visitor(elem.type_url, elem.payload);
+#else
+ // In debug mode invaldiate the type url to prevent users from relying on
+ // this std::string lifetime.
+
+ // NOLINTNEXTLINE intentional extra conversion to force temporary.
+ visitor(std::string(elem.type_url), elem.payload);
+#endif // NDEBUG
+ }
+ }
+}
+
+const std::string* Status::EmptyString() {
+ static std::string* empty_string = new std::string();
+ return empty_string;
+}
+
+constexpr const char Status::kMovedFromString[];
+
+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;
+ }
+}
+
+uintptr_t Status::NewRep(absl::StatusCode code, absl::string_view msg,
+ std::unique_ptr<status_internal::Payloads> payloads) {
+ status_internal::StatusRep* rep = new status_internal::StatusRep;
+ rep->ref.store(1, std::memory_order_relaxed);
+ rep->code = code;
+ rep->message.assign(msg.data(), msg.size());
+ rep->payloads = std::move(payloads);
+ return PointerToRep(rep);
+}
+
+Status::Status(absl::StatusCode code, absl::string_view msg)
+ : rep_(CodeToInlinedRep(code)) {
+ if (code != absl::StatusCode::kOk && !msg.empty()) {
+ rep_ = NewRep(code, msg, nullptr);
+ }
+}
+
+int Status::raw_code() const {
+ if (IsInlined(rep_)) {
+ return static_cast<int>(InlinedRepToCode(rep_));
+ }
+ status_internal::StatusRep* rep = RepToPointer(rep_);
+ return static_cast<int>(rep->code);
+}
+
+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_ = NewRep(static_cast<absl::StatusCode>(raw_code()),
+ absl::string_view(), nullptr);
+ return;
+ }
+
+ 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);
+ }
+ rep_ = NewRep(rep->code, message(), std::move(payloads));
+ 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() const {
+ std::string text;
+ absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message());
+ status_internal::StatusPayloadPrinter printer =
+ status_internal::GetStatusPayloadPrinter();
+ this->ForEachPayload([&](absl::string_view type_url,
+ const absl::Cord& payload) {
+ absl::optional<std::string> result;
+ if (printer) result = printer(type_url, payload);
+ absl::StrAppend(
+ &text, " [", type_url, "='",
+ result.has_value() ? *result : absl::CHexEscape(std::string(payload)),
+ "']");
+ });
+
+ return text;
+}
+
+std::ostream& operator<<(std::ostream& os, const Status& x) {
+ os << x.ToString();
+ return os;
+}
+
+Status AbortedError(absl::string_view message) {
+ return Status(absl::StatusCode::kAborted, message);
+}
+
+Status AlreadyExistsError(absl::string_view message) {
+ return Status(absl::StatusCode::kAlreadyExists, message);
+}
+
+Status CancelledError(absl::string_view message) {
+ return Status(absl::StatusCode::kCancelled, message);
+}
+
+Status DataLossError(absl::string_view message) {
+ return Status(absl::StatusCode::kDataLoss, message);
+}
+
+Status DeadlineExceededError(absl::string_view message) {
+ return Status(absl::StatusCode::kDeadlineExceeded, message);
+}
+
+Status FailedPreconditionError(absl::string_view message) {
+ return Status(absl::StatusCode::kFailedPrecondition, message);
+}
+
+Status InternalError(absl::string_view message) {
+ return Status(absl::StatusCode::kInternal, message);
+}
+
+Status InvalidArgumentError(absl::string_view message) {
+ return Status(absl::StatusCode::kInvalidArgument, message);
+}
+
+Status NotFoundError(absl::string_view message) {
+ return Status(absl::StatusCode::kNotFound, message);
+}
+
+Status OutOfRangeError(absl::string_view message) {
+ return Status(absl::StatusCode::kOutOfRange, message);
+}
+
+Status PermissionDeniedError(absl::string_view message) {
+ return Status(absl::StatusCode::kPermissionDenied, message);
+}
+
+Status ResourceExhaustedError(absl::string_view message) {
+ return Status(absl::StatusCode::kResourceExhausted, message);
+}
+
+Status UnauthenticatedError(absl::string_view message) {
+ return Status(absl::StatusCode::kUnauthenticated, message);
+}
+
+Status UnavailableError(absl::string_view message) {
+ return Status(absl::StatusCode::kUnavailable, message);
+}
+
+Status UnimplementedError(absl::string_view message) {
+ return Status(absl::StatusCode::kUnimplemented, message);
+}
+
+Status UnknownError(absl::string_view message) {
+ return Status(absl::StatusCode::kUnknown, message);
+}
+
+bool IsAborted(const Status& status) {
+ return status.code() == absl::StatusCode::kAborted;
+}
+
+bool IsAlreadyExists(const Status& status) {
+ return status.code() == absl::StatusCode::kAlreadyExists;
+}
+
+bool IsCancelled(const Status& status) {
+ return status.code() == absl::StatusCode::kCancelled;
+}
+
+bool IsDataLoss(const Status& status) {
+ return status.code() == absl::StatusCode::kDataLoss;
+}
+
+bool IsDeadlineExceeded(const Status& status) {
+ return status.code() == absl::StatusCode::kDeadlineExceeded;
+}
+
+bool IsFailedPrecondition(const Status& status) {
+ return status.code() == absl::StatusCode::kFailedPrecondition;
+}
+
+bool IsInternal(const Status& status) {
+ return status.code() == absl::StatusCode::kInternal;
+}
+
+bool IsInvalidArgument(const Status& status) {
+ return status.code() == absl::StatusCode::kInvalidArgument;
+}
+
+bool IsNotFound(const Status& status) {
+ return status.code() == absl::StatusCode::kNotFound;
+}
+
+bool IsOutOfRange(const Status& status) {
+ return status.code() == absl::StatusCode::kOutOfRange;
+}
+
+bool IsPermissionDenied(const Status& status) {
+ return status.code() == absl::StatusCode::kPermissionDenied;
+}
+
+bool IsResourceExhausted(const Status& status) {
+ return status.code() == absl::StatusCode::kResourceExhausted;
+}
+
+bool IsUnauthenticated(const Status& status) {
+ return status.code() == absl::StatusCode::kUnauthenticated;
+}
+
+bool IsUnavailable(const Status& status) {
+ return status.code() == absl::StatusCode::kUnavailable;
+}
+
+bool IsUnimplemented(const Status& status) {
+ return status.code() == absl::StatusCode::kUnimplemented;
+}
+
+bool IsUnknown(const Status& status) {
+ return status.code() == absl::StatusCode::kUnknown;
+}
+
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/status/status.h b/absl/status/status.h
new file mode 100644
index 00000000..9706d4ba
--- /dev/null
+++ b/absl/status/status.h
@@ -0,0 +1,428 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef ABSL_STATUS_STATUS_H_
+#define ABSL_STATUS_STATUS_H_
+
+#include <iostream>
+#include <string>
+
+#include "absl/container/inlined_vector.h"
+#include "absl/strings/cord.h"
+#include "absl/types/optional.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+enum class StatusCode : int {
+ kOk = 0,
+ kCancelled = 1,
+ kUnknown = 2,
+ kInvalidArgument = 3,
+ kDeadlineExceeded = 4,
+ kNotFound = 5,
+ kAlreadyExists = 6,
+ kPermissionDenied = 7,
+ kResourceExhausted = 8,
+ kFailedPrecondition = 9,
+ kAborted = 10,
+ kOutOfRange = 11,
+ kUnimplemented = 12,
+ kInternal = 13,
+ kUnavailable = 14,
+ kDataLoss = 15,
+ kUnauthenticated = 16,
+ kDoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ = 20
+};
+
+// Returns the name for the status code, or "" if it is an unknown value.
+std::string StatusCodeToString(StatusCode code);
+
+// Streams StatusCodeToString(code) to `os`.
+std::ostream& operator<<(std::ostream& os, StatusCode code);
+
+namespace status_internal {
+
+// Container for status payloads.
+struct Payload {
+ std::string type_url;
+ absl::Cord payload;
+};
+
+using Payloads = absl::InlinedVector<Payload, 1>;
+
+// Reference-counted representation of Status data.
+struct StatusRep {
+ std::atomic<int32_t> ref;
+ absl::StatusCode code;
+ std::string message;
+ std::unique_ptr<status_internal::Payloads> payloads;
+};
+
+absl::StatusCode MapToLocalCode(int value);
+} // namespace status_internal
+
+class ABSL_MUST_USE_RESULT Status final {
+ public:
+ // Creates an OK status with no message or payload.
+ Status();
+
+ // Create a status in the canonical error space with the specified code and
+ // error message. If `code == util::error::OK`, `msg` is ignored and an
+ // object identical to an OK status is constructed.
+ //
+ // `msg` must be in UTF-8. The implementation may complain (e.g.,
+ // by printing a warning) if it is not.
+ Status(absl::StatusCode code, absl::string_view msg);
+
+ Status(const Status&);
+ Status& operator=(const Status& x);
+
+ // Move operations.
+ // The moved-from state is valid but unspecified.
+ Status(Status&&) noexcept;
+ Status& operator=(Status&&);
+
+ ~Status();
+
+ // If `this->ok()`, stores `new_status` into *this. If `!this->ok()`,
+ // preserves the current data. May, in the future, augment the current status
+ // with additional information about `new_status`.
+ //
+ // Convenient way of keeping track of the first error encountered.
+ // Instead of:
+ // if (overall_status.ok()) overall_status = new_status
+ // Use:
+ // overall_status.Update(new_status);
+ //
+ // Style guide exception for rvalue reference granted in CL 153567220.
+ void Update(const Status& new_status);
+ void Update(Status&& new_status);
+
+ // Returns true if the Status is OK.
+ ABSL_MUST_USE_RESULT bool ok() const;
+
+ // Returns the (canonical) error code.
+ absl::StatusCode code() const;
+
+ // Returns the raw (canonical) error code which could be out of the range of
+ // the local `absl::StatusCode` enum. NOTE: This should only be called when
+ // converting to wire format. Use `code` for error handling.
+ int raw_code() const;
+
+ // Returns the error message. Note: prefer ToString() for debug logging.
+ // This message rarely describes the error code. It is not unusual for the
+ // error message to be the empty std::string.
+ absl::string_view message() const;
+
+ friend bool operator==(const Status&, const Status&);
+ friend bool operator!=(const Status&, const Status&);
+
+ // Returns a combination of the error code name, the message and the payloads.
+ // You can expect the code name and the message to be substrings of the
+ // result, and the payloads to be printed by the registered printer extensions
+ // if they are recognized.
+ // WARNING: Do not depend on the exact format of the result of `ToString()`
+ // which is subject to change.
+ std::string ToString() const;
+
+ // Ignores any errors. This method does nothing except potentially suppress
+ // complaints from any tools that are checking that errors are not dropped on
+ // the floor.
+ void IgnoreError() const;
+
+ // Swap the contents of `a` with `b`
+ friend void swap(Status& a, Status& b);
+
+ // Payload management APIs
+
+ // Type URL should be unique and follow the naming convention below:
+ // The idea of type URL comes from `google.protobuf.Any`
+ // (https://developers.google.com/protocol-buffers/docs/proto3#any). The
+ // type URL should be globally unique and follow the format of URL
+ // (https://en.wikipedia.org/wiki/URL). The default type URL for a given
+ // protobuf message type is "type.googleapis.com/packagename.messagename". For
+ // other custom wire formats, users should define the format of type URL in a
+ // similar practice so as to minimize the chance of conflict between type
+ // URLs. Users should make sure that the type URL can be mapped to a concrete
+ // C++ type if they want to deserialize the payload and read it effectively.
+
+ // Gets the payload based for `type_url` key, if it is present.
+ absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
+
+ // Sets the payload for `type_url` key for a non-ok status, overwriting any
+ // existing payload for `type_url`.
+ //
+ // NOTE: Does nothing if the Status is ok.
+ void SetPayload(absl::string_view type_url, absl::Cord payload);
+
+ // Erases the payload corresponding to the `type_url` key. Returns true if
+ // the payload was present.
+ bool ErasePayload(absl::string_view type_url);
+
+ // Iterates over the stored payloads and calls `visitor(type_key, payload)`
+ // for each one.
+ //
+ // NOTE: The order of calls to `visitor` is not specified and may change at
+ // any time.
+ //
+ // NOTE: Any mutation on the same 'Status' object during visitation is
+ // forbidden and could result in undefined behavior.
+ void ForEachPayload(
+ const std::function<void(absl::string_view, const absl::Cord&)>& visitor)
+ const;
+
+ private:
+ friend Status CancelledError();
+
+ // Creates a status in the canonical error space with the specified
+ // code, and an empty error message.
+ explicit Status(absl::StatusCode code);
+
+ static void UnrefNonInlined(uintptr_t 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();
+
+ // Takes ownership of payload.
+ static uintptr_t NewRep(absl::StatusCode code, absl::string_view msg,
+ std::unique_ptr<status_internal::Payloads> payload);
+ static bool EqualsSlow(const absl::Status& a, const absl::Status& b);
+
+ // MSVC 14.0 limitation requires the const.
+ static constexpr const char kMovedFromString[] =
+ "Status accessed after move.";
+
+ static const std::string* EmptyString();
+ static const std::string* MovedFromString();
+
+ // Returns whether rep contains an inlined representation.
+ // See rep_ for details.
+ static 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();
+
+ // 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);
+
+ // 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);
+
+ // Returns std::string for non-ok Status.
+ std::string ToStringSlow() const;
+
+ // Status supports two different representations.
+ // - When the low bit is off 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.
+ // In this case all the data comes from a heap allocated Rep object.
+ // (rep_ - 1) is a status_internal::StatusRep* pointer to that structure.
+ uintptr_t rep_;
+};
+
+// Returns an OK status, equivalent to a default constructed instance.
+Status OkStatus();
+
+// Prints a human-readable representation of `x` to `os`.
+std::ostream& operator<<(std::ostream& os, const Status& x);
+
+// -----------------------------------------------------------------
+// Implementation details follow
+
+inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {}
+
+inline Status::Status(absl::StatusCode code) : rep_(CodeToInlinedRep(code)) {}
+
+inline Status::Status(const Status& x) : rep_(x.rep_) { Ref(rep_); }
+
+inline Status& Status::operator=(const Status& x) {
+ uintptr_t old_rep = rep_;
+ if (x.rep_ != old_rep) {
+ Ref(x.rep_);
+ rep_ = x.rep_;
+ Unref(old_rep);
+ }
+ return *this;
+}
+
+inline Status::Status(Status&& x) noexcept : rep_(x.rep_) {
+ x.rep_ = MovedFromRep();
+}
+
+inline Status& Status::operator=(Status&& x) {
+ uintptr_t old_rep = rep_;
+ rep_ = x.rep_;
+ x.rep_ = MovedFromRep();
+ Unref(old_rep);
+ return *this;
+}
+
+inline void Status::Update(const Status& new_status) {
+ if (ok()) {
+ *this = new_status;
+ }
+}
+
+inline void Status::Update(Status&& new_status) {
+ if (ok()) {
+ *this = std::move(new_status);
+ }
+}
+
+inline Status::~Status() { Unref(rep_); }
+
+inline bool Status::ok() const {
+ return rep_ == CodeToInlinedRep(absl::StatusCode::kOk);
+}
+
+inline absl::string_view Status::message() const {
+ return !IsInlined(rep_)
+ ? 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);
+}
+
+inline bool operator!=(const Status& lhs, const Status& rhs) {
+ return !(lhs == rhs);
+}
+
+inline std::string Status::ToString() const {
+ return ok() ? "OK" : ToStringSlow();
+}
+
+inline void Status::IgnoreError() const {
+ // no-op
+}
+
+inline void swap(absl::Status& a, absl::Status& b) {
+ using std::swap;
+ swap(a.rep_, b.rep_);
+}
+
+inline const status_internal::Payloads* Status::GetPayloads() const {
+ return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
+}
+
+inline status_internal::Payloads* Status::GetPayloads() {
+ return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get();
+}
+
+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 uintptr_t Status::MovedFromRep() {
+ return CodeToInlinedRep(absl::StatusCode::kInternal) | 2;
+}
+
+inline uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) {
+ return static_cast<uintptr_t>(code) << 2;
+}
+
+inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) {
+ assert(IsInlined(rep));
+ return static_cast<absl::StatusCode>(rep >> 2);
+}
+
+inline status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) {
+ assert(!IsInlined(rep));
+ return reinterpret_cast<status_internal::StatusRep*>(rep - 1);
+}
+
+inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) {
+ return reinterpret_cast<uintptr_t>(rep) + 1;
+}
+
+inline void Status::Ref(uintptr_t rep) {
+ if (!IsInlined(rep)) {
+ RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed);
+ }
+}
+
+inline void Status::Unref(uintptr_t rep) {
+ if (!IsInlined(rep)) {
+ UnrefNonInlined(rep);
+ }
+}
+
+inline Status OkStatus() { return Status(); }
+
+// Each of the functions below creates a Status object with a particular error
+// code and the given message. The error code of the returned status object
+// matches the name of the function.
+Status AbortedError(absl::string_view message);
+Status AlreadyExistsError(absl::string_view message);
+Status CancelledError(absl::string_view message);
+Status DataLossError(absl::string_view message);
+Status DeadlineExceededError(absl::string_view message);
+Status FailedPreconditionError(absl::string_view message);
+Status InternalError(absl::string_view message);
+Status InvalidArgumentError(absl::string_view message);
+Status NotFoundError(absl::string_view message);
+Status OutOfRangeError(absl::string_view message);
+Status PermissionDeniedError(absl::string_view message);
+Status ResourceExhaustedError(absl::string_view message);
+Status UnauthenticatedError(absl::string_view message);
+Status UnavailableError(absl::string_view message);
+Status UnimplementedError(absl::string_view message);
+Status UnknownError(absl::string_view message);
+
+// Creates a `Status` object with the `absl::StatusCode::kCancelled` error code
+// and an empty message. It is provided only for efficiency, given that
+// message-less kCancelled errors are common in the infrastructure.
+inline Status CancelledError() { return Status(absl::StatusCode::kCancelled); }
+
+// Each of the functions below returns true if the given status matches the
+// error code implied by the function's name.
+ABSL_MUST_USE_RESULT bool IsAborted(const Status& status);
+ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status);
+ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status);
+ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status);
+ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status);
+ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status);
+ABSL_MUST_USE_RESULT bool IsInternal(const Status& status);
+ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status);
+ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status);
+ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status);
+ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status);
+ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status);
+ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status);
+ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status);
+ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status);
+ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status);
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STATUS_STATUS_H_
diff --git a/absl/status/status_payload_printer.cc b/absl/status/status_payload_printer.cc
new file mode 100644
index 00000000..ad96d76a
--- /dev/null
+++ b/absl/status/status_payload_printer.cc
@@ -0,0 +1,43 @@
+// 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/status/status_payload_printer.h"
+
+#include <atomic>
+
+#include "absl/base/attributes.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace status_internal {
+
+namespace {
+// Tried constant initialized global variable but it doesn't work with Lexan
+// (MSVC's `std::atomic` has trouble constant initializing).
+std::atomic<StatusPayloadPrinter>& GetStatusPayloadPrinterStorage() {
+ ABSL_CONST_INIT static std::atomic<StatusPayloadPrinter> instance{nullptr};
+ return instance;
+}
+} // namespace
+
+void SetStatusPayloadPrinter(StatusPayloadPrinter printer) {
+ GetStatusPayloadPrinterStorage().store(printer, std::memory_order_relaxed);
+}
+
+StatusPayloadPrinter GetStatusPayloadPrinter() {
+ return GetStatusPayloadPrinterStorage().load(std::memory_order_relaxed);
+}
+
+} // namespace status_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/status/status_payload_printer.h b/absl/status/status_payload_printer.h
new file mode 100644
index 00000000..5e0937f6
--- /dev/null
+++ b/absl/status/status_payload_printer.h
@@ -0,0 +1,51 @@
+// Copyright 2019 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_
+#define ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_
+
+#include <string>
+
+#include "absl/strings/cord.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace status_internal {
+
+// By default, `Status::ToString` and `operator<<(Status)` print a payload by
+// dumping the type URL and the raw bytes. To help debugging, we provide an
+// extension point, which is a global printer function that can be set by users
+// to specify how to print payloads. The function takes the type URL and the
+// payload as input, and should return a valid human-readable string on success
+// or `absl::nullopt` on failure (in which case it falls back to the default
+// approach of printing the raw bytes).
+// 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&);
+
+// 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.
+void SetStatusPayloadPrinter(StatusPayloadPrinter);
+
+// Returns the global payload printer if previously set, otherwise `nullptr`.
+StatusPayloadPrinter GetStatusPayloadPrinter();
+
+} // namespace status_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_
diff --git a/absl/status/status_test.cc b/absl/status/status_test.cc
new file mode 100644
index 00000000..7cc65e45
--- /dev/null
+++ b/absl/status/status_test.cc
@@ -0,0 +1,401 @@
+// 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/status/status.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/str_cat.h"
+
+namespace {
+
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Optional;
+using ::testing::UnorderedElementsAreArray;
+
+TEST(StatusCode, InsertionOperator) {
+ const absl::StatusCode code = absl::StatusCode::kUnknown;
+ std::ostringstream oss;
+ oss << code;
+ EXPECT_EQ(oss.str(), absl::StatusCodeToString(code));
+}
+
+// This structure holds the details for testing a single error code,
+// its creator, and its classifier.
+struct ErrorTest {
+ absl::StatusCode code;
+ using Creator = absl::Status (*)(absl::string_view);
+ using Classifier = bool (*)(const absl::Status&);
+ Creator creator;
+ Classifier classifier;
+};
+
+constexpr ErrorTest kErrorTests[]{
+ {absl::StatusCode::kCancelled, absl::CancelledError, absl::IsCancelled},
+ {absl::StatusCode::kUnknown, absl::UnknownError, absl::IsUnknown},
+ {absl::StatusCode::kInvalidArgument, absl::InvalidArgumentError,
+ absl::IsInvalidArgument},
+ {absl::StatusCode::kDeadlineExceeded, absl::DeadlineExceededError,
+ absl::IsDeadlineExceeded},
+ {absl::StatusCode::kNotFound, absl::NotFoundError, absl::IsNotFound},
+ {absl::StatusCode::kAlreadyExists, absl::AlreadyExistsError,
+ absl::IsAlreadyExists},
+ {absl::StatusCode::kPermissionDenied, absl::PermissionDeniedError,
+ absl::IsPermissionDenied},
+ {absl::StatusCode::kResourceExhausted, absl::ResourceExhaustedError,
+ absl::IsResourceExhausted},
+ {absl::StatusCode::kFailedPrecondition, absl::FailedPreconditionError,
+ absl::IsFailedPrecondition},
+ {absl::StatusCode::kAborted, absl::AbortedError, absl::IsAborted},
+ {absl::StatusCode::kOutOfRange, absl::OutOfRangeError, absl::IsOutOfRange},
+ {absl::StatusCode::kUnimplemented, absl::UnimplementedError,
+ absl::IsUnimplemented},
+ {absl::StatusCode::kInternal, absl::InternalError, absl::IsInternal},
+ {absl::StatusCode::kUnavailable, absl::UnavailableError,
+ absl::IsUnavailable},
+ {absl::StatusCode::kDataLoss, absl::DataLossError, absl::IsDataLoss},
+ {absl::StatusCode::kUnauthenticated, absl::UnauthenticatedError,
+ absl::IsUnauthenticated},
+};
+
+TEST(Status, CreateAndClassify) {
+ for (const auto& test : kErrorTests) {
+ SCOPED_TRACE(absl::StatusCodeToString(test.code));
+
+ // Ensure that the creator does, in fact, create status objects with the
+ // expected error code and message.
+ std::string message =
+ absl::StrCat("error code ", test.code, " test message");
+ absl::Status status = test.creator(message);
+ EXPECT_EQ(test.code, status.code());
+ EXPECT_EQ(message, status.message());
+
+ // Ensure that the classifier returns true for a status produced by the
+ // creator.
+ EXPECT_TRUE(test.classifier(status));
+
+ // Ensure that the classifier returns false for status with a different
+ // code.
+ for (const auto& other : kErrorTests) {
+ if (other.code != test.code) {
+ EXPECT_FALSE(test.classifier(absl::Status(other.code, "")))
+ << " other.code = " << other.code;
+ }
+ }
+ }
+}
+
+TEST(Status, DefaultConstructor) {
+ absl::Status status;
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(absl::StatusCode::kOk, status.code());
+ EXPECT_EQ("", status.message());
+}
+
+TEST(Status, OkStatus) {
+ absl::Status status = absl::OkStatus();
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(absl::StatusCode::kOk, status.code());
+ EXPECT_EQ("", status.message());
+}
+
+TEST(Status, ConstructorWithCodeMessage) {
+ {
+ absl::Status status(absl::StatusCode::kCancelled, "");
+ EXPECT_FALSE(status.ok());
+ EXPECT_EQ(absl::StatusCode::kCancelled, status.code());
+ EXPECT_EQ("", status.message());
+ }
+ {
+ absl::Status status(absl::StatusCode::kInternal, "message");
+ EXPECT_FALSE(status.ok());
+ EXPECT_EQ(absl::StatusCode::kInternal, status.code());
+ EXPECT_EQ("message", status.message());
+ }
+}
+
+TEST(Status, ConstructOutOfRangeCode) {
+ const int kRawCode = 9999;
+ absl::Status status(static_cast<absl::StatusCode>(kRawCode), "");
+ EXPECT_EQ(absl::StatusCode::kUnknown, status.code());
+ EXPECT_EQ(kRawCode, status.raw_code());
+}
+
+constexpr char kUrl1[] = "url.payload.1";
+constexpr char kUrl2[] = "url.payload.2";
+constexpr char kUrl3[] = "url.payload.3";
+constexpr char kUrl4[] = "url.payload.xx";
+
+constexpr char kPayload1[] = "aaaaa";
+constexpr char kPayload2[] = "bbbbb";
+constexpr char kPayload3[] = "ccccc";
+
+using PayloadsVec = std::vector<std::pair<std::string, absl::Cord>>;
+
+TEST(Status, TestGetSetPayload) {
+ absl::Status ok_status = absl::OkStatus();
+ ok_status.SetPayload(kUrl1, absl::Cord(kPayload1));
+ ok_status.SetPayload(kUrl2, absl::Cord(kPayload2));
+
+ EXPECT_FALSE(ok_status.GetPayload(kUrl1));
+ EXPECT_FALSE(ok_status.GetPayload(kUrl2));
+
+ absl::Status bad_status(absl::StatusCode::kInternal, "fail");
+ bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
+ bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
+
+ EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload1)));
+ EXPECT_THAT(bad_status.GetPayload(kUrl2), Optional(Eq(kPayload2)));
+
+ EXPECT_FALSE(bad_status.GetPayload(kUrl3));
+
+ bad_status.SetPayload(kUrl1, absl::Cord(kPayload3));
+ EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload3)));
+
+ // Testing dynamically generated type_url
+ bad_status.SetPayload(absl::StrCat(kUrl1, ".1"), absl::Cord(kPayload1));
+ EXPECT_THAT(bad_status.GetPayload(absl::StrCat(kUrl1, ".1")),
+ Optional(Eq(kPayload1)));
+}
+
+TEST(Status, TestErasePayload) {
+ absl::Status bad_status(absl::StatusCode::kInternal, "fail");
+ bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
+ bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
+ bad_status.SetPayload(kUrl3, absl::Cord(kPayload3));
+
+ EXPECT_FALSE(bad_status.ErasePayload(kUrl4));
+
+ EXPECT_TRUE(bad_status.GetPayload(kUrl2));
+ EXPECT_TRUE(bad_status.ErasePayload(kUrl2));
+ EXPECT_FALSE(bad_status.GetPayload(kUrl2));
+ EXPECT_FALSE(bad_status.ErasePayload(kUrl2));
+
+ EXPECT_TRUE(bad_status.ErasePayload(kUrl1));
+ EXPECT_TRUE(bad_status.ErasePayload(kUrl3));
+
+ bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
+ EXPECT_TRUE(bad_status.ErasePayload(kUrl1));
+}
+
+TEST(Status, TestComparePayloads) {
+ absl::Status bad_status1(absl::StatusCode::kInternal, "fail");
+ bad_status1.SetPayload(kUrl1, absl::Cord(kPayload1));
+ bad_status1.SetPayload(kUrl2, absl::Cord(kPayload2));
+ bad_status1.SetPayload(kUrl3, absl::Cord(kPayload3));
+
+ absl::Status bad_status2(absl::StatusCode::kInternal, "fail");
+ bad_status2.SetPayload(kUrl2, absl::Cord(kPayload2));
+ bad_status2.SetPayload(kUrl3, absl::Cord(kPayload3));
+ bad_status2.SetPayload(kUrl1, absl::Cord(kPayload1));
+
+ EXPECT_EQ(bad_status1, bad_status2);
+}
+
+PayloadsVec AllVisitedPayloads(const absl::Status& s) {
+ PayloadsVec result;
+
+ s.ForEachPayload([&](absl::string_view type_url, const absl::Cord& payload) {
+ result.push_back(std::make_pair(std::string(type_url), payload));
+ });
+
+ return result;
+}
+
+TEST(Status, TestForEachPayload) {
+ absl::Status bad_status(absl::StatusCode::kInternal, "fail");
+ bad_status.SetPayload(kUrl1, absl::Cord(kPayload1));
+ bad_status.SetPayload(kUrl2, absl::Cord(kPayload2));
+ bad_status.SetPayload(kUrl3, absl::Cord(kPayload3));
+
+ int count = 0;
+
+ bad_status.ForEachPayload(
+ [&count](absl::string_view, const absl::Cord&) { ++count; });
+
+ EXPECT_EQ(count, 3);
+
+ PayloadsVec expected_payloads = {{kUrl1, absl::Cord(kPayload1)},
+ {kUrl2, absl::Cord(kPayload2)},
+ {kUrl3, absl::Cord(kPayload3)}};
+
+ // Test that we visit all the payloads in the status.
+ PayloadsVec visited_payloads = AllVisitedPayloads(bad_status);
+ EXPECT_THAT(visited_payloads, UnorderedElementsAreArray(expected_payloads));
+
+ // Test that visitation order is not consistent between run.
+ std::vector<absl::Status> scratch;
+ while (true) {
+ scratch.emplace_back(absl::StatusCode::kInternal, "fail");
+
+ scratch.back().SetPayload(kUrl1, absl::Cord(kPayload1));
+ scratch.back().SetPayload(kUrl2, absl::Cord(kPayload2));
+ scratch.back().SetPayload(kUrl3, absl::Cord(kPayload3));
+
+ if (AllVisitedPayloads(scratch.back()) != visited_payloads) {
+ break;
+ }
+ }
+}
+
+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(),
+ AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"),
+ HasSubstr("[bar='\\xff']")));
+}
+
+TEST(Status, CopyConstructor) {
+ {
+ absl::Status status;
+ absl::Status copy(status);
+ EXPECT_EQ(copy, status);
+ }
+ {
+ absl::Status status(absl::StatusCode::kInvalidArgument, "message");
+ absl::Status copy(status);
+ EXPECT_EQ(copy, status);
+ }
+ {
+ absl::Status status(absl::StatusCode::kInvalidArgument, "message");
+ status.SetPayload(kUrl1, absl::Cord(kPayload1));
+ absl::Status copy(status);
+ EXPECT_EQ(copy, status);
+ }
+}
+
+TEST(Status, CopyAssignment) {
+ absl::Status assignee;
+ {
+ absl::Status status;
+ assignee = status;
+ EXPECT_EQ(assignee, status);
+ }
+ {
+ absl::Status status(absl::StatusCode::kInvalidArgument, "message");
+ assignee = status;
+ EXPECT_EQ(assignee, status);
+ }
+ {
+ absl::Status status(absl::StatusCode::kInvalidArgument, "message");
+ status.SetPayload(kUrl1, absl::Cord(kPayload1));
+ assignee = status;
+ EXPECT_EQ(assignee, status);
+ }
+}
+
+TEST(Status, MoveConstructor) {
+ {
+ absl::Status status;
+ absl::Status copy(absl::Status{});
+ EXPECT_EQ(copy, status);
+ }
+ {
+ absl::Status status(absl::StatusCode::kInvalidArgument, "message");
+ absl::Status copy(
+ absl::Status(absl::StatusCode::kInvalidArgument, "message"));
+ EXPECT_EQ(copy, status);
+ }
+ {
+ absl::Status status(absl::StatusCode::kInvalidArgument, "message");
+ status.SetPayload(kUrl1, absl::Cord(kPayload1));
+ absl::Status copy1(status);
+ absl::Status copy2(std::move(status));
+ EXPECT_EQ(copy1, copy2);
+ }
+}
+
+TEST(Status, MoveAssignment) {
+ absl::Status assignee;
+ {
+ absl::Status status;
+ assignee = absl::Status();
+ EXPECT_EQ(assignee, status);
+ }
+ {
+ absl::Status status(absl::StatusCode::kInvalidArgument, "message");
+ assignee = absl::Status(absl::StatusCode::kInvalidArgument, "message");
+ EXPECT_EQ(assignee, status);
+ }
+ {
+ absl::Status status(absl::StatusCode::kInvalidArgument, "message");
+ status.SetPayload(kUrl1, absl::Cord(kPayload1));
+ absl::Status copy(status);
+ assignee = std::move(status);
+ EXPECT_EQ(assignee, copy);
+ }
+}
+
+TEST(Status, Update) {
+ absl::Status s;
+ s.Update(absl::OkStatus());
+ EXPECT_TRUE(s.ok());
+ const absl::Status a(absl::StatusCode::kCancelled, "message");
+ s.Update(a);
+ EXPECT_EQ(s, a);
+ const absl::Status b(absl::StatusCode::kInternal, "other message");
+ s.Update(b);
+ EXPECT_EQ(s, a);
+ s.Update(absl::OkStatus());
+ EXPECT_EQ(s, a);
+ EXPECT_FALSE(s.ok());
+}
+
+TEST(Status, Equality) {
+ absl::Status ok;
+ absl::Status no_payload = absl::CancelledError("no payload");
+ absl::Status one_payload = absl::InvalidArgumentError("one payload");
+ one_payload.SetPayload(kUrl1, absl::Cord(kPayload1));
+ absl::Status two_payloads = one_payload;
+ two_payloads.SetPayload(kUrl2, absl::Cord(kPayload2));
+ const std::array<absl::Status, 4> status_arr = {ok, no_payload, one_payload,
+ two_payloads};
+ for (int i = 0; i < status_arr.size(); i++) {
+ for (int j = 0; j < status_arr.size(); j++) {
+ if (i == j) {
+ EXPECT_TRUE(status_arr[i] == status_arr[j]);
+ EXPECT_FALSE(status_arr[i] != status_arr[j]);
+ } else {
+ EXPECT_TRUE(status_arr[i] != status_arr[j]);
+ EXPECT_FALSE(status_arr[i] == status_arr[j]);
+ }
+ }
+ }
+}
+
+TEST(Status, Swap) {
+ auto test_swap = [](const absl::Status& s1, const absl::Status& s2) {
+ absl::Status copy1 = s1, copy2 = s2;
+ swap(copy1, copy2);
+ EXPECT_EQ(copy1, s2);
+ EXPECT_EQ(copy2, s1);
+ };
+ const absl::Status ok;
+ const absl::Status no_payload(absl::StatusCode::kAlreadyExists, "no payload");
+ absl::Status with_payload(absl::StatusCode::kInternal, "with payload");
+ with_payload.SetPayload(kUrl1, absl::Cord(kPayload1));
+ test_swap(ok, no_payload);
+ test_swap(no_payload, ok);
+ test_swap(ok, with_payload);
+ test_swap(with_payload, ok);
+ test_swap(no_payload, with_payload);
+ test_swap(with_payload, no_payload);
+}
+
+} // namespace
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel
index 20511a35..b950ec76 100644
--- a/absl/strings/BUILD.bazel
+++ b/absl/strings/BUILD.bazel
@@ -13,11 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
- "ABSL_EXCEPTIONS_FLAG",
- "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
"ABSL_TEST_COPTS",
)
@@ -26,7 +25,7 @@ package(
features = ["parse_headers"],
)
-licenses(["notice"]) # Apache 2.0
+licenses(["notice"])
cc_library(
name = "strings",
@@ -73,6 +72,7 @@ cc_library(
"//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
+ "//absl/base:raw_logging_internal",
"//absl/base:throw_delegate",
"//absl/memory",
"//absl/meta:type_traits",
@@ -83,19 +83,23 @@ cc_library(
cc_library(
name = "internal",
srcs = [
+ "internal/escaping.cc",
"internal/ostringstream.cc",
"internal/utf8.cc",
],
hdrs = [
"internal/char_map.h",
+ "internal/escaping.h",
"internal/ostringstream.h",
"internal/resize_uninitialized.h",
"internal/utf8.h",
],
copts = ABSL_DEFAULT_COPTS,
deps = [
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/base:endian",
+ "//absl/base:raw_logging_internal",
"//absl/meta:type_traits",
],
)
@@ -122,6 +126,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
+ ":cord",
":strings",
"//absl/base:core_headers",
"//absl/container:fixed_array",
@@ -140,7 +145,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -225,8 +230,8 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -235,8 +240,7 @@ cc_test(
name = "string_view_test",
size = "small",
srcs = ["string_view_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":strings",
@@ -247,6 +251,74 @@ cc_test(
],
)
+cc_library(
+ name = "cord_internal",
+ hdrs = ["internal/cord_internal.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":strings",
+ "//absl/meta:type_traits",
+ ],
+)
+
+cc_library(
+ name = "cord",
+ srcs = [
+ "cord.cc",
+ ],
+ hdrs = [
+ "cord.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":cord_internal",
+ ":internal",
+ ":str_format",
+ ":strings",
+ "//absl/base",
+ "//absl/base:base_internal",
+ "//absl/base:core_headers",
+ "//absl/base:endian",
+ "//absl/base:raw_logging_internal",
+ "//absl/container:fixed_array",
+ "//absl/container:inlined_vector",
+ "//absl/functional:function_ref",
+ "//absl/meta:type_traits",
+ ],
+)
+
+cc_library(
+ name = "cord_test_helpers",
+ testonly = 1,
+ hdrs = [
+ "cord_test_helpers.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ ":cord",
+ ],
+)
+
+cc_test(
+ name = "cord_test",
+ size = "medium",
+ srcs = ["cord_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ visibility = ["//visibility:private"],
+ deps = [
+ ":cord",
+ ":cord_test_helpers",
+ ":strings",
+ "//absl/base",
+ "//absl/base:config",
+ "//absl/base:endian",
+ "//absl/base:raw_logging_internal",
+ "//absl/container:fixed_array",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
cc_test(
name = "substitute_test",
size = "small",
@@ -268,7 +340,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -306,7 +378,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -414,8 +486,10 @@ cc_test(
deps = [
":pow10_helper",
":strings",
- "//absl/base",
- "//absl/base:core_headers",
+ "//absl/base:config",
+ "//absl/base:raw_logging_internal",
+ "//absl/random",
+ "//absl/random:distributions",
"@com_google_googletest//:gtest_main",
],
)
@@ -428,7 +502,9 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":strings",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
+ "//absl/random",
+ "//absl/random:distributions",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -474,7 +550,6 @@ cc_test(
":pow10_helper",
":str_format",
":strings",
- "//absl/base",
"@com_google_googletest//:gtest_main",
],
)
@@ -488,7 +563,8 @@ cc_test(
copts = ABSL_TEST_COPTS,
deps = [
":strings",
- "//absl/base",
+ "//absl/base:config",
+ "//absl/base:raw_logging_internal",
"@com_google_googletest//:gtest_main",
],
)
@@ -503,7 +579,7 @@ cc_test(
copts = ABSL_TEST_COPTS,
deps = [
":strings",
- "//absl/base",
+ "//absl/base:config",
"@com_google_googletest//:gtest_main",
],
)
@@ -518,7 +594,6 @@ cc_test(
],
deps = [
":strings",
- "//absl/base",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -634,6 +709,7 @@ cc_test(
visibility = ["//visibility:private"],
deps = [
":str_format_internal",
+ "//absl/base:raw_logging_internal",
"//absl/numeric:int128",
"@com_google_googletest//:gtest_main",
],
@@ -668,6 +744,7 @@ cc_library(
srcs = ["internal/pow10_helper.cc"],
hdrs = ["internal/pow10_helper.h"],
visibility = ["//visibility:private"],
+ deps = ["//absl/base:config"],
)
cc_test(
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt
index e63eec19..cebc5928 100644
--- a/absl/strings/CMakeLists.txt
+++ b/absl/strings/CMakeLists.txt
@@ -59,10 +59,11 @@ absl_cc_library(
absl::config
absl::core_headers
absl::endian
- absl::throw_delegate
+ absl::int128
absl::memory
+ absl::raw_logging_internal
+ absl::throw_delegate
absl::type_traits
- absl::int128
PUBLIC
)
@@ -71,6 +72,8 @@ absl_cc_library(
strings_internal
HDRS
"internal/char_map.h"
+ "internal/escaping.cc"
+ "internal/escaping.h"
"internal/ostringstream.h"
"internal/resize_uninitialized.h"
"internal/utf8.h"
@@ -80,8 +83,10 @@ absl_cc_library(
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
+ absl::config
absl::core_headers
absl::endian
+ absl::raw_logging_internal
absl::type_traits
)
@@ -160,9 +165,6 @@ absl_cc_test(
"string_view_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::strings
absl::config
@@ -276,9 +278,12 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::strings
- absl::base
absl::core_headers
absl::pow10_helper
+ absl::config
+ absl::raw_logging_internal
+ absl::random_random
+ absl::random_distributions
gmock_main
)
@@ -317,7 +322,6 @@ absl_cc_test(
DEPS
absl::strings
absl::str_format
- absl::base
absl::pow10_helper
gmock_main
)
@@ -332,7 +336,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::strings
- absl::base
+ absl::config
+ absl::raw_logging_internal
gmock_main
)
@@ -347,7 +352,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::strings
- absl::base
+ absl::config
gmock_main
)
@@ -465,6 +470,7 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::str_format_internal
+ absl::raw_logging_internal
absl::int128
gmock_main
)
@@ -503,6 +509,8 @@ absl_cc_library(
"internal/pow10_helper.cc"
COPTS
${ABSL_TEST_COPTS}
+ DEPS
+ absl::config
TESTONLY
)
@@ -518,3 +526,57 @@ absl_cc_test(
absl::str_format
gmock_main
)
+
+absl_cc_library(
+ NAME
+ cord
+ HDRS
+ "cord.h"
+ SRCS
+ "cord.cc"
+ "internal/cord_internal.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::strings_internal
+ absl::base
+ absl::base_internal
+ absl::core_headers
+ absl::endian
+ absl::fixed_array
+ absl::function_ref
+ absl::inlined_vector
+ absl::raw_logging_internal
+ absl::type_traits
+ PUBLIC
+)
+
+absl_cc_library(
+ NAME
+ cord_test_helpers
+ HDRS
+ "cord_test_helpers.h"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::cord
+ TESTONLY
+)
+
+absl_cc_test(
+ NAME
+ cord_test
+ SRCS
+ "cord_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ DEPS
+ absl::cord
+ absl::strings
+ absl::base
+ absl::config
+ absl::endian
+ absl::raw_logging_internal
+ absl::fixed_array
+ gmock_main
+)
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc
index 045a5e21..93bb03e9 100644
--- a/absl/strings/ascii.cc
+++ b/absl/strings/ascii.cc
@@ -15,7 +15,7 @@
#include "absl/strings/ascii.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace ascii_internal {
// # Table generated by this Python code (bit 0x02 is currently unused):
@@ -57,7 +57,7 @@ namespace ascii_internal {
// of these bits is tightly coupled to this implementation, the individual bits
// are not named. Note that bitfields for all characters above ASCII 127 are
// zero-initialized.
-const unsigned char kPropertyBits[256] = {
+ABSL_DLL const unsigned char kPropertyBits[256] = {
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x00
0x40, 0x68, 0x48, 0x48, 0x48, 0x48, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x10
@@ -79,7 +79,7 @@ const unsigned char kPropertyBits[256] = {
// Array of characters for the ascii_tolower() function. For values 'A'
// through 'Z', return the lower-case character; otherwise, return the
// identity of the passed character.
-const char kToLower[256] = {
+ABSL_DLL const char kToLower[256] = {
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
@@ -117,7 +117,7 @@ const char kToLower[256] = {
// Array of characters for the ascii_toupper() function. For values 'a'
// through 'z', return the upper-case character; otherwise, return the
// identity of the passed character.
-const char kToUpper[256] = {
+ABSL_DLL const char kToUpper[256] = {
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
'\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
'\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
@@ -196,5 +196,5 @@ void RemoveExtraAsciiWhitespace(std::string* str) {
str->erase(output_it - &(*str)[0]);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h
index ebcbb11a..b46bc71f 100644
--- a/absl/strings/ascii.h
+++ b/absl/strings/ascii.h
@@ -56,20 +56,21 @@
#include <string>
#include "absl/base/attributes.h"
+#include "absl/base/config.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace ascii_internal {
// Declaration for an array of bitfields holding character information.
-extern const unsigned char kPropertyBits[256];
+ABSL_DLL extern const unsigned char kPropertyBits[256];
// Declaration for the array of characters to upper-case characters.
-extern const char kToUpper[256];
+ABSL_DLL extern const char kToUpper[256];
// Declaration for the array of characters to lower-case characters.
-extern const char kToLower[256];
+ABSL_DLL extern const char kToLower[256];
} // namespace ascii_internal
@@ -235,7 +236,7 @@ inline void StripAsciiWhitespace(std::string* str) {
// Removes leading, trailing, and consecutive internal whitespace.
void RemoveExtraAsciiWhitespace(std::string*);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_ASCII_H_
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc
index 866a163e..bdba768d 100644
--- a/absl/strings/charconv.cc
+++ b/absl/strings/charconv.cc
@@ -57,7 +57,7 @@
// narrower mantissas.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
template <typename FloatType>
@@ -673,7 +673,6 @@ from_chars_result FromCharsImpl(const char* first, const char* last,
EncodeResult(calculated, negative, &result, &value);
return result;
}
- return result;
}
} // namespace
@@ -981,5 +980,5 @@ const int16_t kPower10ExponentTable[] = {
};
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h
index 0b84ccac..e04be32f 100644
--- a/absl/strings/charconv.h
+++ b/absl/strings/charconv.h
@@ -17,8 +17,10 @@
#include <system_error> // NOLINT(build/c++11)
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Workalike compatibilty version of std::chars_format from C++17.
//
@@ -111,7 +113,7 @@ inline chars_format& operator^=(chars_format& lhs, chars_format rhs) {
return lhs;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_CHARCONV_H_
diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc
index b58fad26..9090e9c8 100644
--- a/absl/strings/charconv_test.cc
+++ b/absl/strings/charconv_test.cc
@@ -511,6 +511,13 @@ TEST(FromChars, Overflow) {
EXPECT_EQ(f, std::numeric_limits<float>::max());
}
+TEST(FromChars, RegressionTestsFromFuzzer) {
+ absl::string_view src = "0x21900000p00000000099";
+ float f;
+ auto result = absl::from_chars(src.data(), src.data() + src.size(), f);
+ EXPECT_EQ(result.ec, std::errc::result_out_of_range);
+}
+
TEST(FromChars, ReturnValuePtr) {
// Check that `ptr` points one past the number scanned, even if that number
// is not representable.
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc
new file mode 100644
index 00000000..d9503ae3
--- /dev/null
+++ b/absl/strings/cord.cc
@@ -0,0 +1,2019 @@
+// Copyright 2020 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/strings/cord.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <iomanip>
+#include <limits>
+#include <ostream>
+#include <sstream>
+#include <type_traits>
+#include <unordered_set>
+#include <vector>
+
+#include "absl/base/casts.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/port.h"
+#include "absl/container/fixed_array.h"
+#include "absl/container/inlined_vector.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/resize_uninitialized.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_join.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+using ::absl::cord_internal::CordRep;
+using ::absl::cord_internal::CordRepConcat;
+using ::absl::cord_internal::CordRepExternal;
+using ::absl::cord_internal::CordRepSubstring;
+
+// Various representations that we allow
+enum CordRepKind {
+ CONCAT = 0,
+ EXTERNAL = 1,
+ SUBSTRING = 2,
+
+ // We have different tags for different sized flat arrays,
+ // starting with FLAT
+ FLAT = 3,
+};
+
+namespace {
+
+// Type used with std::allocator for allocating and deallocating
+// `CordRepExternal`. std::allocator is used because it opaquely handles the
+// different new / delete overloads available on a given platform.
+struct alignas(absl::cord_internal::ExternalRepAlignment()) ExternalAllocType {
+ unsigned char value[absl::cord_internal::ExternalRepAlignment()];
+};
+
+// Returns the number of objects to pass in to std::allocator<ExternalAllocType>
+// allocate() and deallocate() to create enough room for `CordRepExternal` with
+// `releaser_size` bytes on the end.
+constexpr size_t GetExternalAllocNumObjects(size_t releaser_size) {
+ // Be sure to round up since `releaser_size` could be smaller than
+ // `sizeof(ExternalAllocType)`.
+ return (sizeof(CordRepExternal) + releaser_size + sizeof(ExternalAllocType) -
+ 1) /
+ sizeof(ExternalAllocType);
+}
+
+// Allocates enough memory for `CordRepExternal` and a releaser with size
+// `releaser_size` bytes.
+void* AllocateExternal(size_t releaser_size) {
+ return std::allocator<ExternalAllocType>().allocate(
+ GetExternalAllocNumObjects(releaser_size));
+}
+
+// Deallocates the memory for a `CordRepExternal` assuming it was allocated with
+// a releaser of given size and alignment.
+void DeallocateExternal(CordRepExternal* p, size_t releaser_size) {
+ std::allocator<ExternalAllocType>().deallocate(
+ reinterpret_cast<ExternalAllocType*>(p),
+ GetExternalAllocNumObjects(releaser_size));
+}
+
+// Returns a pointer to the type erased releaser for the given CordRepExternal.
+void* GetExternalReleaser(CordRepExternal* rep) {
+ return rep + 1;
+}
+
+} // namespace
+
+namespace cord_internal {
+
+inline CordRepConcat* CordRep::concat() {
+ assert(tag == CONCAT);
+ return static_cast<CordRepConcat*>(this);
+}
+
+inline const CordRepConcat* CordRep::concat() const {
+ assert(tag == CONCAT);
+ return static_cast<const CordRepConcat*>(this);
+}
+
+inline CordRepSubstring* CordRep::substring() {
+ assert(tag == SUBSTRING);
+ return static_cast<CordRepSubstring*>(this);
+}
+
+inline const CordRepSubstring* CordRep::substring() const {
+ assert(tag == SUBSTRING);
+ return static_cast<const CordRepSubstring*>(this);
+}
+
+inline CordRepExternal* CordRep::external() {
+ assert(tag == EXTERNAL);
+ return static_cast<CordRepExternal*>(this);
+}
+
+inline const CordRepExternal* CordRep::external() const {
+ assert(tag == EXTERNAL);
+ return static_cast<const CordRepExternal*>(this);
+}
+
+} // namespace cord_internal
+
+static const size_t kFlatOverhead = offsetof(CordRep, data);
+
+static_assert(kFlatOverhead == 13, "Unittests assume kFlatOverhead == 13");
+
+// Largest and smallest flat node lengths we are willing to allocate
+// Flat allocation size is stored in tag, which currently can encode sizes up
+// to 4K, encoded as multiple of either 8 or 32 bytes.
+// If we allow for larger sizes, we need to change this to 8/64, 16/128, etc.
+static constexpr size_t kMaxFlatSize = 4096;
+static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead;
+static constexpr size_t kMinFlatLength = 32 - kFlatOverhead;
+
+// Prefer copying blocks of at most this size, otherwise reference count.
+static const size_t kMaxBytesToCopy = 511;
+
+// Helper functions for rounded div, and rounding to exact sizes.
+static size_t DivUp(size_t n, size_t m) { return (n + m - 1) / m; }
+static size_t RoundUp(size_t n, size_t m) { return DivUp(n, m) * m; }
+
+// Returns the size to the nearest equal or larger value that can be
+// expressed exactly as a tag value.
+static size_t RoundUpForTag(size_t size) {
+ return RoundUp(size, (size <= 1024) ? 8 : 32);
+}
+
+// Converts the allocated size to a tag, rounding down if the size
+// does not exactly match a 'tag expressible' size value. The result is
+// undefined if the size exceeds the maximum size that can be encoded in
+// a tag, i.e., if size is larger than TagToAllocatedSize(<max tag>).
+static uint8_t AllocatedSizeToTag(size_t size) {
+ const size_t tag = (size <= 1024) ? size / 8 : 128 + size / 32 - 1024 / 32;
+ assert(tag <= std::numeric_limits<uint8_t>::max());
+ return tag;
+}
+
+// Converts the provided tag to the corresponding allocated size
+static constexpr size_t TagToAllocatedSize(uint8_t tag) {
+ return (tag <= 128) ? (tag * 8) : (1024 + (tag - 128) * 32);
+}
+
+// Converts the provided tag to the corresponding available data length
+static constexpr size_t TagToLength(uint8_t tag) {
+ return TagToAllocatedSize(tag) - kFlatOverhead;
+}
+
+// Enforce that kMaxFlatSize maps to a well-known exact tag value.
+static_assert(TagToAllocatedSize(224) == kMaxFlatSize, "Bad tag logic");
+
+constexpr uint64_t Fibonacci(unsigned char n, uint64_t a = 0, uint64_t b = 1) {
+ return n == 0 ? a : Fibonacci(n - 1, b, a + b);
+}
+
+static_assert(Fibonacci(63) == 6557470319842,
+ "Fibonacci values computed incorrectly");
+
+// Minimum length required for a given depth tree -- a tree is considered
+// balanced if
+// length(t) >= min_length[depth(t)]
+// The root node depth is allowed to become twice as large to reduce rebalancing
+// for larger strings (see IsRootBalanced).
+static constexpr uint64_t min_length[] = {
+ Fibonacci(2),
+ Fibonacci(3),
+ Fibonacci(4),
+ Fibonacci(5),
+ Fibonacci(6),
+ Fibonacci(7),
+ Fibonacci(8),
+ Fibonacci(9),
+ Fibonacci(10),
+ Fibonacci(11),
+ Fibonacci(12),
+ Fibonacci(13),
+ Fibonacci(14),
+ Fibonacci(15),
+ Fibonacci(16),
+ Fibonacci(17),
+ Fibonacci(18),
+ Fibonacci(19),
+ Fibonacci(20),
+ Fibonacci(21),
+ Fibonacci(22),
+ Fibonacci(23),
+ Fibonacci(24),
+ Fibonacci(25),
+ Fibonacci(26),
+ Fibonacci(27),
+ Fibonacci(28),
+ Fibonacci(29),
+ Fibonacci(30),
+ Fibonacci(31),
+ Fibonacci(32),
+ Fibonacci(33),
+ Fibonacci(34),
+ Fibonacci(35),
+ Fibonacci(36),
+ Fibonacci(37),
+ Fibonacci(38),
+ Fibonacci(39),
+ Fibonacci(40),
+ Fibonacci(41),
+ Fibonacci(42),
+ Fibonacci(43),
+ Fibonacci(44),
+ Fibonacci(45),
+ Fibonacci(46),
+ Fibonacci(47),
+ 0xffffffffffffffffull, // Avoid overflow
+};
+
+static const int kMinLengthSize = ABSL_ARRAYSIZE(min_length);
+
+// The inlined size to use with absl::InlinedVector.
+//
+// Note: The InlinedVectors in this file (and in cord.h) do not need to use
+// the same value for their inlined size. The fact that they do is historical.
+// It may be desirable for each to use a different inlined size optimized for
+// that InlinedVector's usage.
+//
+// TODO(jgm): Benchmark to see if there's a more optimal value than 47 for
+// the inlined vector size (47 exists for backward compatibility).
+static const int kInlinedVectorSize = 47;
+
+static inline bool IsRootBalanced(CordRep* node) {
+ if (node->tag != CONCAT) {
+ return true;
+ } else if (node->concat()->depth() <= 15) {
+ return true;
+ } else if (node->concat()->depth() > kMinLengthSize) {
+ return false;
+ } else {
+ // Allow depth to become twice as large as implied by fibonacci rule to
+ // reduce rebalancing for larger strings.
+ return (node->length >= min_length[node->concat()->depth() / 2]);
+ }
+}
+
+static CordRep* Rebalance(CordRep* node);
+static void DumpNode(CordRep* rep, bool include_data, std::ostream* os);
+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);
+
+ return node;
+}
+
+// --------------------------------------------------------------------
+// Memory management
+
+inline CordRep* Ref(CordRep* rep) {
+ if (rep != nullptr) {
+ rep->refcount.Increment();
+ }
+ return rep;
+}
+
+// This internal routine is called from the cold path of Unref below. Keeping it
+// in a separate routine allows good inlining of Unref into many profitable call
+// sites. However, the call to this function can be highly disruptive to the
+// register pressure in those callers. To minimize the cost to callers, we use
+// a special LLVM calling convention that preserves most registers. This allows
+// the call to this routine in cold paths to not disrupt the caller's register
+// pressure. This calling convention is not available on all platforms; we
+// intentionally allow LLVM to ignore the attribute rather than attempting to
+// hardcode the list of supported platforms.
+#if defined(__clang__) && !defined(__i386__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wattributes"
+__attribute__((preserve_most))
+#pragma clang diagnostic pop
+#endif
+static void UnrefInternal(CordRep* rep) {
+ assert(rep != nullptr);
+
+ absl::InlinedVector<CordRep*, kInlinedVectorSize> pending;
+ while (true) {
+ if (rep->tag == CONCAT) {
+ CordRepConcat* rep_concat = rep->concat();
+ CordRep* right = rep_concat->right;
+ if (!right->refcount.Decrement()) {
+ pending.push_back(right);
+ }
+ CordRep* left = rep_concat->left;
+ delete rep_concat;
+ rep = nullptr;
+ if (!left->refcount.Decrement()) {
+ rep = left;
+ continue;
+ }
+ } else if (rep->tag == EXTERNAL) {
+ CordRepExternal* rep_external = rep->external();
+ absl::string_view data(rep_external->base, rep->length);
+ void* releaser = GetExternalReleaser(rep_external);
+ size_t releaser_size = rep_external->releaser_invoker(releaser, data);
+ rep_external->~CordRepExternal();
+ DeallocateExternal(rep_external, releaser_size);
+ rep = nullptr;
+ } else if (rep->tag == SUBSTRING) {
+ CordRepSubstring* rep_substring = rep->substring();
+ CordRep* child = rep_substring->child;
+ delete rep_substring;
+ rep = nullptr;
+ if (!child->refcount.Decrement()) {
+ rep = child;
+ continue;
+ }
+ } else {
+ // Flat CordReps are allocated and constructed with raw ::operator new
+ // and placement new, and must be destructed and deallocated
+ // accordingly.
+#if defined(__cpp_sized_deallocation)
+ size_t size = TagToAllocatedSize(rep->tag);
+ rep->~CordRep();
+ ::operator delete(rep, size);
+#else
+ rep->~CordRep();
+ ::operator delete(rep);
+#endif
+ rep = nullptr;
+ }
+
+ if (!pending.empty()) {
+ rep = pending.back();
+ pending.pop_back();
+ } else {
+ break;
+ }
+ }
+}
+
+inline void Unref(CordRep* rep) {
+ // Fast-path for two common, hot cases: a null rep and a shared root.
+ if (ABSL_PREDICT_TRUE(rep == nullptr ||
+ rep->refcount.DecrementExpectHighRefcount())) {
+ return;
+ }
+
+ UnrefInternal(rep);
+}
+
+// Return the depth of a node
+static int Depth(const CordRep* rep) {
+ if (rep->tag == CONCAT) {
+ return rep->concat()->depth();
+ } else {
+ return 0;
+ }
+}
+
+static void SetConcatChildren(CordRepConcat* concat, CordRep* left,
+ CordRep* right) {
+ concat->left = left;
+ concat->right = right;
+
+ concat->length = left->length + right->length;
+ concat->set_depth(1 + std::max(Depth(left), Depth(right)));
+}
+
+// Create a concatenation of the specified nodes.
+// Does not change the refcounts of "left" and "right".
+// The returned node has a refcount of 1.
+static CordRep* RawConcat(CordRep* left, CordRep* right) {
+ // Avoid making degenerate concat nodes (one child is empty)
+ if (left == nullptr || left->length == 0) {
+ Unref(left);
+ return right;
+ }
+ if (right == nullptr || right->length == 0) {
+ Unref(right);
+ return left;
+ }
+
+ CordRepConcat* rep = new CordRepConcat();
+ rep->tag = CONCAT;
+ SetConcatChildren(rep, left, right);
+
+ return rep;
+}
+
+static CordRep* Concat(CordRep* left, CordRep* right) {
+ CordRep* rep = RawConcat(left, right);
+ if (rep != nullptr && !IsRootBalanced(rep)) {
+ rep = Rebalance(rep);
+ }
+ return VerifyTree(rep);
+}
+
+// Make a balanced tree out of an array of leaf nodes.
+static CordRep* MakeBalancedTree(CordRep** reps, size_t n) {
+ // Make repeated passes over the array, merging adjacent pairs
+ // until we are left with just a single node.
+ while (n > 1) {
+ size_t dst = 0;
+ for (size_t src = 0; src < n; src += 2) {
+ if (src + 1 < n) {
+ reps[dst] = Concat(reps[src], reps[src + 1]);
+ } else {
+ reps[dst] = reps[src];
+ }
+ dst++;
+ }
+ n = dst;
+ }
+
+ return reps[0];
+}
+
+// Create a new flat node.
+static CordRep* NewFlat(size_t length_hint) {
+ if (length_hint <= kMinFlatLength) {
+ length_hint = kMinFlatLength;
+ } else if (length_hint > kMaxFlatLength) {
+ length_hint = kMaxFlatLength;
+ }
+
+ // Round size up so it matches a size we can exactly express in a tag.
+ const size_t size = RoundUpForTag(length_hint + kFlatOverhead);
+ void* const raw_rep = ::operator new(size);
+ CordRep* rep = new (raw_rep) CordRep();
+ rep->tag = AllocatedSizeToTag(size);
+ return VerifyTree(rep);
+}
+
+// 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) {
+ if (length == 0) return nullptr;
+ absl::FixedArray<CordRep*> reps((length - 1) / kMaxFlatLength + 1);
+ size_t n = 0;
+ do {
+ const size_t len = std::min(length, kMaxFlatLength);
+ CordRep* rep = NewFlat(len + alloc_hint);
+ rep->length = len;
+ memcpy(rep->data, data, len);
+ reps[n++] = VerifyTree(rep);
+ data += len;
+ length -= len;
+ } while (length != 0);
+ return MakeBalancedTree(reps.data(), n);
+}
+
+namespace cord_internal {
+
+ExternalRepReleaserPair NewExternalWithUninitializedReleaser(
+ absl::string_view data, ExternalReleaserInvoker invoker,
+ size_t releaser_size) {
+ assert(!data.empty());
+
+ void* raw_rep = AllocateExternal(releaser_size);
+ auto* rep = new (raw_rep) CordRepExternal();
+ rep->length = data.size();
+ rep->tag = EXTERNAL;
+ rep->base = data.data();
+ rep->releaser_invoker = invoker;
+ return {VerifyTree(rep), GetExternalReleaser(rep)};
+}
+
+} // namespace cord_internal
+
+static CordRep* NewSubstring(CordRep* child, size_t offset, size_t length) {
+ // Never create empty substring nodes
+ if (length == 0) {
+ Unref(child);
+ return nullptr;
+ } else {
+ CordRepSubstring* rep = new CordRepSubstring();
+ assert((offset + length) <= child->length);
+ rep->length = length;
+ rep->tag = SUBSTRING;
+ rep->start = offset;
+ rep->child = child;
+ return VerifyTree(rep);
+ }
+}
+
+// --------------------------------------------------------------------
+// Cord::InlineRep functions
+
+// This will trigger LNK2005 in MSVC.
+#ifndef COMPILER_MSVC
+const unsigned char Cord::InlineRep::kMaxInline;
+#endif // COMPILER_MSVC
+
+inline void Cord::InlineRep::set_data(const char* data, size_t n,
+ bool nullify_tail) {
+ static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15");
+
+ cord_internal::SmallMemmove(data_, data, n, nullify_tail);
+ data_[kMaxInline] = static_cast<char>(n);
+}
+
+inline char* Cord::InlineRep::set_data(size_t n) {
+ assert(n <= kMaxInline);
+ memset(data_, 0, sizeof(data_));
+ data_[kMaxInline] = static_cast<char>(n);
+ return data_;
+}
+
+inline CordRep* Cord::InlineRep::force_tree(size_t extra_hint) {
+ size_t len = data_[kMaxInline];
+ CordRep* result;
+ if (len > kMaxInline) {
+ memcpy(&result, data_, sizeof(result));
+ } else {
+ result = NewFlat(len + extra_hint);
+ result->length = len;
+ memcpy(result->data, data_, len);
+ set_tree(result);
+ }
+ return result;
+}
+
+inline void Cord::InlineRep::reduce_size(size_t n) {
+ size_t tag = data_[kMaxInline];
+ assert(tag <= kMaxInline);
+ assert(tag >= n);
+ tag -= n;
+ memset(data_ + tag, 0, n);
+ data_[kMaxInline] = static_cast<char>(tag);
+}
+
+inline void Cord::InlineRep::remove_prefix(size_t n) {
+ cord_internal::SmallMemmove(data_, data_ + n, data_[kMaxInline] - n);
+ reduce_size(n);
+}
+
+void Cord::InlineRep::AppendTree(CordRep* tree) {
+ if (tree == nullptr) return;
+ size_t len = data_[kMaxInline];
+ if (len == 0) {
+ set_tree(tree);
+ } else {
+ set_tree(Concat(force_tree(0), tree));
+ }
+}
+
+void Cord::InlineRep::PrependTree(CordRep* tree) {
+ if (tree == nullptr) return;
+ size_t len = data_[kMaxInline];
+ if (len == 0) {
+ set_tree(tree);
+ } else {
+ set_tree(Concat(tree, force_tree(0)));
+ }
+}
+
+// Searches for a non-full flat node at the rightmost leaf of the tree. If a
+// 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) {
+ // Search down the right-hand path for a non-full FLAT node.
+ CordRep* dst = root;
+ while (dst->tag == CONCAT && dst->refcount.IsOne()) {
+ dst = dst->concat()->right;
+ }
+
+ if (dst->tag < FLAT || !dst->refcount.IsOne()) {
+ *region = nullptr;
+ *size = 0;
+ return false;
+ }
+
+ const size_t in_use = dst->length;
+ const size_t capacity = TagToLength(dst->tag);
+ if (in_use == capacity) {
+ *region = nullptr;
+ *size = 0;
+ return false;
+ }
+
+ size_t size_increase = std::min(capacity - in_use, max_length);
+
+ // We need to update the length fields for all nodes, including the leaf node.
+ for (CordRep* rep = root; rep != dst; rep = rep->concat()->right) {
+ rep->length += size_increase;
+ }
+ dst->length += size_increase;
+
+ *region = dst->data + in_use;
+ *size = size_increase;
+ return true;
+}
+
+void Cord::InlineRep::GetAppendRegion(char** region, size_t* size,
+ size_t max_length) {
+ if (max_length == 0) {
+ *region = nullptr;
+ *size = 0;
+ return;
+ }
+
+ // Try to fit in the inline buffer if possible.
+ size_t inline_length = data_[kMaxInline];
+ if (inline_length < kMaxInline && max_length <= kMaxInline - inline_length) {
+ *region = data_ + inline_length;
+ *size = max_length;
+ data_[kMaxInline] = static_cast<char>(inline_length + max_length);
+ return;
+ }
+
+ CordRep* root = force_tree(max_length);
+
+ if (PrepareAppendRegion(root, region, size, max_length)) {
+ return;
+ }
+
+ // Allocate new node.
+ CordRep* new_node =
+ NewFlat(std::max(static_cast<size_t>(root->length), max_length));
+ new_node->length =
+ std::min(static_cast<size_t>(TagToLength(new_node->tag)), max_length);
+ *region = new_node->data;
+ *size = new_node->length;
+ replace_tree(Concat(root, new_node));
+}
+
+void Cord::InlineRep::GetAppendRegion(char** region, size_t* size) {
+ const size_t max_length = std::numeric_limits<size_t>::max();
+
+ // Try to fit in the inline buffer if possible.
+ size_t inline_length = data_[kMaxInline];
+ if (inline_length < kMaxInline) {
+ *region = data_ + inline_length;
+ *size = kMaxInline - inline_length;
+ data_[kMaxInline] = kMaxInline;
+ return;
+ }
+
+ CordRep* root = force_tree(max_length);
+
+ if (PrepareAppendRegion(root, region, size, max_length)) {
+ return;
+ }
+
+ // Allocate new node.
+ CordRep* new_node = NewFlat(root->length);
+ new_node->length = TagToLength(new_node->tag);
+ *region = new_node->data;
+ *size = new_node->length;
+ replace_tree(Concat(root, new_node));
+}
+
+// If the rep is a leaf, this will increment the value at total_mem_usage and
+// will return true.
+static bool RepMemoryUsageLeaf(const CordRep* rep, size_t* total_mem_usage) {
+ if (rep->tag >= FLAT) {
+ *total_mem_usage += TagToAllocatedSize(rep->tag);
+ return true;
+ }
+ if (rep->tag == EXTERNAL) {
+ *total_mem_usage += sizeof(CordRepConcat) + rep->length;
+ return true;
+ }
+ return false;
+}
+
+void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) {
+ ClearSlow();
+
+ memcpy(data_, src.data_, sizeof(data_));
+ if (is_tree()) {
+ Ref(tree());
+ }
+}
+
+void Cord::InlineRep::ClearSlow() {
+ if (is_tree()) {
+ Unref(tree());
+ }
+ memset(data_, 0, sizeof(data_));
+}
+
+// --------------------------------------------------------------------
+// Constructors and destructors
+
+Cord::Cord(const Cord& src) : contents_(src.contents_) {
+ Ref(contents_.tree()); // Does nothing if contents_ has embedded data
+}
+
+Cord::Cord(absl::string_view src) {
+ const size_t n = src.size();
+ if (n <= InlineRep::kMaxInline) {
+ contents_.set_data(src.data(), n, false);
+ } else {
+ contents_.set_tree(NewTree(src.data(), n, 0));
+ }
+}
+
+// The destruction code is separate so that the compiler can determine
+// that it does not need to call the destructor on a moved-from Cord.
+void Cord::DestroyCordSlow() {
+ Unref(VerifyTree(contents_.tree()));
+}
+
+// --------------------------------------------------------------------
+// Mutators
+
+void Cord::Clear() {
+ Unref(contents_.clear());
+}
+
+Cord& Cord::operator=(absl::string_view src) {
+
+ const char* data = src.data();
+ size_t length = src.size();
+ CordRep* tree = contents_.tree();
+ if (length <= InlineRep::kMaxInline) {
+ // Embed into this->contents_
+ contents_.set_data(data, length, true);
+ Unref(tree);
+ return *this;
+ }
+ if (tree != nullptr && tree->tag >= FLAT &&
+ TagToLength(tree->tag) >= length && tree->refcount.IsOne()) {
+ // Copy in place if the existing FLAT node is reusable.
+ memmove(tree->data, data, length);
+ tree->length = length;
+ VerifyTree(tree);
+ return *this;
+ }
+ contents_.set_tree(NewTree(data, length, 0));
+ Unref(tree);
+ return *this;
+}
+
+// TODO(sanjay): Move to Cord::InlineRep section of file. For now,
+// we keep it here to make diffs easier.
+void Cord::InlineRep::AppendArray(const char* src_data, size_t src_size) {
+ if (src_size == 0) return; // memcpy(_, nullptr, 0) is undefined.
+ // Try to fit in the inline buffer if possible.
+ size_t inline_length = data_[kMaxInline];
+ if (inline_length < kMaxInline && src_size <= kMaxInline - inline_length) {
+ // Append new data to embedded array
+ data_[kMaxInline] = static_cast<char>(inline_length + src_size);
+ memcpy(data_ + inline_length, src_data, src_size);
+ return;
+ }
+
+ CordRep* root = tree();
+
+ size_t appended = 0;
+ if (root) {
+ char* region;
+ if (PrepareAppendRegion(root, &region, &appended, src_size)) {
+ memcpy(region, src_data, appended);
+ }
+ } else {
+ // It is possible that src_data == data_, but when we transition from an
+ // InlineRep to a tree we need to assign data_ = root via set_tree. To
+ // avoid corrupting the source data before we copy it, delay calling
+ // set_tree until after we've copied data.
+ // We are going from an inline size to beyond inline size. Make the new size
+ // either double the inlined size, or the added size + 10%.
+ const size_t size1 = inline_length * 2 + src_size;
+ const size_t size2 = inline_length + src_size / 10;
+ root = NewFlat(std::max<size_t>(size1, size2));
+ appended = std::min(src_size, TagToLength(root->tag) - inline_length);
+ memcpy(root->data, data_, inline_length);
+ memcpy(root->data + inline_length, src_data, appended);
+ root->length = inline_length + appended;
+ set_tree(root);
+ }
+
+ src_data += appended;
+ src_size -= appended;
+ if (src_size == 0) {
+ return;
+ }
+
+ // Use new block(s) for any remaining bytes that were not handled above.
+ // Alloc extra memory only if the right child of the root of the new tree is
+ // going to be a FLAT node, which will permit further inplace appends.
+ size_t length = src_size;
+ if (src_size < kMaxFlatLength) {
+ // The new length is either
+ // - old size + 10%
+ // - old_size + src_size
+ // This will cause a reasonable conservative step-up in size that is still
+ // large enough to avoid excessive amounts of small fragments being added.
+ length = std::max<size_t>(root->length / 10, src_size);
+ }
+ set_tree(Concat(root, NewTree(src_data, src_size, length - src_size)));
+}
+
+inline CordRep* Cord::TakeRep() const& {
+ return Ref(contents_.tree());
+}
+
+inline CordRep* Cord::TakeRep() && {
+ CordRep* rep = contents_.tree();
+ contents_.clear();
+ return rep;
+}
+
+template <typename C>
+inline void Cord::AppendImpl(C&& src) {
+ if (empty()) {
+ // In case of an empty destination avoid allocating a new node, do not copy
+ // data.
+ *this = std::forward<C>(src);
+ return;
+ }
+
+ // For short cords, it is faster to copy data if there is room in dst.
+ const size_t src_size = src.contents_.size();
+ if (src_size <= kMaxBytesToCopy) {
+ CordRep* src_tree = src.contents_.tree();
+ if (src_tree == nullptr) {
+ // src has embedded data.
+ contents_.AppendArray(src.contents_.data(), src_size);
+ return;
+ }
+ if (src_tree->tag >= FLAT) {
+ // src tree just has one flat node.
+ contents_.AppendArray(src_tree->data, src_size);
+ return;
+ }
+ if (&src == this) {
+ // ChunkIterator below assumes that src is not modified during traversal.
+ Append(Cord(src));
+ return;
+ }
+ // TODO(mec): Should we only do this if "dst" has space?
+ for (absl::string_view chunk : src.Chunks()) {
+ Append(chunk);
+ }
+ return;
+ }
+
+ contents_.AppendTree(std::forward<C>(src).TakeRep());
+}
+
+void Cord::Append(const Cord& src) { AppendImpl(src); }
+
+void Cord::Append(Cord&& src) { AppendImpl(std::move(src)); }
+
+void Cord::Prepend(const Cord& src) {
+ CordRep* src_tree = src.contents_.tree();
+ if (src_tree != nullptr) {
+ Ref(src_tree);
+ contents_.PrependTree(src_tree);
+ return;
+ }
+
+ // `src` cord is inlined.
+ absl::string_view src_contents(src.contents_.data(), src.contents_.size());
+ return Prepend(src_contents);
+}
+
+void Cord::Prepend(absl::string_view src) {
+ if (src.empty()) return; // memcpy(_, nullptr, 0) is undefined.
+ size_t cur_size = contents_.size();
+ if (!contents_.is_tree() && cur_size + src.size() <= InlineRep::kMaxInline) {
+ // Use embedded storage.
+ char data[InlineRep::kMaxInline + 1] = {0};
+ data[InlineRep::kMaxInline] = cur_size + src.size(); // set size
+ memcpy(data, src.data(), src.size());
+ memcpy(data + src.size(), contents_.data(), cur_size);
+ memcpy(reinterpret_cast<void*>(&contents_), data,
+ InlineRep::kMaxInline + 1);
+ } else {
+ contents_.PrependTree(NewTree(src.data(), src.size(), 0));
+ }
+}
+
+static CordRep* RemovePrefixFrom(CordRep* node, size_t n) {
+ if (n >= node->length) return nullptr;
+ if (n == 0) return Ref(node);
+ absl::InlinedVector<CordRep*, kInlinedVectorSize> rhs_stack;
+
+ while (node->tag == CONCAT) {
+ assert(n <= node->length);
+ if (n < node->concat()->left->length) {
+ // Push right to stack, descend left.
+ rhs_stack.push_back(node->concat()->right);
+ node = node->concat()->left;
+ } else {
+ // Drop left, descend right.
+ n -= node->concat()->left->length;
+ node = node->concat()->right;
+ }
+ }
+ assert(n <= node->length);
+
+ if (n == 0) {
+ Ref(node);
+ } else {
+ size_t start = n;
+ size_t len = node->length - n;
+ if (node->tag == SUBSTRING) {
+ // Consider in-place update of node, similar to in RemoveSuffixFrom().
+ start += node->substring()->start;
+ node = node->substring()->child;
+ }
+ node = NewSubstring(Ref(node), start, len);
+ }
+ while (!rhs_stack.empty()) {
+ node = Concat(node, Ref(rhs_stack.back()));
+ rhs_stack.pop_back();
+ }
+ return node;
+}
+
+// RemoveSuffixFrom() is very similar to RemovePrefixFrom(), with the
+// exception that removing a suffix has an optimization where a node may be
+// edited in place iff that node and all its ancestors have a refcount of 1.
+static CordRep* RemoveSuffixFrom(CordRep* node, size_t n) {
+ if (n >= node->length) return nullptr;
+ if (n == 0) return Ref(node);
+ absl::InlinedVector<CordRep*, kInlinedVectorSize> lhs_stack;
+ bool inplace_ok = node->refcount.IsOne();
+
+ while (node->tag == CONCAT) {
+ assert(n <= node->length);
+ if (n < node->concat()->right->length) {
+ // Push left to stack, descend right.
+ lhs_stack.push_back(node->concat()->left);
+ node = node->concat()->right;
+ } else {
+ // Drop right, descend left.
+ n -= node->concat()->right->length;
+ node = node->concat()->left;
+ }
+ inplace_ok = inplace_ok && node->refcount.IsOne();
+ }
+ assert(n <= node->length);
+
+ if (n == 0) {
+ Ref(node);
+ } else if (inplace_ok && node->tag != EXTERNAL) {
+ // Consider making a new buffer if the current node capacity is much
+ // larger than the new length.
+ Ref(node);
+ node->length -= n;
+ } else {
+ size_t start = 0;
+ size_t len = node->length - n;
+ if (node->tag == SUBSTRING) {
+ start = node->substring()->start;
+ node = node->substring()->child;
+ }
+ node = NewSubstring(Ref(node), start, len);
+ }
+ while (!lhs_stack.empty()) {
+ node = Concat(Ref(lhs_stack.back()), node);
+ lhs_stack.pop_back();
+ }
+ return node;
+}
+
+void Cord::RemovePrefix(size_t n) {
+ ABSL_INTERNAL_CHECK(n <= size(),
+ absl::StrCat("Requested prefix size ", n,
+ " exceeds Cord's size ", size()));
+ CordRep* tree = contents_.tree();
+ if (tree == nullptr) {
+ contents_.remove_prefix(n);
+ } else {
+ CordRep* newrep = RemovePrefixFrom(tree, n);
+ Unref(tree);
+ contents_.replace_tree(VerifyTree(newrep));
+ }
+}
+
+void Cord::RemoveSuffix(size_t n) {
+ ABSL_INTERNAL_CHECK(n <= size(),
+ absl::StrCat("Requested suffix size ", n,
+ " exceeds Cord's size ", size()));
+ CordRep* tree = contents_.tree();
+ if (tree == nullptr) {
+ contents_.reduce_size(n);
+ } else {
+ CordRep* newrep = RemoveSuffixFrom(tree, n);
+ Unref(tree);
+ contents_.replace_tree(VerifyTree(newrep));
+ }
+}
+
+// Work item for NewSubRange().
+struct SubRange {
+ SubRange(CordRep* a_node, size_t a_pos, size_t a_n)
+ : node(a_node), pos(a_pos), n(a_n) {}
+ CordRep* node; // nullptr means concat last 2 results.
+ size_t pos;
+ size_t n;
+};
+
+static CordRep* NewSubRange(CordRep* node, size_t pos, size_t n) {
+ absl::InlinedVector<CordRep*, kInlinedVectorSize> results;
+ absl::InlinedVector<SubRange, kInlinedVectorSize> todo;
+ todo.push_back(SubRange(node, pos, n));
+ do {
+ const SubRange& sr = todo.back();
+ node = sr.node;
+ pos = sr.pos;
+ n = sr.n;
+ todo.pop_back();
+
+ if (node == nullptr) {
+ assert(results.size() >= 2);
+ CordRep* right = results.back();
+ results.pop_back();
+ CordRep* left = results.back();
+ results.pop_back();
+ results.push_back(Concat(left, right));
+ } else if (pos == 0 && n == node->length) {
+ results.push_back(Ref(node));
+ } else if (node->tag != CONCAT) {
+ if (node->tag == SUBSTRING) {
+ pos += node->substring()->start;
+ node = node->substring()->child;
+ }
+ results.push_back(NewSubstring(Ref(node), pos, n));
+ } else if (pos + n <= node->concat()->left->length) {
+ todo.push_back(SubRange(node->concat()->left, pos, n));
+ } else if (pos >= node->concat()->left->length) {
+ pos -= node->concat()->left->length;
+ todo.push_back(SubRange(node->concat()->right, pos, n));
+ } else {
+ size_t left_n = node->concat()->left->length - pos;
+ todo.push_back(SubRange(nullptr, 0, 0)); // Concat()
+ todo.push_back(SubRange(node->concat()->right, 0, n - left_n));
+ todo.push_back(SubRange(node->concat()->left, pos, left_n));
+ }
+ } while (!todo.empty());
+ assert(results.size() == 1);
+ return results[0];
+}
+
+Cord Cord::Subcord(size_t pos, size_t new_size) const {
+ Cord sub_cord;
+ size_t length = size();
+ if (pos > length) pos = length;
+ if (new_size > length - pos) new_size = length - pos;
+ CordRep* tree = contents_.tree();
+ if (tree == nullptr) {
+ // sub_cord is newly constructed, no need to re-zero-out the tail of
+ // contents_ memory.
+ sub_cord.contents_.set_data(contents_.data() + pos, new_size, false);
+ } else if (new_size == 0) {
+ // We want to return empty subcord, so nothing to do.
+ } else if (new_size <= InlineRep::kMaxInline) {
+ Cord::ChunkIterator it = chunk_begin();
+ it.AdvanceBytes(pos);
+ char* dest = sub_cord.contents_.data_;
+ size_t remaining_size = new_size;
+ while (remaining_size > it->size()) {
+ cord_internal::SmallMemmove(dest, it->data(), it->size());
+ remaining_size -= it->size();
+ dest += it->size();
+ ++it;
+ }
+ cord_internal::SmallMemmove(dest, it->data(), remaining_size);
+ sub_cord.contents_.data_[InlineRep::kMaxInline] = new_size;
+ } else {
+ sub_cord.contents_.set_tree(NewSubRange(tree, pos, new_size));
+ }
+ return sub_cord;
+}
+
+// --------------------------------------------------------------------
+// Balancing
+
+class CordForest {
+ public:
+ explicit CordForest(size_t length)
+ : root_length_(length), trees_(kMinLengthSize, nullptr) {}
+
+ void Build(CordRep* cord_root) {
+ std::vector<CordRep*> pending = {cord_root};
+
+ while (!pending.empty()) {
+ CordRep* node = pending.back();
+ pending.pop_back();
+ CheckNode(node);
+ if (ABSL_PREDICT_FALSE(node->tag != CONCAT)) {
+ AddNode(node);
+ continue;
+ }
+
+ CordRepConcat* concat_node = node->concat();
+ if (concat_node->depth() >= kMinLengthSize ||
+ concat_node->length < min_length[concat_node->depth()]) {
+ pending.push_back(concat_node->right);
+ pending.push_back(concat_node->left);
+
+ if (concat_node->refcount.IsOne()) {
+ concat_node->left = concat_freelist_;
+ concat_freelist_ = concat_node;
+ } else {
+ Ref(concat_node->right);
+ Ref(concat_node->left);
+ Unref(concat_node);
+ }
+ } else {
+ AddNode(node);
+ }
+ }
+ }
+
+ CordRep* ConcatNodes() {
+ CordRep* sum = nullptr;
+ for (auto* node : trees_) {
+ if (node == nullptr) continue;
+
+ sum = PrependNode(node, sum);
+ root_length_ -= node->length;
+ if (root_length_ == 0) break;
+ }
+ ABSL_INTERNAL_CHECK(sum != nullptr, "Failed to locate sum node");
+ return VerifyTree(sum);
+ }
+
+ private:
+ CordRep* AppendNode(CordRep* node, CordRep* sum) {
+ return (sum == nullptr) ? node : MakeConcat(sum, node);
+ }
+
+ CordRep* PrependNode(CordRep* node, CordRep* sum) {
+ return (sum == nullptr) ? node : MakeConcat(node, sum);
+ }
+
+ void AddNode(CordRep* node) {
+ CordRep* sum = nullptr;
+
+ // Collect together everything with which we will merge node
+ int i = 0;
+ for (; node->length > min_length[i + 1]; ++i) {
+ auto& tree_at_i = trees_[i];
+
+ if (tree_at_i == nullptr) continue;
+ sum = PrependNode(tree_at_i, sum);
+ tree_at_i = nullptr;
+ }
+
+ sum = AppendNode(node, sum);
+
+ // Insert sum into appropriate place in the forest
+ for (; sum->length >= min_length[i]; ++i) {
+ auto& tree_at_i = trees_[i];
+ if (tree_at_i == nullptr) continue;
+
+ sum = MakeConcat(tree_at_i, sum);
+ tree_at_i = nullptr;
+ }
+
+ // min_length[0] == 1, which means sum->length >= min_length[0]
+ assert(i > 0);
+ trees_[i - 1] = sum;
+ }
+
+ // Make concat node trying to resue existing CordRepConcat nodes we
+ // already collected in the concat_freelist_.
+ CordRep* MakeConcat(CordRep* left, CordRep* right) {
+ if (concat_freelist_ == nullptr) return RawConcat(left, right);
+
+ CordRepConcat* rep = concat_freelist_;
+ if (concat_freelist_->left == nullptr) {
+ concat_freelist_ = nullptr;
+ } else {
+ concat_freelist_ = concat_freelist_->left->concat();
+ }
+ SetConcatChildren(rep, left, right);
+
+ return rep;
+ }
+
+ static void CheckNode(CordRep* node) {
+ ABSL_INTERNAL_CHECK(node->length != 0u, "");
+ if (node->tag == CONCAT) {
+ ABSL_INTERNAL_CHECK(node->concat()->left != nullptr, "");
+ ABSL_INTERNAL_CHECK(node->concat()->right != nullptr, "");
+ ABSL_INTERNAL_CHECK(node->length == (node->concat()->left->length +
+ node->concat()->right->length),
+ "");
+ }
+ }
+
+ size_t root_length_;
+
+ // use an inlined vector instead of a flat array to get bounds checking
+ absl::InlinedVector<CordRep*, kInlinedVectorSize> trees_;
+
+ // List of concat nodes we can re-use for Cord balancing.
+ CordRepConcat* concat_freelist_ = nullptr;
+};
+
+static CordRep* Rebalance(CordRep* node) {
+ VerifyTree(node);
+ assert(node->tag == CONCAT);
+
+ if (node->length == 0) {
+ return nullptr;
+ }
+
+ CordForest forest(node->length);
+ forest.Build(node);
+ return forest.ConcatNodes();
+}
+
+// --------------------------------------------------------------------
+// Comparators
+
+namespace {
+
+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) {
+ size_t compared_size = std::min(lhs->size(), rhs->size());
+ assert(*size_to_compare >= compared_size);
+ *size_to_compare -= compared_size;
+
+ int memcmp_res = ::memcmp(lhs->data(), rhs->data(), compared_size);
+ if (memcmp_res != 0) return memcmp_res;
+
+ lhs->remove_prefix(compared_size);
+ rhs->remove_prefix(compared_size);
+
+ return 0;
+}
+
+// This overload set computes comparison results from memcmp result. This
+// interface is used inside GenericCompare below. Differet implementations
+// are specialized for int and bool. For int we clamp result to {-1, 0, 1}
+// set. For bool we just interested in "value == 0".
+template <typename ResultType>
+ResultType ComputeCompareResult(int memcmp_res) {
+ return ClampResult(memcmp_res);
+}
+template <>
+bool ComputeCompareResult<bool>(int memcmp_res) {
+ return memcmp_res == 0;
+}
+
+} // namespace
+
+// Helper routine. Locates the first flat chunk of the Cord without
+// initializing the iterator.
+inline absl::string_view Cord::InlineRep::FindFlatStartPiece() const {
+ size_t n = data_[kMaxInline];
+ if (n <= kMaxInline) {
+ return absl::string_view(data_, n);
+ }
+
+ CordRep* node = tree();
+ if (node->tag >= FLAT) {
+ return absl::string_view(node->data, node->length);
+ }
+
+ if (node->tag == EXTERNAL) {
+ return absl::string_view(node->external()->base, node->length);
+ }
+
+ // Walk down the left branches until we hit a non-CONCAT node.
+ while (node->tag == CONCAT) {
+ node = node->concat()->left;
+ }
+
+ // Get the child node if we encounter a SUBSTRING.
+ size_t offset = 0;
+ size_t length = node->length;
+ assert(length != 0);
+
+ if (node->tag == SUBSTRING) {
+ offset = node->substring()->start;
+ node = node->substring()->child;
+ }
+
+ if (node->tag >= FLAT) {
+ return absl::string_view(node->data + offset, length);
+ }
+
+ assert((node->tag == EXTERNAL) && "Expect FLAT or EXTERNAL node here");
+
+ return absl::string_view(node->external()->base + offset, length);
+}
+
+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) {
+ if (!chunk->empty()) return true;
+ ++*it;
+ if (it->bytes_remaining_ == 0) return false;
+ *chunk = **it;
+ return true;
+ };
+
+ Cord::ChunkIterator lhs_it = chunk_begin();
+
+ // compared_size is inside first chunk.
+ absl::string_view lhs_chunk =
+ (lhs_it.bytes_remaining_ != 0) ? *lhs_it : absl::string_view();
+ assert(compared_size <= lhs_chunk.size());
+ assert(compared_size <= rhs.size());
+ lhs_chunk.remove_prefix(compared_size);
+ rhs.remove_prefix(compared_size);
+ size_to_compare -= compared_size; // skip already compared size.
+
+ while (advance(&lhs_it, &lhs_chunk) && !rhs.empty()) {
+ int comparison_result = CompareChunks(&lhs_chunk, &rhs, &size_to_compare);
+ if (comparison_result != 0) return comparison_result;
+ if (size_to_compare == 0) return 0;
+ }
+
+ return static_cast<int>(rhs.empty()) - static_cast<int>(lhs_chunk.empty());
+}
+
+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) {
+ if (!chunk->empty()) return true;
+ ++*it;
+ if (it->bytes_remaining_ == 0) return false;
+ *chunk = **it;
+ return true;
+ };
+
+ Cord::ChunkIterator lhs_it = chunk_begin();
+ Cord::ChunkIterator rhs_it = rhs.chunk_begin();
+
+ // compared_size is inside both first chunks.
+ absl::string_view lhs_chunk =
+ (lhs_it.bytes_remaining_ != 0) ? *lhs_it : absl::string_view();
+ absl::string_view rhs_chunk =
+ (rhs_it.bytes_remaining_ != 0) ? *rhs_it : absl::string_view();
+ assert(compared_size <= lhs_chunk.size());
+ assert(compared_size <= rhs_chunk.size());
+ lhs_chunk.remove_prefix(compared_size);
+ rhs_chunk.remove_prefix(compared_size);
+ size_to_compare -= compared_size; // skip already compared size.
+
+ while (advance(&lhs_it, &lhs_chunk) && advance(&rhs_it, &rhs_chunk)) {
+ int memcmp_res = CompareChunks(&lhs_chunk, &rhs_chunk, &size_to_compare);
+ if (memcmp_res != 0) return memcmp_res;
+ if (size_to_compare == 0) return 0;
+ }
+
+ return static_cast<int>(rhs_chunk.empty()) -
+ static_cast<int>(lhs_chunk.empty());
+}
+
+inline absl::string_view Cord::GetFirstChunk(const Cord& c) {
+ return c.contents_.FindFlatStartPiece();
+}
+inline absl::string_view Cord::GetFirstChunk(absl::string_view sv) {
+ return sv;
+}
+
+// Compares up to 'size_to_compare' bytes of 'lhs' with 'rhs'. It is assumed
+// that 'size_to_compare' is greater that size of smallest of first chunks.
+template <typename ResultType, typename RHS>
+ResultType GenericCompare(const Cord& lhs, const RHS& rhs,
+ size_t size_to_compare) {
+ absl::string_view lhs_chunk = Cord::GetFirstChunk(lhs);
+ absl::string_view rhs_chunk = Cord::GetFirstChunk(rhs);
+
+ size_t compared_size = std::min(lhs_chunk.size(), rhs_chunk.size());
+ assert(size_to_compare >= compared_size);
+ int memcmp_res = ::memcmp(lhs_chunk.data(), rhs_chunk.data(), compared_size);
+ if (compared_size == size_to_compare || memcmp_res != 0) {
+ return ComputeCompareResult<ResultType>(memcmp_res);
+ }
+
+ return ComputeCompareResult<ResultType>(
+ lhs.CompareSlowPath(rhs, compared_size, size_to_compare));
+}
+
+bool Cord::EqualsImpl(absl::string_view rhs, size_t size_to_compare) const {
+ return GenericCompare<bool>(*this, rhs, size_to_compare);
+}
+
+bool Cord::EqualsImpl(const Cord& rhs, size_t size_to_compare) const {
+ return GenericCompare<bool>(*this, rhs, size_to_compare);
+}
+
+template <typename RHS>
+inline int SharedCompareImpl(const Cord& lhs, const RHS& rhs) {
+ size_t lhs_size = lhs.size();
+ size_t rhs_size = rhs.size();
+ if (lhs_size == rhs_size) {
+ return GenericCompare<int>(lhs, rhs, lhs_size);
+ }
+ if (lhs_size < rhs_size) {
+ auto data_comp_res = GenericCompare<int>(lhs, rhs, lhs_size);
+ return data_comp_res == 0 ? -1 : data_comp_res;
+ }
+
+ auto data_comp_res = GenericCompare<int>(lhs, rhs, rhs_size);
+ return data_comp_res == 0 ? +1 : data_comp_res;
+}
+
+int Cord::Compare(absl::string_view rhs) const {
+ return SharedCompareImpl(*this, rhs);
+}
+
+int Cord::CompareImpl(const Cord& rhs) const {
+ return SharedCompareImpl(*this, rhs);
+}
+
+bool Cord::EndsWith(absl::string_view rhs) const {
+ size_t my_size = size();
+ size_t rhs_size = rhs.size();
+
+ if (my_size < rhs_size) return false;
+
+ Cord tmp(*this);
+ tmp.RemovePrefix(my_size - rhs_size);
+ return tmp.EqualsImpl(rhs, rhs_size);
+}
+
+bool Cord::EndsWith(const Cord& rhs) const {
+ size_t my_size = size();
+ size_t rhs_size = rhs.size();
+
+ if (my_size < rhs_size) return false;
+
+ Cord tmp(*this);
+ tmp.RemovePrefix(my_size - rhs_size);
+ return tmp.EqualsImpl(rhs, rhs_size);
+}
+
+// --------------------------------------------------------------------
+// Misc.
+
+Cord::operator std::string() const {
+ std::string s;
+ absl::CopyCordToString(*this, &s);
+ return s;
+}
+
+void CopyCordToString(const Cord& src, std::string* dst) {
+ if (!src.contents_.is_tree()) {
+ src.contents_.CopyTo(dst);
+ } else {
+ absl::strings_internal::STLStringResizeUninitialized(dst, src.size());
+ src.CopyToArraySlowPath(&(*dst)[0]);
+ }
+}
+
+void Cord::CopyToArraySlowPath(char* dst) const {
+ assert(contents_.is_tree());
+ absl::string_view fragment;
+ if (GetFlatAux(contents_.tree(), &fragment)) {
+ memcpy(dst, fragment.data(), fragment.size());
+ return;
+ }
+ for (absl::string_view chunk : Chunks()) {
+ memcpy(dst, chunk.data(), chunk.size());
+ dst += chunk.size();
+ }
+}
+
+Cord::ChunkIterator& Cord::ChunkIterator::operator++() {
+ assert(bytes_remaining_ > 0 && "Attempted to iterate past `end()`");
+ assert(bytes_remaining_ >= current_chunk_.size());
+ bytes_remaining_ -= current_chunk_.size();
+
+ if (stack_of_right_children_.empty()) {
+ assert(!current_chunk_.empty()); // Called on invalid iterator.
+ // We have reached the end of the Cord.
+ return *this;
+ }
+
+ // Process the next node on the stack.
+ CordRep* node = stack_of_right_children_.back();
+ stack_of_right_children_.pop_back();
+
+ // Walk down the left branches until we hit a non-CONCAT node. Save the
+ // right children to the stack for subsequent traversal.
+ while (node->tag == CONCAT) {
+ stack_of_right_children_.push_back(node->concat()->right);
+ node = node->concat()->left;
+ }
+
+ // Get the child node if we encounter a SUBSTRING.
+ size_t offset = 0;
+ size_t length = node->length;
+ if (node->tag == SUBSTRING) {
+ offset = node->substring()->start;
+ node = node->substring()->child;
+ }
+
+ assert(node->tag == EXTERNAL || node->tag >= FLAT);
+ assert(length != 0);
+ const char* data =
+ node->tag == EXTERNAL ? node->external()->base : node->data;
+ current_chunk_ = absl::string_view(data + offset, length);
+ current_leaf_ = node;
+ return *this;
+}
+
+Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) {
+ assert(bytes_remaining_ >= n && "Attempted to iterate past `end()`");
+ Cord subcord;
+
+ if (n <= InlineRep::kMaxInline) {
+ // Range to read fits in inline data. Flatten it.
+ char* data = subcord.contents_.set_data(n);
+ while (n > current_chunk_.size()) {
+ memcpy(data, current_chunk_.data(), current_chunk_.size());
+ data += current_chunk_.size();
+ n -= current_chunk_.size();
+ ++*this;
+ }
+ memcpy(data, current_chunk_.data(), n);
+ if (n < current_chunk_.size()) {
+ RemoveChunkPrefix(n);
+ } else if (n > 0) {
+ ++*this;
+ }
+ return subcord;
+ }
+ if (n < current_chunk_.size()) {
+ // Range to read is a proper subrange of the current chunk.
+ assert(current_leaf_ != nullptr);
+ CordRep* subnode = Ref(current_leaf_);
+ const char* data =
+ subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data;
+ subnode = NewSubstring(subnode, current_chunk_.data() - data, n);
+ subcord.contents_.set_tree(VerifyTree(subnode));
+ RemoveChunkPrefix(n);
+ return subcord;
+ }
+
+ // Range to read begins with a proper subrange of the current chunk.
+ assert(!current_chunk_.empty());
+ assert(current_leaf_ != nullptr);
+ CordRep* subnode = Ref(current_leaf_);
+ if (current_chunk_.size() < subnode->length) {
+ const char* data =
+ subnode->tag == EXTERNAL ? subnode->external()->base : subnode->data;
+ subnode = NewSubstring(subnode, current_chunk_.data() - data,
+ current_chunk_.size());
+ }
+ n -= current_chunk_.size();
+ bytes_remaining_ -= current_chunk_.size();
+
+ // Process the next node(s) on the stack, reading whole subtrees depending on
+ // their length and how many bytes we are advancing.
+ CordRep* node = nullptr;
+ while (!stack_of_right_children_.empty()) {
+ node = stack_of_right_children_.back();
+ stack_of_right_children_.pop_back();
+ if (node->length > n) break;
+ // TODO(qrczak): This might unnecessarily recreate existing concat nodes.
+ // Avoiding that would need pretty complicated logic (instead of
+ // current_leaf_, keep current_subtree_ which points to the highest node
+ // such that the current leaf can be found on the path of left children
+ // starting from current_subtree_; delay creating subnode while node is
+ // below current_subtree_; find the proper node along the path of left
+ // children starting from current_subtree_ if this loop exits while staying
+ // below current_subtree_; etc.; alternatively, push parents instead of
+ // right children on the stack).
+ subnode = Concat(subnode, Ref(node));
+ n -= node->length;
+ bytes_remaining_ -= node->length;
+ node = nullptr;
+ }
+
+ if (node == nullptr) {
+ // We have reached the end of the Cord.
+ assert(bytes_remaining_ == 0);
+ subcord.contents_.set_tree(VerifyTree(subnode));
+ return subcord;
+ }
+
+ // Walk down the appropriate branches until we hit a non-CONCAT node. Save the
+ // right children to the stack for subsequent traversal.
+ while (node->tag == CONCAT) {
+ if (node->concat()->left->length > n) {
+ // Push right, descend left.
+ stack_of_right_children_.push_back(node->concat()->right);
+ node = node->concat()->left;
+ } else {
+ // Read left, descend right.
+ subnode = Concat(subnode, Ref(node->concat()->left));
+ n -= node->concat()->left->length;
+ bytes_remaining_ -= node->concat()->left->length;
+ node = node->concat()->right;
+ }
+ }
+
+ // Get the child node if we encounter a SUBSTRING.
+ size_t offset = 0;
+ size_t length = node->length;
+ if (node->tag == SUBSTRING) {
+ offset = node->substring()->start;
+ node = node->substring()->child;
+ }
+
+ // Range to read ends with a proper (possibly empty) subrange of the current
+ // chunk.
+ assert(node->tag == EXTERNAL || node->tag >= FLAT);
+ assert(length > n);
+ if (n > 0) subnode = Concat(subnode, NewSubstring(Ref(node), offset, n));
+ const char* data =
+ node->tag == EXTERNAL ? node->external()->base : node->data;
+ current_chunk_ = absl::string_view(data + offset + n, length - n);
+ current_leaf_ = node;
+ bytes_remaining_ -= n;
+ subcord.contents_.set_tree(VerifyTree(subnode));
+ return subcord;
+}
+
+void Cord::ChunkIterator::AdvanceBytesSlowPath(size_t n) {
+ assert(bytes_remaining_ >= n && "Attempted to iterate past `end()`");
+ assert(n >= current_chunk_.size()); // This should only be called when
+ // iterating to a new node.
+
+ n -= current_chunk_.size();
+ bytes_remaining_ -= current_chunk_.size();
+
+ // Process the next node(s) on the stack, skipping whole subtrees depending on
+ // their length and how many bytes we are advancing.
+ CordRep* node = nullptr;
+ while (!stack_of_right_children_.empty()) {
+ node = stack_of_right_children_.back();
+ stack_of_right_children_.pop_back();
+ if (node->length > n) break;
+ n -= node->length;
+ bytes_remaining_ -= node->length;
+ node = nullptr;
+ }
+
+ if (node == nullptr) {
+ // We have reached the end of the Cord.
+ assert(bytes_remaining_ == 0);
+ return;
+ }
+
+ // Walk down the appropriate branches until we hit a non-CONCAT node. Save the
+ // right children to the stack for subsequent traversal.
+ while (node->tag == CONCAT) {
+ if (node->concat()->left->length > n) {
+ // Push right, descend left.
+ stack_of_right_children_.push_back(node->concat()->right);
+ node = node->concat()->left;
+ } else {
+ // Skip left, descend right.
+ n -= node->concat()->left->length;
+ bytes_remaining_ -= node->concat()->left->length;
+ node = node->concat()->right;
+ }
+ }
+
+ // Get the child node if we encounter a SUBSTRING.
+ size_t offset = 0;
+ size_t length = node->length;
+ if (node->tag == SUBSTRING) {
+ offset = node->substring()->start;
+ node = node->substring()->child;
+ }
+
+ assert(node->tag == EXTERNAL || node->tag >= FLAT);
+ assert(length > n);
+ const char* data =
+ node->tag == EXTERNAL ? node->external()->base : node->data;
+ current_chunk_ = absl::string_view(data + offset + n, length - n);
+ current_leaf_ = node;
+ bytes_remaining_ -= n;
+}
+
+char Cord::operator[](size_t i) const {
+ assert(i < size());
+ size_t offset = i;
+ const CordRep* rep = contents_.tree();
+ if (rep == nullptr) {
+ return contents_.data()[i];
+ }
+ while (true) {
+ assert(rep != nullptr);
+ assert(offset < rep->length);
+ if (rep->tag >= FLAT) {
+ // Get the "i"th character directly from the flat array.
+ return rep->data[offset];
+ } else if (rep->tag == EXTERNAL) {
+ // Get the "i"th character from the external array.
+ return rep->external()->base[offset];
+ } else if (rep->tag == CONCAT) {
+ // Recursively branch to the side of the concatenation that the "i"th
+ // character is on.
+ size_t left_length = rep->concat()->left->length;
+ if (offset < left_length) {
+ rep = rep->concat()->left;
+ } else {
+ offset -= left_length;
+ rep = rep->concat()->right;
+ }
+ } else {
+ // This must be a substring a node, so bypass it to get to the child.
+ assert(rep->tag == SUBSTRING);
+ offset += rep->substring()->start;
+ rep = rep->substring()->child;
+ }
+ }
+}
+
+absl::string_view Cord::FlattenSlowPath() {
+ size_t total_size = size();
+ CordRep* new_rep;
+ char* new_buffer;
+
+ // Try to put the contents into a new flat rep. If they won't fit in the
+ // biggest possible flat node, use an external rep instead.
+ if (total_size <= kMaxFlatLength) {
+ new_rep = NewFlat(total_size);
+ new_rep->length = total_size;
+ new_buffer = new_rep->data;
+ CopyToArraySlowPath(new_buffer);
+ } else {
+ new_buffer = std::allocator<char>().allocate(total_size);
+ CopyToArraySlowPath(new_buffer);
+ new_rep = absl::cord_internal::NewExternalRep(
+ absl::string_view(new_buffer, total_size), [](absl::string_view s) {
+ std::allocator<char>().deallocate(const_cast<char*>(s.data()),
+ s.size());
+ });
+ }
+ Unref(contents_.tree());
+ contents_.set_tree(new_rep);
+ return absl::string_view(new_buffer, total_size);
+}
+
+/* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) {
+ assert(rep != nullptr);
+ if (rep->tag >= FLAT) {
+ *fragment = absl::string_view(rep->data, rep->length);
+ return true;
+ } else if (rep->tag == EXTERNAL) {
+ *fragment = absl::string_view(rep->external()->base, rep->length);
+ return true;
+ } else if (rep->tag == SUBSTRING) {
+ CordRep* child = rep->substring()->child;
+ if (child->tag >= FLAT) {
+ *fragment =
+ absl::string_view(child->data + rep->substring()->start, rep->length);
+ return true;
+ } else if (child->tag == EXTERNAL) {
+ *fragment = absl::string_view(
+ child->external()->base + rep->substring()->start, rep->length);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* static */ void Cord::ForEachChunkAux(
+ absl::cord_internal::CordRep* rep,
+ absl::FunctionRef<void(absl::string_view)> callback) {
+ assert(rep != nullptr);
+ int stack_pos = 0;
+ constexpr int stack_max = 128;
+ // Stack of right branches for tree traversal
+ absl::cord_internal::CordRep* stack[stack_max];
+ absl::cord_internal::CordRep* current_node = rep;
+ while (true) {
+ if (current_node->tag == CONCAT) {
+ if (stack_pos == stack_max) {
+ // There's no more room on our stack array to add another right branch,
+ // and the idea is to avoid allocations, so call this function
+ // recursively to navigate this subtree further. (This is not something
+ // we expect to happen in practice).
+ ForEachChunkAux(current_node, callback);
+
+ // Pop the next right branch and iterate.
+ current_node = stack[--stack_pos];
+ continue;
+ } else {
+ // Save the right branch for later traversal and continue down the left
+ // branch.
+ stack[stack_pos++] = current_node->concat()->right;
+ current_node = current_node->concat()->left;
+ continue;
+ }
+ }
+ // This is a leaf node, so invoke our callback.
+ absl::string_view chunk;
+ bool success = GetFlatAux(current_node, &chunk);
+ assert(success);
+ if (success) {
+ callback(chunk);
+ }
+ if (stack_pos == 0) {
+ // end of traversal
+ return;
+ }
+ current_node = stack[--stack_pos];
+ }
+}
+
+static void DumpNode(CordRep* rep, bool include_data, std::ostream* os) {
+ const int kIndentStep = 1;
+ int indent = 0;
+ absl::InlinedVector<CordRep*, kInlinedVectorSize> stack;
+ absl::InlinedVector<int, kInlinedVectorSize> indents;
+ for (;;) {
+ *os << std::setw(3) << rep->refcount.Get();
+ *os << " " << std::setw(7) << rep->length;
+ *os << " [";
+ if (include_data) *os << static_cast<void*>(rep);
+ *os << "]";
+ *os << " " << (IsRootBalanced(rep) ? 'b' : 'u');
+ *os << " " << std::setw(indent) << "";
+ if (rep->tag == CONCAT) {
+ *os << "CONCAT depth=" << Depth(rep) << "\n";
+ indent += kIndentStep;
+ indents.push_back(indent);
+ stack.push_back(rep->concat()->right);
+ rep = rep->concat()->left;
+ } else if (rep->tag == SUBSTRING) {
+ *os << "SUBSTRING @ " << rep->substring()->start << "\n";
+ indent += kIndentStep;
+ rep = rep->substring()->child;
+ } else { // Leaf
+ if (rep->tag == EXTERNAL) {
+ *os << "EXTERNAL [";
+ if (include_data)
+ *os << absl::CEscape(std::string(rep->external()->base, rep->length));
+ *os << "]\n";
+ } else {
+ *os << "FLAT cap=" << TagToLength(rep->tag) << " [";
+ if (include_data)
+ *os << absl::CEscape(std::string(rep->data, rep->length));
+ *os << "]\n";
+ }
+ if (stack.empty()) break;
+ rep = stack.back();
+ stack.pop_back();
+ indent = indents.back();
+ indents.pop_back();
+ }
+ }
+ ABSL_INTERNAL_CHECK(indents.empty(), "");
+}
+
+static std::string ReportError(CordRep* root, 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;
+ worklist.push_back(start_node);
+ do {
+ CordRep* node = worklist.back();
+ worklist.pop_back();
+
+ ABSL_INTERNAL_CHECK(node != nullptr, ReportError(root, node));
+ if (node != root) {
+ ABSL_INTERNAL_CHECK(node->length != 0, ReportError(root, node));
+ }
+
+ if (node->tag == CONCAT) {
+ ABSL_INTERNAL_CHECK(node->concat()->left != nullptr,
+ ReportError(root, node));
+ ABSL_INTERNAL_CHECK(node->concat()->right != nullptr,
+ ReportError(root, node));
+ ABSL_INTERNAL_CHECK((node->length == node->concat()->left->length +
+ node->concat()->right->length),
+ ReportError(root, node));
+ if (full_validation) {
+ worklist.push_back(node->concat()->right);
+ worklist.push_back(node->concat()->left);
+ }
+ } else if (node->tag >= FLAT) {
+ ABSL_INTERNAL_CHECK(node->length <= TagToLength(node->tag),
+ ReportError(root, node));
+ } else if (node->tag == EXTERNAL) {
+ ABSL_INTERNAL_CHECK(node->external()->base != nullptr,
+ ReportError(root, node));
+ } else if (node->tag == SUBSTRING) {
+ ABSL_INTERNAL_CHECK(
+ node->substring()->start < node->substring()->child->length,
+ ReportError(root, node));
+ ABSL_INTERNAL_CHECK(node->substring()->start + node->length <=
+ node->substring()->child->length,
+ ReportError(root, node));
+ }
+ } while (!worklist.empty());
+ return true;
+}
+
+// Traverses the tree and computes the total memory allocated.
+/* static */ size_t Cord::MemoryUsageAux(const CordRep* rep) {
+ size_t total_mem_usage = 0;
+
+ // Allow a quick exit for the common case that the root is a leaf.
+ if (RepMemoryUsageLeaf(rep, &total_mem_usage)) {
+ return total_mem_usage;
+ }
+
+ // Iterate over the tree. cur_node is never a leaf node and leaf nodes will
+ // never be appended to tree_stack. This reduces overhead from manipulating
+ // tree_stack.
+ absl::InlinedVector<const CordRep*, kInlinedVectorSize> tree_stack;
+ const CordRep* cur_node = rep;
+ while (true) {
+ const CordRep* next_node = nullptr;
+
+ if (cur_node->tag == CONCAT) {
+ total_mem_usage += sizeof(CordRepConcat);
+ const CordRep* left = cur_node->concat()->left;
+ if (!RepMemoryUsageLeaf(left, &total_mem_usage)) {
+ next_node = left;
+ }
+
+ const CordRep* right = cur_node->concat()->right;
+ if (!RepMemoryUsageLeaf(right, &total_mem_usage)) {
+ if (next_node) {
+ tree_stack.push_back(next_node);
+ }
+ next_node = right;
+ }
+ } else {
+ // Since cur_node is not a leaf or a concat node it must be a substring.
+ assert(cur_node->tag == SUBSTRING);
+ total_mem_usage += sizeof(CordRepSubstring);
+ next_node = cur_node->substring()->child;
+ if (RepMemoryUsageLeaf(next_node, &total_mem_usage)) {
+ next_node = nullptr;
+ }
+ }
+
+ if (!next_node) {
+ if (tree_stack.empty()) {
+ return total_mem_usage;
+ }
+ next_node = tree_stack.back();
+ tree_stack.pop_back();
+ }
+ cur_node = next_node;
+ }
+}
+
+std::ostream& operator<<(std::ostream& out, const Cord& cord) {
+ for (absl::string_view chunk : cord.Chunks()) {
+ out.write(chunk.data(), chunk.size());
+ }
+ return out;
+}
+
+namespace strings_internal {
+size_t CordTestAccess::FlatOverhead() { return kFlatOverhead; }
+size_t CordTestAccess::MaxFlatLength() { return kMaxFlatLength; }
+size_t CordTestAccess::FlatTagToLength(uint8_t tag) {
+ return TagToLength(tag);
+}
+uint8_t CordTestAccess::LengthToTag(size_t s) {
+ ABSL_INTERNAL_CHECK(s <= kMaxFlatLength, absl::StrCat("Invalid length ", s));
+ return AllocatedSizeToTag(s + kFlatOverhead);
+}
+size_t CordTestAccess::SizeofCordRepConcat() { return sizeof(CordRepConcat); }
+size_t CordTestAccess::SizeofCordRepExternal() {
+ return sizeof(CordRepExternal);
+}
+size_t CordTestAccess::SizeofCordRepSubstring() {
+ return sizeof(CordRepSubstring);
+}
+} // namespace strings_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/strings/cord.h b/absl/strings/cord.h
new file mode 100644
index 00000000..40566cba
--- /dev/null
+++ b/absl/strings/cord.h
@@ -0,0 +1,1121 @@
+// 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.
+
+// A Cord is a sequence of characters with some unusual access propreties.
+// A Cord supports efficient insertions and deletions at the start and end of
+// the byte sequence, but random access reads are slower, and random access
+// modifications are not supported by the API. Cord also provides cheap copies
+// (using a copy-on-write strategy) and cheap substring operations.
+//
+// Thread safety
+// -------------
+// Cord has the same thread-safety properties as many other types like
+// std::string, std::vector<>, int, etc -- it is thread-compatible. In
+// particular, if no thread may call a non-const method, then it is safe to
+// concurrently call const methods. Copying a Cord produces a new instance that
+// can be used concurrently with the original in arbitrary ways.
+//
+// Implementation is similar to the "Ropes" described in:
+// Ropes: An alternative to strings
+// Hans J. Boehm, Russ Atkinson, Michael Plass
+// Software Practice and Experience, December 1995
+
+#ifndef ABSL_STRINGS_CORD_H_
+#define ABSL_STRINGS_CORD_H_
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <iterator>
+#include <string>
+
+#include "absl/base/internal/endian.h"
+#include "absl/base/internal/invoke.h"
+#include "absl/base/internal/per_thread_tls.h"
+#include "absl/base/macros.h"
+#include "absl/base/port.h"
+#include "absl/container/inlined_vector.h"
+#include "absl/functional/function_ref.h"
+#include "absl/meta/type_traits.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/resize_uninitialized.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+class Cord;
+class CordTestPeer;
+template <typename Releaser>
+Cord MakeCordFromExternal(absl::string_view, Releaser&&);
+void CopyCordToString(const Cord& src, std::string* dst);
+namespace hash_internal {
+template <typename H>
+H HashFragmentedCord(H, const Cord&);
+}
+
+// A Cord is a sequence of characters.
+class Cord {
+ private:
+ template <typename T>
+ using EnableIfString =
+ absl::enable_if_t<std::is_same<T, std::string>::value, int>;
+
+ public:
+ // --------------------------------------------------------------------
+ // Constructors, destructors and helper factories
+
+ // Create an empty cord
+ constexpr Cord() noexcept;
+
+ // Cord is copyable and efficiently movable.
+ // The moved-from state is valid but unspecified.
+ Cord(const Cord& src);
+ Cord(Cord&& src) noexcept;
+ Cord& operator=(const Cord& x);
+ Cord& operator=(Cord&& x) noexcept;
+
+ // Create a cord out of "src". This constructor is explicit on
+ // purpose so that people do not get automatic type conversions.
+ explicit Cord(absl::string_view src);
+ Cord& operator=(absl::string_view src);
+
+ // These are templated to avoid ambiguities for types that are convertible to
+ // both `absl::string_view` and `std::string`, such as `const char*`.
+ //
+ // Note that these functions reserve the right to reuse the `string&&`'s
+ // memory and that they will do so in the future.
+ template <typename T, EnableIfString<T> = 0>
+ explicit Cord(T&& src) : Cord(absl::string_view(src)) {}
+ template <typename T, EnableIfString<T> = 0>
+ Cord& operator=(T&& src);
+
+ // Destroy the cord
+ ~Cord() {
+ if (contents_.is_tree()) DestroyCordSlow();
+ }
+
+ // Creates a Cord that takes ownership of external memory. The contents of
+ // `data` are not copied.
+ //
+ // This function takes a callable that is invoked when all Cords are
+ // finished with `data`. The data must remain live and unchanging until the
+ // releaser is called. The requirements for the releaser are that it:
+ // * is move constructible,
+ // * supports `void operator()(absl::string_view) const`,
+ // * does not have alignment requirement greater than what is guaranteed by
+ // ::operator new. This is dictated by alignof(std::max_align_t) before
+ // C++17 and __STDCPP_DEFAULT_NEW_ALIGNMENT__ if compiling with C++17 or
+ // it is supported by the implementation.
+ //
+ // Example:
+ //
+ // Cord MakeCord(BlockPool* pool) {
+ // Block* block = pool->NewBlock();
+ // FillBlock(block);
+ // return absl::MakeCordFromExternal(
+ // block->ToStringView(),
+ // [pool, block](absl::string_view /*ignored*/) {
+ // pool->FreeBlock(block);
+ // });
+ // }
+ //
+ // WARNING: It's likely a bug if your releaser doesn't do anything.
+ // For example, consider the following:
+ //
+ // void Foo(const char* buffer, int len) {
+ // auto c = absl::MakeCordFromExternal(absl::string_view(buffer, len),
+ // [](absl::string_view) {});
+ //
+ // // BUG: If Bar() copies its cord for any reason, including keeping a
+ // // substring of it, the lifetime of buffer might be extended beyond
+ // // when Foo() returns.
+ // Bar(c);
+ // }
+ template <typename Releaser>
+ friend Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser);
+
+ // --------------------------------------------------------------------
+ // Mutations
+
+ void Clear();
+
+ void Append(const Cord& src);
+ void Append(Cord&& src);
+ void Append(absl::string_view src);
+ template <typename T, EnableIfString<T> = 0>
+ void Append(T&& src);
+
+ void Prepend(const Cord& src);
+ void Prepend(absl::string_view src);
+ template <typename T, EnableIfString<T> = 0>
+ void Prepend(T&& src);
+
+ void RemovePrefix(size_t n);
+ void RemoveSuffix(size_t n);
+
+ // Returns a new cord representing the subrange [pos, pos + new_size) of
+ // *this. If pos >= size(), the result is empty(). If
+ // (pos + new_size) >= size(), the result is the subrange [pos, size()).
+ Cord Subcord(size_t pos, size_t new_size) const;
+
+ friend void swap(Cord& x, Cord& y) noexcept;
+
+ // --------------------------------------------------------------------
+ // Accessors
+
+ size_t size() const;
+ bool empty() const;
+
+ // Returns the approximate number of bytes pinned by this Cord. Note that
+ // Cords that share memory could each be "charged" independently for the same
+ // shared memory.
+ size_t EstimatedMemoryUsage() const;
+
+ // --------------------------------------------------------------------
+ // Comparators
+
+ // Compares 'this' Cord with rhs. This function and its relatives
+ // treat Cords as sequences of unsigned bytes. The comparison is a
+ // straightforward lexicographic comparison. Return value:
+ // -1 'this' Cord is smaller
+ // 0 two Cords are equal
+ // 1 'this' Cord is larger
+ int Compare(absl::string_view rhs) const;
+ int Compare(const Cord& rhs) const;
+
+ // Does 'this' cord start/end with rhs
+ bool StartsWith(const Cord& rhs) const;
+ bool StartsWith(absl::string_view rhs) const;
+ bool EndsWith(absl::string_view rhs) const;
+ bool EndsWith(const Cord& rhs) const;
+
+ // --------------------------------------------------------------------
+ // Conversion to other types
+
+ explicit operator std::string() const;
+
+ // Copies the contents from `src` to `*dst`.
+ //
+ // This function optimizes the case of reusing the destination std::string since it
+ // can reuse previously allocated capacity. However, this function does not
+ // guarantee that pointers previously returned by `dst->data()` remain valid
+ // even if `*dst` had enough capacity to hold `src`. If `*dst` is a new
+ // object, prefer to simply use the conversion operator to `std::string`.
+ friend void CopyCordToString(const Cord& src, std::string* dst);
+
+ // --------------------------------------------------------------------
+ // Iteration
+
+ class CharIterator;
+
+ // Type for iterating over the chunks of a `Cord`. See comments for
+ // `Cord::chunk_begin()`, `Cord::chunk_end()` and `Cord::Chunks()` below for
+ // preferred usage.
+ //
+ // Additional notes:
+ // * The `string_view` returned by dereferencing a valid, non-`end()`
+ // iterator is guaranteed to be non-empty.
+ // * A `ChunkIterator` object is invalidated after any non-const
+ // operation on the `Cord` object over which it iterates.
+ // * Two `ChunkIterator` objects can be equality compared if and only if
+ // they remain valid and iterate over the same `Cord`.
+ // * This is a proxy iterator. This means the `string_view` returned by the
+ // iterator does not live inside the Cord, and its lifetime is limited to
+ // the lifetime of the iterator itself. To help prevent issues,
+ // `ChunkIterator::reference` is not a true reference type and is
+ // equivalent to `value_type`.
+ // * The iterator keeps state that can grow for `Cord`s that contain many
+ // nodes and are imbalanced due to sharing. Prefer to pass this type by
+ // const reference instead of by value.
+ class ChunkIterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = absl::string_view;
+ using difference_type = ptrdiff_t;
+ using pointer = const value_type*;
+ using reference = value_type;
+
+ ChunkIterator() = default;
+
+ ChunkIterator& operator++();
+ ChunkIterator operator++(int);
+ bool operator==(const ChunkIterator& other) const;
+ bool operator!=(const ChunkIterator& other) const;
+ reference operator*() const;
+ pointer operator->() const;
+
+ friend class Cord;
+ friend class CharIterator;
+
+ private:
+ // Constructs a `begin()` iterator from `cord`.
+ explicit ChunkIterator(const Cord* cord);
+
+ // Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than
+ // `current_chunk_.size()`.
+ void RemoveChunkPrefix(size_t n);
+ Cord AdvanceAndReadBytes(size_t n);
+ void AdvanceBytes(size_t n);
+ // Iterates `n` bytes, where `n` is expected to be greater than or equal to
+ // `current_chunk_.size()`.
+ void AdvanceBytesSlowPath(size_t n);
+
+ // A view into bytes of the current `CordRep`. It may only be a view to a
+ // suffix of bytes if this is being used by `CharIterator`.
+ absl::string_view current_chunk_;
+ // 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;
+ // The number of bytes left in the `Cord` over which we are iterating.
+ size_t bytes_remaining_ = 0;
+ absl::InlinedVector<absl::cord_internal::CordRep*, 4>
+ stack_of_right_children_;
+ };
+
+ // Returns an iterator to the first chunk of the `Cord`.
+ //
+ // This is useful for getting a `ChunkIterator` outside the context of a
+ // range-based for-loop (in which case see `Cord::Chunks()` below).
+ //
+ // Example:
+ //
+ // absl::Cord::ChunkIterator FindAsChunk(const absl::Cord& c,
+ // absl::string_view s) {
+ // return std::find(c.chunk_begin(), c.chunk_end(), s);
+ // }
+ ChunkIterator chunk_begin() const;
+ // Returns an iterator one increment past the last chunk of the `Cord`.
+ ChunkIterator chunk_end() const;
+
+ // Convenience wrapper over `Cord::chunk_begin()` and `Cord::chunk_end()` to
+ // enable range-based for-loop iteration over `Cord` chunks.
+ //
+ // Prefer to use `Cord::Chunks()` below instead of constructing this directly.
+ class ChunkRange {
+ public:
+ explicit ChunkRange(const Cord* cord) : cord_(cord) {}
+
+ ChunkIterator begin() const;
+ ChunkIterator end() const;
+
+ private:
+ const Cord* cord_;
+ };
+
+ // Returns a range for iterating over the chunks of a `Cord` with a
+ // range-based for-loop.
+ //
+ // Example:
+ //
+ // void ProcessChunks(const Cord& cord) {
+ // for (absl::string_view chunk : cord.Chunks()) { ... }
+ // }
+ //
+ // Note that the ordinary caveats of temporary lifetime extension apply:
+ //
+ // void Process() {
+ // for (absl::string_view chunk : CordFactory().Chunks()) {
+ // // The temporary Cord returned by CordFactory has been destroyed!
+ // }
+ // }
+ ChunkRange Chunks() const;
+
+ // Type for iterating over the characters of a `Cord`. See comments for
+ // `Cord::char_begin()`, `Cord::char_end()` and `Cord::Chars()` below for
+ // preferred usage.
+ //
+ // Additional notes:
+ // * A `CharIterator` object is invalidated after any non-const
+ // operation on the `Cord` object over which it iterates.
+ // * Two `CharIterator` objects can be equality compared if and only if
+ // they remain valid and iterate over the same `Cord`.
+ // * The iterator keeps state that can grow for `Cord`s that contain many
+ // nodes and are imbalanced due to sharing. Prefer to pass this type by
+ // const reference instead of by value.
+ // * This type cannot be a forward iterator because a `Cord` can reuse
+ // sections of memory. This violates the requirement that if dereferencing
+ // two iterators returns the same object, the iterators must compare
+ // equal.
+ class CharIterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = char;
+ using difference_type = ptrdiff_t;
+ using pointer = const char*;
+ using reference = const char&;
+
+ CharIterator() = default;
+
+ CharIterator& operator++();
+ CharIterator operator++(int);
+ bool operator==(const CharIterator& other) const;
+ bool operator!=(const CharIterator& other) const;
+ reference operator*() const;
+ pointer operator->() const;
+
+ friend Cord;
+
+ private:
+ explicit CharIterator(const Cord* cord) : chunk_iterator_(cord) {}
+
+ ChunkIterator chunk_iterator_;
+ };
+
+ // Advances `*it` by `n_bytes` and returns the bytes passed as a `Cord`.
+ //
+ // `n_bytes` must be less than or equal to the number of bytes remaining for
+ // iteration. Otherwise the behavior is undefined. It is valid to pass
+ // `char_end()` and 0.
+ static Cord AdvanceAndRead(CharIterator* it, size_t n_bytes);
+
+ // Advances `*it` by `n_bytes`.
+ //
+ // `n_bytes` must be less than or equal to the number of bytes remaining for
+ // iteration. Otherwise the behavior is undefined. It is valid to pass
+ // `char_end()` and 0.
+ static void Advance(CharIterator* it, size_t n_bytes);
+
+ // Returns the longest contiguous view starting at the iterator's position.
+ //
+ // `it` must be dereferenceable.
+ static absl::string_view ChunkRemaining(const CharIterator& it);
+
+ // Returns an iterator to the first character of the `Cord`.
+ CharIterator char_begin() const;
+ // Returns an iterator to one past the last character of the `Cord`.
+ CharIterator char_end() const;
+
+ // Convenience wrapper over `Cord::char_begin()` and `Cord::char_end()` to
+ // enable range-based for-loop iterator over the characters of a `Cord`.
+ //
+ // Prefer to use `Cord::Chars()` below instead of constructing this directly.
+ class CharRange {
+ public:
+ explicit CharRange(const Cord* cord) : cord_(cord) {}
+
+ CharIterator begin() const;
+ CharIterator end() const;
+
+ private:
+ const Cord* cord_;
+ };
+
+ // Returns a range for iterating over the characters of a `Cord` with a
+ // range-based for-loop.
+ //
+ // Example:
+ //
+ // void ProcessCord(const Cord& cord) {
+ // for (char c : cord.Chars()) { ... }
+ // }
+ //
+ // Note that the ordinary caveats of temporary lifetime extension apply:
+ //
+ // void Process() {
+ // for (char c : CordFactory().Chars()) {
+ // // The temporary Cord returned by CordFactory has been destroyed!
+ // }
+ // }
+ CharRange Chars() const;
+
+ // --------------------------------------------------------------------
+ // Miscellaneous
+
+ // Get the "i"th character of 'this' and return it.
+ // NOTE: This routine is reasonably efficient. It is roughly
+ // logarithmic in the number of nodes that make up the cord. Still,
+ // if you need to iterate over the contents of a cord, you should
+ // use a CharIterator/CordIterator rather than call operator[] or Get()
+ // repeatedly in a loop.
+ //
+ // REQUIRES: 0 <= i < size()
+ char operator[](size_t i) const;
+
+ // 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();
+
+ private:
+ friend class CordTestPeer;
+ template <typename H>
+ friend H absl::hash_internal::HashFragmentedCord(H, const Cord&);
+ friend bool operator==(const Cord& lhs, const Cord& rhs);
+ friend bool operator==(const Cord& lhs, absl::string_view rhs);
+
+ // Call the provided function once for each cord chunk, in order. Unlike
+ // Chunks(), this API will not allocate memory.
+ void ForEachChunk(absl::FunctionRef<void(absl::string_view)>) const;
+
+ // Allocates new contiguous storage for the contents of the cord. This is
+ // called by Flatten() when the cord was not already flat.
+ absl::string_view FlattenSlowPath();
+
+ // Actual cord contents are hidden inside the following simple
+ // class so that we can isolate the bulk of cord.cc from changes
+ // to the representation.
+ //
+ // InlineRep holds either either a tree pointer, or an array of kMaxInline
+ // bytes.
+ class InlineRep {
+ public:
+ static const unsigned char kMaxInline = 15;
+ static_assert(kMaxInline >= sizeof(absl::cord_internal::CordRep*), "");
+ // Tag byte & kMaxInline means we are storing a pointer.
+ static const unsigned char kTreeFlag = 1 << 4;
+ // Tag byte & kProfiledFlag means we are profiling the Cord.
+ static const unsigned char kProfiledFlag = 1 << 5;
+
+ constexpr InlineRep() : data_{} {}
+ InlineRep(const InlineRep& src);
+ InlineRep(InlineRep&& src);
+ InlineRep& operator=(const InlineRep& src);
+ InlineRep& operator=(InlineRep&& src) noexcept;
+
+ void Swap(InlineRep* rhs);
+ bool empty() const;
+ size_t size() const;
+ const char* data() const; // Returns nullptr if holding pointer
+ void set_data(const char* data, size_t n,
+ bool nullify_tail); // Discards pointer, if any
+ char* set_data(size_t n); // Write data to the result
+ // Returns nullptr if holding bytes
+ absl::cord_internal::CordRep* tree() const;
+ // Discards old pointer, if any
+ void set_tree(absl::cord_internal::CordRep* rep);
+ // Replaces a tree with a new root. This is faster than set_tree, but it
+ // should only be used when it's clear that the old rep was a tree.
+ void replace_tree(absl::cord_internal::CordRep* rep);
+ // Returns non-null iff was holding a pointer
+ absl::cord_internal::CordRep* clear();
+ // Convert to pointer if necessary
+ absl::cord_internal::CordRep* force_tree(size_t extra_hint);
+ void reduce_size(size_t n); // REQUIRES: holding data
+ void remove_prefix(size_t n); // REQUIRES: holding data
+ void AppendArray(const char* src_data, size_t src_size);
+ absl::string_view FindFlatStartPiece() const;
+ void AppendTree(absl::cord_internal::CordRep* tree);
+ void PrependTree(absl::cord_internal::CordRep* tree);
+ void GetAppendRegion(char** region, size_t* size, size_t max_length);
+ void GetAppendRegion(char** region, size_t* size);
+ bool IsSame(const InlineRep& other) const {
+ return memcmp(data_, other.data_, sizeof(data_)) == 0;
+ }
+ int BitwiseCompare(const InlineRep& other) const {
+ uint64_t x, y;
+ // Use memcpy to avoid anti-aliasing issues.
+ memcpy(&x, data_, sizeof(x));
+ memcpy(&y, other.data_, sizeof(y));
+ if (x == y) {
+ memcpy(&x, data_ + 8, sizeof(x));
+ memcpy(&y, other.data_ + 8, sizeof(y));
+ if (x == y) return 0;
+ }
+ return absl::big_endian::FromHost64(x) < absl::big_endian::FromHost64(y)
+ ? -1
+ : 1;
+ }
+ void CopyTo(std::string* dst) const {
+ // memcpy is much faster when operating on a known size. On most supported
+ // platforms, the small std::string optimization is large enough that resizing
+ // to 15 bytes does not cause a memory allocation.
+ absl::strings_internal::STLStringResizeUninitialized(dst,
+ sizeof(data_) - 1);
+ memcpy(&(*dst)[0], data_, sizeof(data_) - 1);
+ // erase is faster than resize because the logic for memory allocation is
+ // not needed.
+ dst->erase(data_[kMaxInline]);
+ }
+
+ // Copies the inline contents into `dst`. Assumes the cord is not empty.
+ void CopyToArray(char* dst) const;
+
+ bool is_tree() const { return data_[kMaxInline] > kMaxInline; }
+
+ private:
+ friend class Cord;
+
+ void AssignSlow(const InlineRep& src);
+ // Unrefs the tree, stops profiling, and zeroes the contents
+ void ClearSlow();
+
+ // If the data has length <= kMaxInline, we store it in data_[0..len-1],
+ // and store the length in data_[kMaxInline]. Else we store it in a tree
+ // and store a pointer to that tree in data_[0..sizeof(CordRep*)-1].
+ alignas(absl::cord_internal::CordRep*) char data_[kMaxInline + 1];
+ };
+ InlineRep contents_;
+
+ // Helper for MemoryUsage()
+ static size_t MemoryUsageAux(const absl::cord_internal::CordRep* rep);
+
+ // Helper for GetFlat()
+ static bool GetFlatAux(absl::cord_internal::CordRep* rep,
+ absl::string_view* fragment);
+
+ // Helper for ForEachChunk()
+ static void ForEachChunkAux(
+ 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;
+ 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,
+ size_t size_to_compare) const;
+ bool EqualsImpl(absl::string_view rhs, size_t size_to_compare) const;
+ bool EqualsImpl(const Cord& rhs, size_t size_to_compare) const;
+ int CompareImpl(const Cord& rhs) const;
+
+ template <typename ResultType, typename RHS>
+ friend ResultType GenericCompare(const Cord& lhs, const RHS& rhs,
+ size_t size_to_compare);
+ static absl::string_view GetFirstChunk(const Cord& c);
+ static absl::string_view GetFirstChunk(absl::string_view sv);
+
+ // 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() &&;
+
+ // Helper for Append()
+ template <typename C>
+ void AppendImpl(C&& src);
+};
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// allow a Cord to be logged
+extern std::ostream& operator<<(std::ostream& out, const Cord& cord);
+
+// ------------------------------------------------------------------
+// Internal details follow. Clients should ignore.
+
+namespace cord_internal {
+
+// Fast implementation of memmove for up to 15 bytes. This implementation is
+// safe for overlapping regions. If nullify_tail is true, the destination is
+// padded with '\0' up to 16 bytes.
+inline void SmallMemmove(char* dst, const char* src, size_t n,
+ bool nullify_tail = false) {
+ if (n >= 8) {
+ assert(n <= 16);
+ uint64_t buf1;
+ uint64_t buf2;
+ memcpy(&buf1, src, 8);
+ memcpy(&buf2, src + n - 8, 8);
+ if (nullify_tail) {
+ memset(dst + 8, 0, 8);
+ }
+ memcpy(dst, &buf1, 8);
+ memcpy(dst + n - 8, &buf2, 8);
+ } else if (n >= 4) {
+ uint32_t buf1;
+ uint32_t buf2;
+ memcpy(&buf1, src, 4);
+ memcpy(&buf2, src + n - 4, 4);
+ if (nullify_tail) {
+ memset(dst + 4, 0, 4);
+ memset(dst + 8, 0, 8);
+ }
+ memcpy(dst, &buf1, 4);
+ memcpy(dst + n - 4, &buf2, 4);
+ } else {
+ if (n != 0) {
+ dst[0] = src[0];
+ dst[n / 2] = src[n / 2];
+ dst[n - 1] = src[n - 1];
+ }
+ if (nullify_tail) {
+ memset(dst + 8, 0, 8);
+ memset(dst + n, 0, 8);
+ }
+ }
+}
+
+struct ExternalRepReleaserPair {
+ CordRep* rep;
+ void* releaser_address;
+};
+
+// Allocates a new external `CordRep` and returns a pointer to it and a pointer
+// to `releaser_size` bytes where the desired releaser can be constructed.
+// Expects `data` to be non-empty.
+ExternalRepReleaserPair NewExternalWithUninitializedReleaser(
+ absl::string_view data, ExternalReleaserInvoker invoker,
+ size_t releaser_size);
+
+// Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer
+// to it, or `nullptr` if `data` was empty.
+template <typename Releaser>
+// NOLINTNEXTLINE - suppress clang-tidy raw pointer return.
+CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) {
+ static_assert(
+#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ alignof(Releaser) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__,
+#else
+ alignof(Releaser) <= alignof(max_align_t),
+#endif
+ "Releasers with alignment requirement greater than what is returned by "
+ "default `::operator new()` are not supported.");
+
+ using ReleaserType = absl::decay_t<Releaser>;
+ if (data.empty()) {
+ // Never create empty external nodes.
+ ::absl::base_internal::Invoke(
+ ReleaserType(std::forward<Releaser>(releaser)), data);
+ return nullptr;
+ }
+
+ auto releaser_invoker = [](void* type_erased_releaser, absl::string_view d) {
+ auto* my_releaser = static_cast<ReleaserType*>(type_erased_releaser);
+ ::absl::base_internal::Invoke(std::move(*my_releaser), d);
+ my_releaser->~ReleaserType();
+ return sizeof(Releaser);
+ };
+
+ ExternalRepReleaserPair external = NewExternalWithUninitializedReleaser(
+ data, releaser_invoker, sizeof(releaser));
+ ::new (external.releaser_address)
+ ReleaserType(std::forward<Releaser>(releaser));
+ return external.rep;
+}
+
+// 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,
+ void (&releaser)(absl::string_view)) {
+ return NewExternalRep(data, &releaser);
+}
+
+} // namespace cord_internal
+
+template <typename Releaser>
+Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) {
+ Cord cord;
+ cord.contents_.set_tree(::absl::cord_internal::NewExternalRep(
+ data, std::forward<Releaser>(releaser)));
+ return cord;
+}
+
+inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) {
+ cord_internal::SmallMemmove(data_, src.data_, sizeof(data_));
+}
+
+inline Cord::InlineRep::InlineRep(Cord::InlineRep&& src) {
+ memcpy(data_, src.data_, sizeof(data_));
+ memset(src.data_, 0, sizeof(data_));
+}
+
+inline Cord::InlineRep& Cord::InlineRep::operator=(const Cord::InlineRep& src) {
+ if (this == &src) {
+ return *this;
+ }
+ if (!is_tree() && !src.is_tree()) {
+ cord_internal::SmallMemmove(data_, src.data_, sizeof(data_));
+ return *this;
+ }
+ AssignSlow(src);
+ return *this;
+}
+
+inline Cord::InlineRep& Cord::InlineRep::operator=(
+ Cord::InlineRep&& src) noexcept {
+ if (is_tree()) {
+ ClearSlow();
+ }
+ memcpy(data_, src.data_, sizeof(data_));
+ memset(src.data_, 0, sizeof(data_));
+ return *this;
+}
+
+inline void Cord::InlineRep::Swap(Cord::InlineRep* rhs) {
+ if (rhs == this) {
+ return;
+ }
+
+ Cord::InlineRep tmp;
+ cord_internal::SmallMemmove(tmp.data_, data_, sizeof(data_));
+ cord_internal::SmallMemmove(data_, rhs->data_, sizeof(data_));
+ cord_internal::SmallMemmove(rhs->data_, tmp.data_, sizeof(data_));
+}
+
+inline const char* Cord::InlineRep::data() const {
+ return is_tree() ? nullptr : data_;
+}
+
+inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const {
+ if (is_tree()) {
+ absl::cord_internal::CordRep* rep;
+ memcpy(&rep, data_, sizeof(rep));
+ return rep;
+ } else {
+ return nullptr;
+ }
+}
+
+inline bool Cord::InlineRep::empty() const { return data_[kMaxInline] == 0; }
+
+inline size_t Cord::InlineRep::size() const {
+ const char tag = data_[kMaxInline];
+ if (tag <= kMaxInline) return tag;
+ return static_cast<size_t>(tree()->length);
+}
+
+inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) {
+ if (rep == nullptr) {
+ memset(data_, 0, sizeof(data_));
+ } else {
+ bool was_tree = is_tree();
+ memcpy(data_, &rep, sizeof(rep));
+ memset(data_ + sizeof(rep), 0, sizeof(data_) - sizeof(rep) - 1);
+ if (!was_tree) {
+ data_[kMaxInline] = kTreeFlag;
+ }
+ }
+}
+
+inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) {
+ ABSL_ASSERT(is_tree());
+ if (ABSL_PREDICT_FALSE(rep == nullptr)) {
+ set_tree(rep);
+ return;
+ }
+ memcpy(data_, &rep, sizeof(rep));
+ memset(data_ + sizeof(rep), 0, sizeof(data_) - sizeof(rep) - 1);
+}
+
+inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
+ const char tag = data_[kMaxInline];
+ absl::cord_internal::CordRep* result = nullptr;
+ if (tag > kMaxInline) {
+ memcpy(&result, data_, sizeof(result));
+ }
+ memset(data_, 0, sizeof(data_)); // Clear the cord
+ return result;
+}
+
+inline void Cord::InlineRep::CopyToArray(char* dst) const {
+ assert(!is_tree());
+ size_t n = data_[kMaxInline];
+ assert(n != 0);
+ cord_internal::SmallMemmove(dst, data_, n);
+}
+
+constexpr inline Cord::Cord() noexcept {}
+
+inline Cord& Cord::operator=(const Cord& x) {
+ contents_ = x.contents_;
+ return *this;
+}
+
+inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {}
+
+inline Cord& Cord::operator=(Cord&& x) noexcept {
+ contents_ = std::move(x.contents_);
+ return *this;
+}
+
+template <typename T, Cord::EnableIfString<T>>
+inline Cord& Cord::operator=(T&& src) {
+ *this = absl::string_view(src);
+ return *this;
+}
+
+inline size_t Cord::size() const {
+ // Length is 1st field in str.rep_
+ return contents_.size();
+}
+
+inline bool Cord::empty() const { return contents_.empty(); }
+
+inline size_t Cord::EstimatedMemoryUsage() const {
+ size_t result = sizeof(Cord);
+ if (const absl::cord_internal::CordRep* rep = contents_.tree()) {
+ result += MemoryUsageAux(rep);
+ }
+ return result;
+}
+
+inline absl::string_view Cord::Flatten() {
+ absl::cord_internal::CordRep* rep = contents_.tree();
+ if (rep == nullptr) {
+ return absl::string_view(contents_.data(), contents_.size());
+ } else {
+ absl::string_view already_flat_contents;
+ if (GetFlatAux(rep, &already_flat_contents)) {
+ return already_flat_contents;
+ }
+ }
+ return FlattenSlowPath();
+}
+
+inline void Cord::Append(absl::string_view src) {
+ contents_.AppendArray(src.data(), src.size());
+}
+
+template <typename T, Cord::EnableIfString<T>>
+inline void Cord::Append(T&& src) {
+ // Note that this function reserves the right to reuse the `string&&`'s
+ // memory and that it will do so in the future.
+ Append(absl::string_view(src));
+}
+
+template <typename T, Cord::EnableIfString<T>>
+inline void Cord::Prepend(T&& src) {
+ // Note that this function reserves the right to reuse the `string&&`'s
+ // memory and that it will do so in the future.
+ Prepend(absl::string_view(src));
+}
+
+inline int Cord::Compare(const Cord& rhs) const {
+ if (!contents_.is_tree() && !rhs.contents_.is_tree()) {
+ return contents_.BitwiseCompare(rhs.contents_);
+ }
+
+ return CompareImpl(rhs);
+}
+
+// Does 'this' cord start/end with rhs
+inline bool Cord::StartsWith(const Cord& rhs) const {
+ if (contents_.IsSame(rhs.contents_)) return true;
+ size_t rhs_size = rhs.size();
+ if (size() < rhs_size) return false;
+ return EqualsImpl(rhs, rhs_size);
+}
+
+inline bool Cord::StartsWith(absl::string_view rhs) const {
+ size_t rhs_size = rhs.size();
+ if (size() < rhs_size) return false;
+ return EqualsImpl(rhs, rhs_size);
+}
+
+inline Cord::ChunkIterator::ChunkIterator(const Cord* cord)
+ : bytes_remaining_(cord->size()) {
+ if (cord->empty()) return;
+ if (cord->contents_.is_tree()) {
+ stack_of_right_children_.push_back(cord->contents_.tree());
+ operator++();
+ } else {
+ current_chunk_ = absl::string_view(cord->contents_.data(), cord->size());
+ }
+}
+
+inline Cord::ChunkIterator Cord::ChunkIterator::operator++(int) {
+ ChunkIterator tmp(*this);
+ operator++();
+ return tmp;
+}
+
+inline bool Cord::ChunkIterator::operator==(const ChunkIterator& other) const {
+ return bytes_remaining_ == other.bytes_remaining_;
+}
+
+inline bool Cord::ChunkIterator::operator!=(const ChunkIterator& other) const {
+ return !(*this == other);
+}
+
+inline Cord::ChunkIterator::reference Cord::ChunkIterator::operator*() const {
+ assert(bytes_remaining_ != 0);
+ return current_chunk_;
+}
+
+inline Cord::ChunkIterator::pointer Cord::ChunkIterator::operator->() const {
+ assert(bytes_remaining_ != 0);
+ return &current_chunk_;
+}
+
+inline void Cord::ChunkIterator::RemoveChunkPrefix(size_t n) {
+ assert(n < current_chunk_.size());
+ current_chunk_.remove_prefix(n);
+ bytes_remaining_ -= n;
+}
+
+inline void Cord::ChunkIterator::AdvanceBytes(size_t n) {
+ if (ABSL_PREDICT_TRUE(n < current_chunk_.size())) {
+ RemoveChunkPrefix(n);
+ } else if (n != 0) {
+ AdvanceBytesSlowPath(n);
+ }
+}
+
+inline Cord::ChunkIterator Cord::chunk_begin() const {
+ return ChunkIterator(this);
+}
+
+inline Cord::ChunkIterator Cord::chunk_end() const { return ChunkIterator(); }
+
+inline Cord::ChunkIterator Cord::ChunkRange::begin() const {
+ return cord_->chunk_begin();
+}
+
+inline Cord::ChunkIterator Cord::ChunkRange::end() const {
+ return cord_->chunk_end();
+}
+
+inline Cord::ChunkRange Cord::Chunks() const { return ChunkRange(this); }
+
+inline Cord::CharIterator& Cord::CharIterator::operator++() {
+ if (ABSL_PREDICT_TRUE(chunk_iterator_->size() > 1)) {
+ chunk_iterator_.RemoveChunkPrefix(1);
+ } else {
+ ++chunk_iterator_;
+ }
+ return *this;
+}
+
+inline Cord::CharIterator Cord::CharIterator::operator++(int) {
+ CharIterator tmp(*this);
+ operator++();
+ return tmp;
+}
+
+inline bool Cord::CharIterator::operator==(const CharIterator& other) const {
+ return chunk_iterator_ == other.chunk_iterator_;
+}
+
+inline bool Cord::CharIterator::operator!=(const CharIterator& other) const {
+ return !(*this == other);
+}
+
+inline Cord::CharIterator::reference Cord::CharIterator::operator*() const {
+ return *chunk_iterator_->data();
+}
+
+inline Cord::CharIterator::pointer Cord::CharIterator::operator->() const {
+ return chunk_iterator_->data();
+}
+
+inline Cord Cord::AdvanceAndRead(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) {
+ assert(it != nullptr);
+ it->chunk_iterator_.AdvanceBytes(n_bytes);
+}
+
+inline absl::string_view Cord::ChunkRemaining(const CharIterator& it) {
+ return *it.chunk_iterator_;
+}
+
+inline Cord::CharIterator Cord::char_begin() const {
+ return CharIterator(this);
+}
+
+inline Cord::CharIterator Cord::char_end() const { return CharIterator(); }
+
+inline Cord::CharIterator Cord::CharRange::begin() const {
+ return cord_->char_begin();
+}
+
+inline Cord::CharIterator Cord::CharRange::end() const {
+ return cord_->char_end();
+}
+
+inline Cord::CharRange Cord::Chars() const { return CharRange(this); }
+
+inline void Cord::ForEachChunk(
+ absl::FunctionRef<void(absl::string_view)> callback) const {
+ absl::cord_internal::CordRep* rep = contents_.tree();
+ if (rep == nullptr) {
+ callback(absl::string_view(contents_.data(), contents_.size()));
+ } else {
+ return ForEachChunkAux(rep, callback);
+ }
+}
+
+// Nonmember Cord-to-Cord relational operarators.
+inline bool operator==(const Cord& lhs, const Cord& rhs) {
+ if (lhs.contents_.IsSame(rhs.contents_)) return true;
+ size_t rhs_size = rhs.size();
+ if (lhs.size() != rhs_size) return false;
+ return lhs.EqualsImpl(rhs, rhs_size);
+}
+
+inline bool operator!=(const Cord& x, const Cord& y) { return !(x == y); }
+inline bool operator<(const Cord& x, const Cord& y) {
+ return x.Compare(y) < 0;
+}
+inline bool operator>(const Cord& x, const Cord& y) {
+ return x.Compare(y) > 0;
+}
+inline bool operator<=(const Cord& x, const Cord& y) {
+ return x.Compare(y) <= 0;
+}
+inline bool operator>=(const Cord& x, const Cord& y) {
+ return x.Compare(y) >= 0;
+}
+
+// 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*.
+inline bool operator==(const Cord& lhs, absl::string_view rhs) {
+ size_t lhs_size = lhs.size();
+ size_t rhs_size = rhs.size();
+ if (lhs_size != rhs_size) return false;
+ return lhs.EqualsImpl(rhs, rhs_size);
+}
+
+inline bool operator==(absl::string_view x, const Cord& y) { return y == x; }
+inline bool operator!=(const Cord& x, absl::string_view y) { return !(x == y); }
+inline bool operator!=(absl::string_view x, const Cord& y) { return !(x == y); }
+inline bool operator<(const Cord& x, absl::string_view y) {
+ return x.Compare(y) < 0;
+}
+inline bool operator<(absl::string_view x, const Cord& y) {
+ return y.Compare(x) > 0;
+}
+inline bool operator>(const Cord& x, absl::string_view y) { return y < x; }
+inline bool operator>(absl::string_view x, const Cord& y) { return y < x; }
+inline bool operator<=(const Cord& x, absl::string_view y) { return !(y < x); }
+inline bool operator<=(absl::string_view x, const Cord& y) { return !(y < x); }
+inline bool operator>=(const Cord& x, absl::string_view y) { return !(x < y); }
+inline bool operator>=(absl::string_view x, const Cord& y) { return !(x < y); }
+
+// Overload of swap for Cord. The use of non-const references is
+// required. :(
+inline void swap(Cord& x, Cord& y) noexcept { y.contents_.Swap(&x.contents_); }
+
+// Some internals exposed to test code.
+namespace strings_internal {
+class CordTestAccess {
+ public:
+ static size_t FlatOverhead();
+ static size_t MaxFlatLength();
+ static size_t SizeofCordRepConcat();
+ static size_t SizeofCordRepExternal();
+ static size_t SizeofCordRepSubstring();
+ static size_t FlatTagToLength(uint8_t tag);
+ static uint8_t LengthToTag(size_t s);
+};
+} // namespace strings_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STRINGS_CORD_H_
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc
new file mode 100644
index 00000000..434f3a24
--- /dev/null
+++ b/absl/strings/cord_test.cc
@@ -0,0 +1,1526 @@
+#include "absl/strings/cord.h"
+
+#include <algorithm>
+#include <climits>
+#include <cstdio>
+#include <iterator>
+#include <map>
+#include <numeric>
+#include <random>
+#include <sstream>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/base/casts.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/endian.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/container/fixed_array.h"
+#include "absl/strings/cord_test_helpers.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+
+typedef std::mt19937_64 RandomEngine;
+
+static std::string RandomLowercaseString(RandomEngine* rng);
+static std::string RandomLowercaseString(RandomEngine* rng, size_t length);
+
+static int GetUniformRandomUpTo(RandomEngine* rng, int upper_bound) {
+ if (upper_bound > 0) {
+ std::uniform_int_distribution<int> uniform(0, upper_bound - 1);
+ return uniform(*rng);
+ } else {
+ return 0;
+ }
+}
+
+static size_t GetUniformRandomUpTo(RandomEngine* rng, size_t upper_bound) {
+ if (upper_bound > 0) {
+ std::uniform_int_distribution<size_t> uniform(0, upper_bound - 1);
+ return uniform(*rng);
+ } else {
+ return 0;
+ }
+}
+
+static int32_t GenerateSkewedRandom(RandomEngine* rng, int max_log) {
+ const uint32_t base = (*rng)() % (max_log + 1);
+ const uint32_t mask = ((base < 32) ? (1u << base) : 0u) - 1u;
+ return (*rng)() & mask;
+}
+
+static std::string RandomLowercaseString(RandomEngine* rng) {
+ int length;
+ std::bernoulli_distribution one_in_1k(0.001);
+ std::bernoulli_distribution one_in_10k(0.0001);
+ // With low probability, make a large fragment
+ if (one_in_10k(*rng)) {
+ length = GetUniformRandomUpTo(rng, 1048576);
+ } else if (one_in_1k(*rng)) {
+ length = GetUniformRandomUpTo(rng, 10000);
+ } else {
+ length = GenerateSkewedRandom(rng, 10);
+ }
+ return RandomLowercaseString(rng, length);
+}
+
+static std::string RandomLowercaseString(RandomEngine* rng, size_t length) {
+ std::string result(length, '\0');
+ std::uniform_int_distribution<int> chars('a', 'z');
+ std::generate(result.begin(), result.end(), [&]() {
+ return static_cast<char>(chars(*rng));
+ });
+ return result;
+}
+
+static void DoNothing(absl::string_view /* data */, void* /* arg */) {}
+
+static void DeleteExternalString(absl::string_view data, void* arg) {
+ std::string* s = reinterpret_cast<std::string*>(arg);
+ EXPECT_EQ(data, *s);
+ delete s;
+}
+
+// Add "s" to *dst via `MakeCordFromExternal`
+static void AddExternalMemory(absl::string_view s, absl::Cord* dst) {
+ std::string* str = new std::string(s.data(), s.size());
+ dst->Append(absl::MakeCordFromExternal(*str, [str](absl::string_view data) {
+ DeleteExternalString(data, str);
+ }));
+}
+
+static void DumpGrowth() {
+ absl::Cord str;
+ for (int i = 0; i < 1000; i++) {
+ char c = 'a' + i % 26;
+ str.Append(absl::string_view(&c, 1));
+ }
+}
+
+// Make a Cord with some number of fragments. Return the size (in bytes)
+// of the smallest fragment.
+static size_t AppendWithFragments(const std::string& s, RandomEngine* rng,
+ absl::Cord* cord) {
+ size_t j = 0;
+ const size_t max_size = s.size() / 5; // Make approx. 10 fragments
+ size_t min_size = max_size; // size of smallest fragment
+ while (j < s.size()) {
+ size_t N = 1 + GetUniformRandomUpTo(rng, max_size);
+ if (N > (s.size() - j)) {
+ N = s.size() - j;
+ }
+ if (N < min_size) {
+ min_size = N;
+ }
+
+ std::bernoulli_distribution coin_flip(0.5);
+ if (coin_flip(*rng)) {
+ // Grow by adding an external-memory.
+ AddExternalMemory(absl::string_view(s.data() + j, N), cord);
+ } else {
+ cord->Append(absl::string_view(s.data() + j, N));
+ }
+ j += N;
+ }
+ return min_size;
+}
+
+// Add an external memory that contains the specified std::string to cord
+static void AddNewStringBlock(const std::string& str, absl::Cord* dst) {
+ char* data = new char[str.size()];
+ memcpy(data, str.data(), str.size());
+ dst->Append(absl::MakeCordFromExternal(
+ absl::string_view(data, str.size()),
+ [](absl::string_view s) { delete[] s.data(); }));
+}
+
+// Make a Cord out of many different types of nodes.
+static absl::Cord MakeComposite() {
+ absl::Cord cord;
+ cord.Append("the");
+ AddExternalMemory(" quick brown", &cord);
+ AddExternalMemory(" fox jumped", &cord);
+
+ absl::Cord full(" over");
+ AddExternalMemory(" the lazy", &full);
+ AddNewStringBlock(" dog slept the whole day away", &full);
+ absl::Cord substring = full.Subcord(0, 18);
+
+ // Make substring long enough to defeat the copying fast path in Append.
+ substring.Append(std::string(1000, '.'));
+ cord.Append(substring);
+ cord = cord.Subcord(0, cord.size() - 998); // Remove most of extra junk
+
+ return cord;
+}
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+class CordTestPeer {
+ public:
+ static void ForEachChunk(
+ const Cord& c, absl::FunctionRef<void(absl::string_view)> callback) {
+ c.ForEachChunk(callback);
+ }
+};
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+TEST(Cord, AllFlatSizes) {
+ using absl::strings_internal::CordTestAccess;
+
+ for (size_t s = 0; s < CordTestAccess::MaxFlatLength(); s++) {
+ // Make a std::string of length s.
+ std::string src;
+ while (src.size() < s) {
+ src.push_back('a' + (src.size() % 26));
+ }
+
+ absl::Cord dst(src);
+ EXPECT_EQ(std::string(dst), src) << s;
+ }
+}
+
+// We create a Cord at least 128GB in size using the fact that Cords can
+// internally reference-count; thus the Cord is enormous without actually
+// consuming very much memory.
+TEST(GigabyteCord, FromExternal) {
+ const size_t one_gig = 1024U * 1024U * 1024U;
+ size_t max_size = 2 * one_gig;
+ if (sizeof(max_size) > 4) max_size = 128 * one_gig;
+
+ size_t length = 128 * 1024;
+ char* data = new char[length];
+ absl::Cord from = absl::MakeCordFromExternal(
+ absl::string_view(data, length),
+ [](absl::string_view sv) { delete[] sv.data(); });
+
+ // This loop may seem odd due to its combination of exponential doubling of
+ // size and incremental size increases. We do it incrementally to be sure the
+ // Cord will need rebalancing and will exercise code that, in the past, has
+ // caused crashes in production. We grow exponentially so that the code will
+ // execute in a reasonable amount of time.
+ absl::Cord c;
+ ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size());
+ c.Append(from);
+ while (c.size() < max_size) {
+ c.Append(c);
+ c.Append(from);
+ c.Append(from);
+ c.Append(from);
+ c.Append(from);
+ }
+
+ for (int i = 0; i < 1024; ++i) {
+ c.Append(from);
+ }
+ ABSL_RAW_LOG(INFO, "Made a Cord with %zu bytes!", c.size());
+ // Note: on a 32-bit build, this comes out to 2,818,048,000 bytes.
+ // Note: on a 64-bit build, this comes out to 171,932,385,280 bytes.
+}
+
+static absl::Cord MakeExternalCord(int size) {
+ char* buffer = new char[size];
+ memset(buffer, 'x', size);
+ absl::Cord cord;
+ cord.Append(absl::MakeCordFromExternal(
+ absl::string_view(buffer, size),
+ [](absl::string_view s) { delete[] s.data(); }));
+ return cord;
+}
+
+// Extern to fool clang that this is not constant. Needed to suppress
+// a warning of unsafe code we want to test.
+extern bool my_unique_true_boolean;
+bool my_unique_true_boolean = true;
+
+TEST(Cord, Assignment) {
+ absl::Cord x(absl::string_view("hi there"));
+ absl::Cord y(x);
+ ASSERT_EQ(std::string(x), "hi there");
+ ASSERT_EQ(std::string(y), "hi there");
+ ASSERT_TRUE(x == y);
+ ASSERT_TRUE(x <= y);
+ ASSERT_TRUE(y <= x);
+
+ x = absl::string_view("foo");
+ ASSERT_EQ(std::string(x), "foo");
+ ASSERT_EQ(std::string(y), "hi there");
+ ASSERT_TRUE(x < y);
+ ASSERT_TRUE(y > x);
+ ASSERT_TRUE(x != y);
+ ASSERT_TRUE(x <= y);
+ ASSERT_TRUE(y >= x);
+
+ x = "foo";
+ ASSERT_EQ(x, "foo");
+
+ // Test that going from inline rep to tree we don't leak memory.
+ std::vector<std::pair<absl::string_view, absl::string_view>>
+ test_string_pairs = {{"hi there", "foo"},
+ {"loooooong coooooord", "short cord"},
+ {"short cord", "loooooong coooooord"},
+ {"loooooong coooooord1", "loooooong coooooord2"}};
+ for (std::pair<absl::string_view, absl::string_view> test_strings :
+ test_string_pairs) {
+ absl::Cord tmp(test_strings.first);
+ absl::Cord z(std::move(tmp));
+ ASSERT_EQ(std::string(z), test_strings.first);
+ tmp = test_strings.second;
+ z = std::move(tmp);
+ ASSERT_EQ(std::string(z), test_strings.second);
+ }
+ {
+ // Test that self-move assignment doesn't crash/leak.
+ // Do not write such code!
+ absl::Cord my_small_cord("foo");
+ absl::Cord my_big_cord("loooooong coooooord");
+ // Bypass clang's warning on self move-assignment.
+ absl::Cord* my_small_alias =
+ my_unique_true_boolean ? &my_small_cord : &my_big_cord;
+ absl::Cord* my_big_alias =
+ !my_unique_true_boolean ? &my_small_cord : &my_big_cord;
+
+ *my_small_alias = std::move(my_small_cord);
+ *my_big_alias = std::move(my_big_cord);
+ // my_small_cord and my_big_cord are in an unspecified but valid
+ // state, and will be correctly destroyed here.
+ }
+}
+
+TEST(Cord, StartsEndsWith) {
+ absl::Cord x(absl::string_view("abcde"));
+ absl::Cord empty("");
+
+ ASSERT_TRUE(x.StartsWith(absl::Cord("abcde")));
+ ASSERT_TRUE(x.StartsWith(absl::Cord("abc")));
+ ASSERT_TRUE(x.StartsWith(absl::Cord("")));
+ ASSERT_TRUE(empty.StartsWith(absl::Cord("")));
+ ASSERT_TRUE(x.EndsWith(absl::Cord("abcde")));
+ ASSERT_TRUE(x.EndsWith(absl::Cord("cde")));
+ ASSERT_TRUE(x.EndsWith(absl::Cord("")));
+ ASSERT_TRUE(empty.EndsWith(absl::Cord("")));
+
+ ASSERT_TRUE(!x.StartsWith(absl::Cord("xyz")));
+ ASSERT_TRUE(!empty.StartsWith(absl::Cord("xyz")));
+ ASSERT_TRUE(!x.EndsWith(absl::Cord("xyz")));
+ ASSERT_TRUE(!empty.EndsWith(absl::Cord("xyz")));
+
+ ASSERT_TRUE(x.StartsWith("abcde"));
+ ASSERT_TRUE(x.StartsWith("abc"));
+ ASSERT_TRUE(x.StartsWith(""));
+ ASSERT_TRUE(empty.StartsWith(""));
+ ASSERT_TRUE(x.EndsWith("abcde"));
+ ASSERT_TRUE(x.EndsWith("cde"));
+ ASSERT_TRUE(x.EndsWith(""));
+ ASSERT_TRUE(empty.EndsWith(""));
+
+ ASSERT_TRUE(!x.StartsWith("xyz"));
+ ASSERT_TRUE(!empty.StartsWith("xyz"));
+ ASSERT_TRUE(!x.EndsWith("xyz"));
+ ASSERT_TRUE(!empty.EndsWith("xyz"));
+}
+
+TEST(Cord, Subcord) {
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+ const std::string s = RandomLowercaseString(&rng, 1024);
+
+ absl::Cord a;
+ AppendWithFragments(s, &rng, &a);
+ ASSERT_EQ(s.size(), a.size());
+
+ // Check subcords of a, from a variety of interesting points.
+ std::set<size_t> positions;
+ for (int i = 0; i <= 32; ++i) {
+ positions.insert(i);
+ positions.insert(i * 32 - 1);
+ positions.insert(i * 32);
+ positions.insert(i * 32 + 1);
+ positions.insert(a.size() - i);
+ }
+ positions.insert(237);
+ positions.insert(732);
+ for (size_t pos : positions) {
+ if (pos > a.size()) continue;
+ for (size_t end_pos : positions) {
+ if (end_pos < pos || end_pos > a.size()) continue;
+ absl::Cord sa = a.Subcord(pos, end_pos - pos);
+ EXPECT_EQ(absl::string_view(s).substr(pos, end_pos - pos),
+ std::string(sa))
+ << a;
+ }
+ }
+
+ // Do the same thing for an inline cord.
+ const std::string sh = "short";
+ absl::Cord c(sh);
+ for (size_t pos = 0; pos <= sh.size(); ++pos) {
+ for (size_t n = 0; n <= sh.size() - pos; ++n) {
+ absl::Cord sc = c.Subcord(pos, n);
+ EXPECT_EQ(sh.substr(pos, n), std::string(sc)) << c;
+ }
+ }
+
+ // Check subcords of subcords.
+ absl::Cord sa = a.Subcord(0, a.size());
+ std::string ss = s.substr(0, s.size());
+ while (sa.size() > 1) {
+ sa = sa.Subcord(1, sa.size() - 2);
+ ss = ss.substr(1, ss.size() - 2);
+ EXPECT_EQ(ss, std::string(sa)) << a;
+ if (HasFailure()) break; // halt cascade
+ }
+
+ // It is OK to ask for too much.
+ sa = a.Subcord(0, a.size() + 1);
+ EXPECT_EQ(s, std::string(sa));
+
+ // It is OK to ask for something beyond the end.
+ sa = a.Subcord(a.size() + 1, 0);
+ EXPECT_TRUE(sa.empty());
+ sa = a.Subcord(a.size() + 1, 1);
+ EXPECT_TRUE(sa.empty());
+}
+
+TEST(Cord, Swap) {
+ absl::string_view a("Dexter");
+ absl::string_view b("Mandark");
+ absl::Cord x(a);
+ absl::Cord y(b);
+ swap(x, y);
+ ASSERT_EQ(x, absl::Cord(b));
+ ASSERT_EQ(y, absl::Cord(a));
+}
+
+static void VerifyCopyToString(const absl::Cord& cord) {
+ std::string initially_empty;
+ absl::CopyCordToString(cord, &initially_empty);
+ EXPECT_EQ(initially_empty, cord);
+
+ constexpr size_t kInitialLength = 1024;
+ std::string has_initial_contents(kInitialLength, 'x');
+ const char* address_before_copy = has_initial_contents.data();
+ absl::CopyCordToString(cord, &has_initial_contents);
+ EXPECT_EQ(has_initial_contents, cord);
+
+ if (cord.size() <= kInitialLength) {
+ EXPECT_EQ(has_initial_contents.data(), address_before_copy)
+ << "CopyCordToString allocated new std::string storage; "
+ "has_initial_contents = \""
+ << has_initial_contents << "\"";
+ }
+}
+
+TEST(Cord, CopyToString) {
+ VerifyCopyToString(absl::Cord());
+ VerifyCopyToString(absl::Cord("small cord"));
+ VerifyCopyToString(
+ absl::MakeFragmentedCord({"fragmented ", "cord ", "to ", "test ",
+ "copying ", "to ", "a ", "string."}));
+}
+
+static bool IsFlat(const absl::Cord& c) {
+ return c.chunk_begin() == c.chunk_end() || ++c.chunk_begin() == c.chunk_end();
+}
+
+static void VerifyFlatten(absl::Cord c) {
+ std::string old_contents(c);
+ absl::string_view old_flat;
+ bool already_flat_and_non_empty = IsFlat(c) && !c.empty();
+ if (already_flat_and_non_empty) {
+ old_flat = *c.chunk_begin();
+ }
+ absl::string_view new_flat = c.Flatten();
+
+ // Verify that the contents of the flattened Cord are correct.
+ EXPECT_EQ(new_flat, old_contents);
+ EXPECT_EQ(std::string(c), old_contents);
+
+ // If the Cord contained data and was already flat, verify that the data
+ // wasn't copied.
+ if (already_flat_and_non_empty) {
+ EXPECT_EQ(old_flat.data(), new_flat.data())
+ << "Allocated new memory even though the Cord was already flat.";
+ }
+
+ // Verify that the flattened Cord is in fact flat.
+ EXPECT_TRUE(IsFlat(c));
+}
+
+TEST(Cord, Flatten) {
+ VerifyFlatten(absl::Cord());
+ VerifyFlatten(absl::Cord("small cord"));
+ VerifyFlatten(absl::Cord("larger than small buffer optimization"));
+ VerifyFlatten(absl::MakeFragmentedCord({"small ", "fragmented ", "cord"}));
+
+ // Test with a cord that is longer than the largest flat buffer
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+ VerifyFlatten(absl::Cord(RandomLowercaseString(&rng, 8192)));
+}
+
+// Test data
+namespace {
+class TestData {
+ private:
+ std::vector<std::string> data_;
+
+ // Return a std::string of the specified length.
+ static std::string MakeString(int length) {
+ std::string result;
+ char buf[30];
+ snprintf(buf, sizeof(buf), "(%d)", length);
+ while (result.size() < length) {
+ result += buf;
+ }
+ result.resize(length);
+ return result;
+ }
+
+ public:
+ TestData() {
+ // short strings increasing in length by one
+ for (int i = 0; i < 30; i++) {
+ data_.push_back(MakeString(i));
+ }
+
+ // strings around half kMaxFlatLength
+ static const int kMaxFlatLength = 4096 - 9;
+ static const int kHalf = kMaxFlatLength / 2;
+
+ for (int i = -10; i <= +10; i++) {
+ data_.push_back(MakeString(kHalf + i));
+ }
+
+ for (int i = -10; i <= +10; i++) {
+ data_.push_back(MakeString(kMaxFlatLength + i));
+ }
+ }
+
+ size_t size() const { return data_.size(); }
+ const std::string& data(size_t i) const { return data_[i]; }
+};
+} // namespace
+
+TEST(Cord, MultipleLengths) {
+ TestData d;
+ for (size_t i = 0; i < d.size(); i++) {
+ std::string a = d.data(i);
+
+ { // Construct from Cord
+ absl::Cord tmp(a);
+ absl::Cord x(tmp);
+ EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
+ }
+
+ { // Construct from absl::string_view
+ absl::Cord x(a);
+ EXPECT_EQ(a, std::string(x)) << "'" << a << "'";
+ }
+
+ { // Append cord to self
+ absl::Cord self(a);
+ self.Append(self);
+ EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
+ }
+
+ { // Prepend cord to self
+ absl::Cord self(a);
+ self.Prepend(self);
+ EXPECT_EQ(a + a, std::string(self)) << "'" << a << "' + '" << a << "'";
+ }
+
+ // Try to append/prepend others
+ for (size_t j = 0; j < d.size(); j++) {
+ std::string b = d.data(j);
+
+ { // CopyFrom Cord
+ absl::Cord x(a);
+ absl::Cord y(b);
+ x = y;
+ EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
+ }
+
+ { // CopyFrom absl::string_view
+ absl::Cord x(a);
+ x = b;
+ EXPECT_EQ(b, std::string(x)) << "'" << a << "' + '" << b << "'";
+ }
+
+ { // Cord::Append(Cord)
+ absl::Cord x(a);
+ absl::Cord y(b);
+ x.Append(y);
+ EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
+ }
+
+ { // Cord::Append(absl::string_view)
+ absl::Cord x(a);
+ x.Append(b);
+ EXPECT_EQ(a + b, std::string(x)) << "'" << a << "' + '" << b << "'";
+ }
+
+ { // Cord::Prepend(Cord)
+ absl::Cord x(a);
+ absl::Cord y(b);
+ x.Prepend(y);
+ EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
+ }
+
+ { // Cord::Prepend(absl::string_view)
+ absl::Cord x(a);
+ x.Prepend(b);
+ EXPECT_EQ(b + a, std::string(x)) << "'" << b << "' + '" << a << "'";
+ }
+ }
+ }
+}
+
+namespace {
+
+TEST(Cord, RemoveSuffixWithExternalOrSubstring) {
+ absl::Cord cord = absl::MakeCordFromExternal(
+ "foo bar baz", [](absl::string_view s) { DoNothing(s, nullptr); });
+
+ EXPECT_EQ("foo bar baz", std::string(cord));
+
+ // This RemoveSuffix() will wrap the EXTERNAL node in a SUBSTRING node.
+ cord.RemoveSuffix(4);
+ EXPECT_EQ("foo bar", std::string(cord));
+
+ // This RemoveSuffix() will adjust the SUBSTRING node in-place.
+ cord.RemoveSuffix(4);
+ EXPECT_EQ("foo", std::string(cord));
+}
+
+TEST(Cord, RemoveSuffixMakesZeroLengthNode) {
+ absl::Cord c;
+ c.Append(absl::Cord(std::string(100, 'x')));
+ absl::Cord other_ref = c; // Prevent inplace appends
+ c.Append(absl::Cord(std::string(200, 'y')));
+ c.RemoveSuffix(200);
+ EXPECT_EQ(std::string(100, 'x'), std::string(c));
+}
+
+} // namespace
+
+// CordSpliceTest contributed by hendrie.
+namespace {
+
+// Create a cord with an external memory block filled with 'z'
+absl::Cord CordWithZedBlock(size_t size) {
+ char* data = new char[size];
+ if (size > 0) {
+ memset(data, 'z', size);
+ }
+ absl::Cord cord = absl::MakeCordFromExternal(
+ absl::string_view(data, size),
+ [](absl::string_view s) { delete[] s.data(); });
+ return cord;
+}
+
+// Establish that ZedBlock does what we think it does.
+TEST(CordSpliceTest, ZedBlock) {
+ absl::Cord blob = CordWithZedBlock(10);
+ EXPECT_EQ(10, blob.size());
+ std::string s;
+ absl::CopyCordToString(blob, &s);
+ EXPECT_EQ("zzzzzzzzzz", s);
+}
+
+TEST(CordSpliceTest, ZedBlock0) {
+ absl::Cord blob = CordWithZedBlock(0);
+ EXPECT_EQ(0, blob.size());
+ std::string s;
+ absl::CopyCordToString(blob, &s);
+ EXPECT_EQ("", s);
+}
+
+TEST(CordSpliceTest, ZedBlockSuffix1) {
+ absl::Cord blob = CordWithZedBlock(10);
+ EXPECT_EQ(10, blob.size());
+ absl::Cord suffix(blob);
+ suffix.RemovePrefix(9);
+ EXPECT_EQ(1, suffix.size());
+ std::string s;
+ absl::CopyCordToString(suffix, &s);
+ EXPECT_EQ("z", s);
+}
+
+// Remove all of a prefix block
+TEST(CordSpliceTest, ZedBlockSuffix0) {
+ absl::Cord blob = CordWithZedBlock(10);
+ EXPECT_EQ(10, blob.size());
+ absl::Cord suffix(blob);
+ suffix.RemovePrefix(10);
+ EXPECT_EQ(0, suffix.size());
+ std::string s;
+ absl::CopyCordToString(suffix, &s);
+ EXPECT_EQ("", s);
+}
+
+absl::Cord BigCord(size_t len, char v) {
+ std::string s(len, v);
+ return absl::Cord(s);
+}
+
+// Splice block into cord.
+absl::Cord SpliceCord(const absl::Cord& blob, int64_t offset,
+ const absl::Cord& block) {
+ ABSL_RAW_CHECK(offset >= 0, "");
+ ABSL_RAW_CHECK(offset + block.size() <= blob.size(), "");
+ absl::Cord result(blob);
+ result.RemoveSuffix(blob.size() - offset);
+ result.Append(block);
+ absl::Cord suffix(blob);
+ suffix.RemovePrefix(offset + block.size());
+ result.Append(suffix);
+ ABSL_RAW_CHECK(blob.size() == result.size(), "");
+ return result;
+}
+
+// Taking an empty suffix of a block breaks appending.
+TEST(CordSpliceTest, RemoveEntireBlock1) {
+ absl::Cord zero = CordWithZedBlock(10);
+ absl::Cord suffix(zero);
+ suffix.RemovePrefix(10);
+ absl::Cord result;
+ result.Append(suffix);
+}
+
+TEST(CordSpliceTest, RemoveEntireBlock2) {
+ absl::Cord zero = CordWithZedBlock(10);
+ absl::Cord prefix(zero);
+ prefix.RemoveSuffix(10);
+ absl::Cord suffix(zero);
+ suffix.RemovePrefix(10);
+ absl::Cord result(prefix);
+ result.Append(suffix);
+}
+
+TEST(CordSpliceTest, RemoveEntireBlock3) {
+ absl::Cord blob = CordWithZedBlock(10);
+ absl::Cord block = BigCord(10, 'b');
+ blob = SpliceCord(blob, 0, block);
+}
+
+struct CordCompareTestCase {
+ template <typename LHS, typename RHS>
+ CordCompareTestCase(const LHS& lhs, const RHS& rhs)
+ : lhs_cord(lhs), rhs_cord(rhs) {}
+
+ absl::Cord lhs_cord;
+ absl::Cord rhs_cord;
+};
+
+const auto sign = [](int x) { return x == 0 ? 0 : (x > 0 ? 1 : -1); };
+
+void VerifyComparison(const CordCompareTestCase& test_case) {
+ std::string lhs_string(test_case.lhs_cord);
+ std::string rhs_string(test_case.rhs_cord);
+ int expected = sign(lhs_string.compare(rhs_string));
+ EXPECT_EQ(expected, test_case.lhs_cord.Compare(test_case.rhs_cord))
+ << "LHS=" << lhs_string << "; RHS=" << rhs_string;
+ EXPECT_EQ(expected, test_case.lhs_cord.Compare(rhs_string))
+ << "LHS=" << lhs_string << "; RHS=" << rhs_string;
+ EXPECT_EQ(-expected, test_case.rhs_cord.Compare(test_case.lhs_cord))
+ << "LHS=" << rhs_string << "; RHS=" << lhs_string;
+ EXPECT_EQ(-expected, test_case.rhs_cord.Compare(lhs_string))
+ << "LHS=" << rhs_string << "; RHS=" << lhs_string;
+}
+
+TEST(Cord, Compare) {
+ absl::Cord subcord("aaaaaBBBBBcccccDDDDD");
+ subcord = subcord.Subcord(3, 10);
+
+ absl::Cord tmp("aaaaaaaaaaaaaaaa");
+ tmp.Append("BBBBBBBBBBBBBBBB");
+ absl::Cord concat = absl::Cord("cccccccccccccccc");
+ concat.Append("DDDDDDDDDDDDDDDD");
+ concat.Prepend(tmp);
+
+ absl::Cord concat2("aaaaaaaaaaaaa");
+ concat2.Append("aaaBBBBBBBBBBBBBBBBccccc");
+ concat2.Append("cccccccccccDDDDDDDDDDDDDD");
+ concat2.Append("DD");
+
+ std::vector<CordCompareTestCase> test_cases = {{
+ // Inline cords
+ {"abcdef", "abcdef"},
+ {"abcdef", "abcdee"},
+ {"abcdef", "abcdeg"},
+ {"bbcdef", "abcdef"},
+ {"bbcdef", "abcdeg"},
+ {"abcdefa", "abcdef"},
+ {"abcdef", "abcdefa"},
+
+ // Small flat cords
+ {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDD"},
+ {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBxccccDDDDD"},
+ {"aaaaaBBBBBcxcccDDDDD", "aaaaaBBBBBcccccDDDDD"},
+ {"aaaaaBBBBBxccccDDDDD", "aaaaaBBBBBcccccDDDDX"},
+ {"aaaaaBBBBBcccccDDDDDa", "aaaaaBBBBBcccccDDDDD"},
+ {"aaaaaBBBBBcccccDDDDD", "aaaaaBBBBBcccccDDDDDa"},
+
+ // Subcords
+ {subcord, subcord},
+ {subcord, "aaBBBBBccc"},
+ {subcord, "aaBBBBBccd"},
+ {subcord, "aaBBBBBccb"},
+ {subcord, "aaBBBBBxcb"},
+ {subcord, "aaBBBBBccca"},
+ {subcord, "aaBBBBBcc"},
+
+ // Concats
+ {concat, concat},
+ {concat,
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDD"},
+ {concat,
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBcccccccccccccccxDDDDDDDDDDDDDDDD"},
+ {concat,
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBacccccccccccccccDDDDDDDDDDDDDDDD"},
+ {concat,
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDD"},
+ {concat,
+ "aaaaaaaaaaaaaaaaBBBBBBBBBBBBBBBBccccccccccccccccDDDDDDDDDDDDDDDDe"},
+
+ {concat, concat2},
+ }};
+
+ for (const auto& tc : test_cases) {
+ VerifyComparison(tc);
+ }
+}
+
+TEST(Cord, CompareAfterAssign) {
+ absl::Cord a("aaaaaa1111111");
+ absl::Cord b("aaaaaa2222222");
+ a = "cccccc";
+ b = "cccccc";
+ EXPECT_EQ(a, b);
+ EXPECT_FALSE(a < b);
+
+ a = "aaaa";
+ b = "bbbbb";
+ a = "";
+ b = "";
+ EXPECT_EQ(a, b);
+ EXPECT_FALSE(a < b);
+}
+
+// Test CompareTo() and ComparePrefix() against string and substring
+// comparison methods from std::basic_string.
+static void TestCompare(const absl::Cord& c, const absl::Cord& d,
+ RandomEngine* rng) {
+ typedef std::basic_string<uint8_t> ustring;
+ ustring cs(reinterpret_cast<const uint8_t*>(std::string(c).data()), c.size());
+ ustring ds(reinterpret_cast<const uint8_t*>(std::string(d).data()), d.size());
+ // ustring comparison is ideal because we expect Cord comparisons to be
+ // based on unsigned byte comparisons regardless of whether char is signed.
+ int expected = sign(cs.compare(ds));
+ EXPECT_EQ(expected, sign(c.Compare(d))) << c << ", " << d;
+}
+
+TEST(Compare, ComparisonIsUnsigned) {
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+ std::uniform_int_distribution<uint32_t> uniform_uint8(0, 255);
+ char x = static_cast<char>(uniform_uint8(rng));
+ TestCompare(
+ absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x)),
+ absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100), x ^ 0x80)), &rng);
+}
+
+TEST(Compare, RandomComparisons) {
+ const int kIters = 5000;
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+
+ int n = GetUniformRandomUpTo(&rng, 5000);
+ absl::Cord a[] = {MakeExternalCord(n),
+ absl::Cord("ant"),
+ absl::Cord("elephant"),
+ absl::Cord("giraffe"),
+ absl::Cord(std::string(GetUniformRandomUpTo(&rng, 100),
+ GetUniformRandomUpTo(&rng, 100))),
+ absl::Cord(""),
+ absl::Cord("x"),
+ absl::Cord("A"),
+ absl::Cord("B"),
+ absl::Cord("C")};
+ for (int i = 0; i < kIters; i++) {
+ absl::Cord c, d;
+ for (int j = 0; j < (i % 7) + 1; j++) {
+ c.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]);
+ d.Append(a[GetUniformRandomUpTo(&rng, ABSL_ARRAYSIZE(a))]);
+ }
+ std::bernoulli_distribution coin_flip(0.5);
+ TestCompare(coin_flip(rng) ? c : absl::Cord(std::string(c)),
+ coin_flip(rng) ? d : absl::Cord(std::string(d)), &rng);
+ }
+}
+
+template <typename T1, typename T2>
+void CompareOperators() {
+ const T1 a("a");
+ const T2 b("b");
+
+ EXPECT_TRUE(a == a);
+ // For pointer type (i.e. `const char*`), operator== compares the address
+ // instead of the std::string, so `a == const char*("a")` isn't necessarily true.
+ EXPECT_TRUE(std::is_pointer<T1>::value || a == T1("a"));
+ EXPECT_TRUE(std::is_pointer<T2>::value || a == T2("a"));
+ EXPECT_FALSE(a == b);
+
+ EXPECT_TRUE(a != b);
+ EXPECT_FALSE(a != a);
+
+ EXPECT_TRUE(a < b);
+ EXPECT_FALSE(b < a);
+
+ EXPECT_TRUE(b > a);
+ EXPECT_FALSE(a > b);
+
+ EXPECT_TRUE(a >= a);
+ EXPECT_TRUE(b >= a);
+ EXPECT_FALSE(a >= b);
+
+ EXPECT_TRUE(a <= a);
+ EXPECT_TRUE(a <= b);
+ EXPECT_FALSE(b <= a);
+}
+
+TEST(ComparisonOperators, Cord_Cord) {
+ CompareOperators<absl::Cord, absl::Cord>();
+}
+
+TEST(ComparisonOperators, Cord_StringPiece) {
+ CompareOperators<absl::Cord, absl::string_view>();
+}
+
+TEST(ComparisonOperators, StringPiece_Cord) {
+ CompareOperators<absl::string_view, absl::Cord>();
+}
+
+TEST(ComparisonOperators, Cord_string) {
+ CompareOperators<absl::Cord, std::string>();
+}
+
+TEST(ComparisonOperators, string_Cord) {
+ CompareOperators<std::string, absl::Cord>();
+}
+
+TEST(ComparisonOperators, stdstring_Cord) {
+ CompareOperators<std::string, absl::Cord>();
+}
+
+TEST(ComparisonOperators, Cord_stdstring) {
+ CompareOperators<absl::Cord, std::string>();
+}
+
+TEST(ComparisonOperators, charstar_Cord) {
+ CompareOperators<const char*, absl::Cord>();
+}
+
+TEST(ComparisonOperators, Cord_charstar) {
+ CompareOperators<absl::Cord, const char*>();
+}
+
+TEST(ConstructFromExternal, ReleaserInvoked) {
+ // Empty external memory means the releaser should be called immediately.
+ {
+ bool invoked = false;
+ auto releaser = [&invoked](absl::string_view) { invoked = true; };
+ {
+ auto c = absl::MakeCordFromExternal("", releaser);
+ EXPECT_TRUE(invoked);
+ }
+ }
+
+ // If the size of the data is small enough, a future constructor
+ // implementation may copy the bytes and immediately invoke the releaser
+ // instead of creating an external node. We make a large dummy std::string to
+ // make this test independent of such an optimization.
+ std::string large_dummy(2048, 'c');
+ {
+ bool invoked = false;
+ auto releaser = [&invoked](absl::string_view) { invoked = true; };
+ {
+ auto c = absl::MakeCordFromExternal(large_dummy, releaser);
+ EXPECT_FALSE(invoked);
+ }
+ EXPECT_TRUE(invoked);
+ }
+
+ {
+ bool invoked = false;
+ auto releaser = [&invoked](absl::string_view) { invoked = true; };
+ {
+ absl::Cord copy;
+ {
+ auto c = absl::MakeCordFromExternal(large_dummy, releaser);
+ copy = c;
+ EXPECT_FALSE(invoked);
+ }
+ EXPECT_FALSE(invoked);
+ }
+ EXPECT_TRUE(invoked);
+ }
+}
+
+TEST(ConstructFromExternal, CompareContents) {
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+
+ for (int length = 1; length <= 2048; length *= 2) {
+ std::string data = RandomLowercaseString(&rng, length);
+ auto* external = new std::string(data);
+ auto cord =
+ absl::MakeCordFromExternal(*external, [external](absl::string_view sv) {
+ EXPECT_EQ(external->data(), sv.data());
+ EXPECT_EQ(external->size(), sv.size());
+ delete external;
+ });
+ EXPECT_EQ(data, cord);
+ }
+}
+
+TEST(ConstructFromExternal, LargeReleaser) {
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+ constexpr size_t kLength = 256;
+ std::string data = RandomLowercaseString(&rng, kLength);
+ std::array<char, kLength> data_array;
+ for (size_t i = 0; i < kLength; ++i) data_array[i] = data[i];
+ bool invoked = false;
+ auto releaser = [data_array, &invoked](absl::string_view data) {
+ EXPECT_EQ(data, absl::string_view(data_array.data(), data_array.size()));
+ invoked = true;
+ };
+ (void)absl::MakeCordFromExternal(data, releaser);
+ EXPECT_TRUE(invoked);
+}
+
+TEST(ConstructFromExternal, FunctionPointerReleaser) {
+ static absl::string_view data("hello world");
+ static bool invoked;
+ auto* releaser =
+ static_cast<void (*)(absl::string_view)>([](absl::string_view sv) {
+ EXPECT_EQ(data, sv);
+ invoked = true;
+ });
+ invoked = false;
+ (void)absl::MakeCordFromExternal(data, releaser);
+ EXPECT_TRUE(invoked);
+
+ invoked = false;
+ (void)absl::MakeCordFromExternal(data, *releaser);
+ EXPECT_TRUE(invoked);
+}
+
+TEST(ConstructFromExternal, MoveOnlyReleaser) {
+ struct Releaser {
+ explicit Releaser(bool* invoked) : invoked(invoked) {}
+ Releaser(Releaser&& other) noexcept : invoked(other.invoked) {}
+ void operator()(absl::string_view) const { *invoked = true; }
+
+ bool* invoked;
+ };
+
+ bool invoked = false;
+ (void)absl::MakeCordFromExternal("dummy", Releaser(&invoked));
+ EXPECT_TRUE(invoked);
+}
+
+TEST(ConstructFromExternal, NonTrivialReleaserDestructor) {
+ struct Releaser {
+ explicit Releaser(bool* destroyed) : destroyed(destroyed) {}
+ ~Releaser() { *destroyed = true; }
+ void operator()(absl::string_view) const {}
+
+ bool* destroyed;
+ };
+
+ bool destroyed = false;
+ Releaser releaser(&destroyed);
+ (void)absl::MakeCordFromExternal("dummy", releaser);
+ EXPECT_TRUE(destroyed);
+}
+
+TEST(ConstructFromExternal, ReferenceQualifierOverloads) {
+ struct Releaser {
+ void operator()(absl::string_view) & { *lvalue_invoked = true; }
+ void operator()(absl::string_view) && { *rvalue_invoked = true; }
+
+ bool* lvalue_invoked;
+ bool* rvalue_invoked;
+ };
+
+ bool lvalue_invoked = false;
+ bool rvalue_invoked = false;
+ Releaser releaser = {&lvalue_invoked, &rvalue_invoked};
+ (void)absl::MakeCordFromExternal("", releaser);
+ EXPECT_FALSE(lvalue_invoked);
+ EXPECT_TRUE(rvalue_invoked);
+ rvalue_invoked = false;
+
+ (void)absl::MakeCordFromExternal("dummy", releaser);
+ EXPECT_FALSE(lvalue_invoked);
+ EXPECT_TRUE(rvalue_invoked);
+ rvalue_invoked = false;
+
+ // NOLINTNEXTLINE: suppress clang-tidy std::move on trivially copyable type.
+ (void)absl::MakeCordFromExternal("dummy", std::move(releaser));
+ EXPECT_FALSE(lvalue_invoked);
+ EXPECT_TRUE(rvalue_invoked);
+}
+
+TEST(ExternalMemory, BasicUsage) {
+ static const char* strings[] = { "", "hello", "there" };
+ for (const char* str : strings) {
+ absl::Cord dst("(prefix)");
+ AddExternalMemory(str, &dst);
+ dst.Append("(suffix)");
+ EXPECT_EQ((std::string("(prefix)") + str + std::string("(suffix)")),
+ std::string(dst));
+ }
+}
+
+TEST(ExternalMemory, RemovePrefixSuffix) {
+ // Exhaustively try all sub-strings.
+ absl::Cord cord = MakeComposite();
+ std::string s = std::string(cord);
+ for (int offset = 0; offset <= s.size(); offset++) {
+ for (int length = 0; length <= s.size() - offset; length++) {
+ absl::Cord result(cord);
+ result.RemovePrefix(offset);
+ result.RemoveSuffix(result.size() - length);
+ EXPECT_EQ(s.substr(offset, length), std::string(result))
+ << offset << " " << length;
+ }
+ }
+}
+
+TEST(ExternalMemory, Get) {
+ absl::Cord cord("hello");
+ AddExternalMemory(" world!", &cord);
+ AddExternalMemory(" how are ", &cord);
+ cord.Append(" you?");
+ std::string s = std::string(cord);
+ for (int i = 0; i < s.size(); i++) {
+ EXPECT_EQ(s[i], cord[i]);
+ }
+}
+
+// CordMemoryUsage tests verify the correctness of the EstimatedMemoryUsage()
+// These tests take into account that the reported memory usage is approximate
+// and non-deterministic. For all tests, We verify that the reported memory
+// usage is larger than `size()`, and less than `size() * 1.5` as a cord should
+// never reserve more 'extra' capacity than half of its size as it grows.
+// Additionally we have some whiteboxed expectations based on our knowledge of
+// the layout and size of empty and inlined cords, and flat nodes.
+
+TEST(CordMemoryUsage, Empty) {
+ EXPECT_EQ(sizeof(absl::Cord), absl::Cord().EstimatedMemoryUsage());
+}
+
+TEST(CordMemoryUsage, Embedded) {
+ absl::Cord a("hello");
+ EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
+}
+
+TEST(CordMemoryUsage, EmbeddedAppend) {
+ absl::Cord a("a");
+ absl::Cord b("bcd");
+ EXPECT_EQ(b.EstimatedMemoryUsage(), sizeof(absl::Cord));
+ a.Append(b);
+ EXPECT_EQ(a.EstimatedMemoryUsage(), sizeof(absl::Cord));
+}
+
+TEST(CordMemoryUsage, ExternalMemory) {
+ static const int kLength = 1000;
+ absl::Cord cord;
+ AddExternalMemory(std::string(kLength, 'x'), &cord);
+ EXPECT_GT(cord.EstimatedMemoryUsage(), kLength);
+ EXPECT_LE(cord.EstimatedMemoryUsage(), kLength * 1.5);
+}
+
+TEST(CordMemoryUsage, Flat) {
+ static const int kLength = 125;
+ absl::Cord a(std::string(kLength, 'a'));
+ EXPECT_GT(a.EstimatedMemoryUsage(), kLength);
+ EXPECT_LE(a.EstimatedMemoryUsage(), kLength * 1.5);
+}
+
+TEST(CordMemoryUsage, AppendFlat) {
+ using absl::strings_internal::CordTestAccess;
+ absl::Cord a(std::string(CordTestAccess::MaxFlatLength(), 'a'));
+ size_t length = a.EstimatedMemoryUsage();
+ a.Append(std::string(CordTestAccess::MaxFlatLength(), 'b'));
+ size_t delta = a.EstimatedMemoryUsage() - length;
+ EXPECT_GT(delta, CordTestAccess::MaxFlatLength());
+ EXPECT_LE(delta, CordTestAccess::MaxFlatLength() * 1.5);
+}
+
+// Regtest for a change that had to be rolled back because it expanded out
+// of the InlineRep too soon, which was observable through MemoryUsage().
+TEST(CordMemoryUsage, InlineRep) {
+ constexpr size_t kMaxInline = 15; // Cord::InlineRep::N
+ const std::string small_string(kMaxInline, 'x');
+ absl::Cord c1(small_string);
+
+ absl::Cord c2;
+ c2.Append(small_string);
+ EXPECT_EQ(c1, c2);
+ EXPECT_EQ(c1.EstimatedMemoryUsage(), c2.EstimatedMemoryUsage());
+}
+
+} // namespace
+
+// Regtest for 7510292 (fix a bug introduced by 7465150)
+TEST(Cord, Concat_Append) {
+ // Create a rep of type CONCAT
+ absl::Cord s1("foobarbarbarbarbar");
+ s1.Append("abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg");
+ size_t size = s1.size();
+
+ // Create a copy of s1 and append to it.
+ absl::Cord s2 = s1;
+ s2.Append("x");
+
+ // 7465150 modifies s1 when it shouldn't.
+ EXPECT_EQ(s1.size(), size);
+ EXPECT_EQ(s2.size(), size + 1);
+}
+
+TEST(MakeFragmentedCord, MakeFragmentedCordFromInitializerList) {
+ absl::Cord fragmented =
+ absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});
+
+ EXPECT_EQ("A fragmented Cord", fragmented);
+
+ auto chunk_it = fragmented.chunk_begin();
+
+ ASSERT_TRUE(chunk_it != fragmented.chunk_end());
+ EXPECT_EQ("A ", *chunk_it);
+
+ ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
+ EXPECT_EQ("fragmented ", *chunk_it);
+
+ ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
+ EXPECT_EQ("Cord", *chunk_it);
+
+ ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
+}
+
+TEST(MakeFragmentedCord, MakeFragmentedCordFromVector) {
+ std::vector<absl::string_view> chunks = {"A ", "fragmented ", "Cord"};
+ absl::Cord fragmented = absl::MakeFragmentedCord(chunks);
+
+ EXPECT_EQ("A fragmented Cord", fragmented);
+
+ auto chunk_it = fragmented.chunk_begin();
+
+ ASSERT_TRUE(chunk_it != fragmented.chunk_end());
+ EXPECT_EQ("A ", *chunk_it);
+
+ ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
+ EXPECT_EQ("fragmented ", *chunk_it);
+
+ ASSERT_TRUE(++chunk_it != fragmented.chunk_end());
+ EXPECT_EQ("Cord", *chunk_it);
+
+ ASSERT_TRUE(++chunk_it == fragmented.chunk_end());
+}
+
+TEST(CordChunkIterator, Traits) {
+ static_assert(std::is_copy_constructible<absl::Cord::ChunkIterator>::value,
+ "");
+ static_assert(std::is_copy_assignable<absl::Cord::ChunkIterator>::value, "");
+
+ // Move semantics to satisfy swappable via std::swap
+ static_assert(std::is_move_constructible<absl::Cord::ChunkIterator>::value,
+ "");
+ static_assert(std::is_move_assignable<absl::Cord::ChunkIterator>::value, "");
+
+ static_assert(
+ std::is_same<
+ std::iterator_traits<absl::Cord::ChunkIterator>::iterator_category,
+ std::input_iterator_tag>::value,
+ "");
+ static_assert(
+ std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::value_type,
+ absl::string_view>::value,
+ "");
+ static_assert(
+ std::is_same<
+ std::iterator_traits<absl::Cord::ChunkIterator>::difference_type,
+ ptrdiff_t>::value,
+ "");
+ static_assert(
+ std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::pointer,
+ const absl::string_view*>::value,
+ "");
+ static_assert(
+ std::is_same<std::iterator_traits<absl::Cord::ChunkIterator>::reference,
+ absl::string_view>::value,
+ "");
+}
+
+static void VerifyChunkIterator(const absl::Cord& cord,
+ size_t expected_chunks) {
+ EXPECT_EQ(cord.chunk_begin() == cord.chunk_end(), cord.empty()) << cord;
+ EXPECT_EQ(cord.chunk_begin() != cord.chunk_end(), !cord.empty());
+
+ absl::Cord::ChunkRange range = cord.Chunks();
+ EXPECT_EQ(range.begin() == range.end(), cord.empty());
+ EXPECT_EQ(range.begin() != range.end(), !cord.empty());
+
+ std::string content(cord);
+ size_t pos = 0;
+ auto pre_iter = cord.chunk_begin(), post_iter = cord.chunk_begin();
+ size_t n_chunks = 0;
+ while (pre_iter != cord.chunk_end() && post_iter != cord.chunk_end()) {
+ EXPECT_FALSE(pre_iter == cord.chunk_end()); // NOLINT: explicitly test ==
+ EXPECT_FALSE(post_iter == cord.chunk_end()); // NOLINT
+
+ EXPECT_EQ(pre_iter, post_iter);
+ EXPECT_EQ(*pre_iter, *post_iter);
+
+ EXPECT_EQ(pre_iter->data(), (*pre_iter).data());
+ EXPECT_EQ(pre_iter->size(), (*pre_iter).size());
+
+ absl::string_view chunk = *pre_iter;
+ EXPECT_FALSE(chunk.empty());
+ EXPECT_LE(pos + chunk.size(), content.size());
+ EXPECT_EQ(absl::string_view(content.c_str() + pos, chunk.size()), chunk);
+
+ int n_equal_iterators = 0;
+ for (absl::Cord::ChunkIterator it = range.begin(); it != range.end();
+ ++it) {
+ n_equal_iterators += static_cast<int>(it == pre_iter);
+ }
+ EXPECT_EQ(n_equal_iterators, 1);
+
+ ++pre_iter;
+ EXPECT_EQ(*post_iter++, chunk);
+
+ pos += chunk.size();
+ ++n_chunks;
+ }
+ EXPECT_EQ(expected_chunks, n_chunks);
+ EXPECT_EQ(pos, content.size());
+ EXPECT_TRUE(pre_iter == cord.chunk_end()); // NOLINT: explicitly test ==
+ EXPECT_TRUE(post_iter == cord.chunk_end()); // NOLINT
+}
+
+TEST(CordChunkIterator, Operations) {
+ absl::Cord empty_cord;
+ VerifyChunkIterator(empty_cord, 0);
+
+ absl::Cord small_buffer_cord("small cord");
+ VerifyChunkIterator(small_buffer_cord, 1);
+
+ absl::Cord flat_node_cord("larger than small buffer optimization");
+ VerifyChunkIterator(flat_node_cord, 1);
+
+ VerifyChunkIterator(
+ absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ",
+ "testing ", "chunk ", "iterations."}),
+ 8);
+
+ absl::Cord reused_nodes_cord(std::string(40, 'c'));
+ reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'b')));
+ reused_nodes_cord.Prepend(absl::Cord(std::string(40, 'a')));
+ size_t expected_chunks = 3;
+ for (int i = 0; i < 8; ++i) {
+ reused_nodes_cord.Prepend(reused_nodes_cord);
+ expected_chunks *= 2;
+ VerifyChunkIterator(reused_nodes_cord, expected_chunks);
+ }
+
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+ absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
+ absl::Cord subcords;
+ for (int i = 0; i < 128; ++i) subcords.Prepend(flat_cord.Subcord(i, 128));
+ VerifyChunkIterator(subcords, 128);
+}
+
+TEST(CordCharIterator, Traits) {
+ static_assert(std::is_copy_constructible<absl::Cord::CharIterator>::value,
+ "");
+ static_assert(std::is_copy_assignable<absl::Cord::CharIterator>::value, "");
+
+ // Move semantics to satisfy swappable via std::swap
+ static_assert(std::is_move_constructible<absl::Cord::CharIterator>::value,
+ "");
+ static_assert(std::is_move_assignable<absl::Cord::CharIterator>::value, "");
+
+ static_assert(
+ std::is_same<
+ std::iterator_traits<absl::Cord::CharIterator>::iterator_category,
+ std::input_iterator_tag>::value,
+ "");
+ static_assert(
+ std::is_same<std::iterator_traits<absl::Cord::CharIterator>::value_type,
+ char>::value,
+ "");
+ static_assert(
+ std::is_same<
+ std::iterator_traits<absl::Cord::CharIterator>::difference_type,
+ ptrdiff_t>::value,
+ "");
+ static_assert(
+ std::is_same<std::iterator_traits<absl::Cord::CharIterator>::pointer,
+ const char*>::value,
+ "");
+ static_assert(
+ std::is_same<std::iterator_traits<absl::Cord::CharIterator>::reference,
+ const char&>::value,
+ "");
+}
+
+static void VerifyCharIterator(const absl::Cord& cord) {
+ EXPECT_EQ(cord.char_begin() == cord.char_end(), cord.empty());
+ EXPECT_EQ(cord.char_begin() != cord.char_end(), !cord.empty());
+
+ absl::Cord::CharRange range = cord.Chars();
+ EXPECT_EQ(range.begin() == range.end(), cord.empty());
+ EXPECT_EQ(range.begin() != range.end(), !cord.empty());
+
+ size_t i = 0;
+ absl::Cord::CharIterator pre_iter = cord.char_begin();
+ absl::Cord::CharIterator post_iter = cord.char_begin();
+ std::string content(cord);
+ while (pre_iter != cord.char_end() && post_iter != cord.char_end()) {
+ EXPECT_FALSE(pre_iter == cord.char_end()); // NOLINT: explicitly test ==
+ EXPECT_FALSE(post_iter == cord.char_end()); // NOLINT
+
+ EXPECT_LT(i, cord.size());
+ EXPECT_EQ(content[i], *pre_iter);
+
+ EXPECT_EQ(pre_iter, post_iter);
+ EXPECT_EQ(*pre_iter, *post_iter);
+ EXPECT_EQ(&*pre_iter, &*post_iter);
+
+ EXPECT_EQ(&*pre_iter, pre_iter.operator->());
+
+ const char* character_address = &*pre_iter;
+ absl::Cord::CharIterator copy = pre_iter;
+ ++copy;
+ EXPECT_EQ(character_address, &*pre_iter);
+
+ int n_equal_iterators = 0;
+ for (absl::Cord::CharIterator it = range.begin(); it != range.end(); ++it) {
+ n_equal_iterators += static_cast<int>(it == pre_iter);
+ }
+ EXPECT_EQ(n_equal_iterators, 1);
+
+ absl::Cord::CharIterator advance_iter = range.begin();
+ absl::Cord::Advance(&advance_iter, i);
+ EXPECT_EQ(pre_iter, advance_iter);
+
+ advance_iter = range.begin();
+ EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, i), cord.Subcord(0, i));
+ EXPECT_EQ(pre_iter, advance_iter);
+
+ advance_iter = pre_iter;
+ absl::Cord::Advance(&advance_iter, cord.size() - i);
+ EXPECT_EQ(range.end(), advance_iter);
+
+ advance_iter = pre_iter;
+ EXPECT_EQ(absl::Cord::AdvanceAndRead(&advance_iter, cord.size() - i),
+ cord.Subcord(i, cord.size() - i));
+ EXPECT_EQ(range.end(), advance_iter);
+
+ ++i;
+ ++pre_iter;
+ post_iter++;
+ }
+ EXPECT_EQ(i, cord.size());
+ EXPECT_TRUE(pre_iter == cord.char_end()); // NOLINT: explicitly test ==
+ EXPECT_TRUE(post_iter == cord.char_end()); // NOLINT
+
+ absl::Cord::CharIterator zero_advanced_end = cord.char_end();
+ absl::Cord::Advance(&zero_advanced_end, 0);
+ EXPECT_EQ(zero_advanced_end, cord.char_end());
+
+ absl::Cord::CharIterator it = cord.char_begin();
+ for (absl::string_view chunk : cord.Chunks()) {
+ while (!chunk.empty()) {
+ EXPECT_EQ(absl::Cord::ChunkRemaining(it), chunk);
+ chunk.remove_prefix(1);
+ ++it;
+ }
+ }
+}
+
+TEST(CordCharIterator, Operations) {
+ absl::Cord empty_cord;
+ VerifyCharIterator(empty_cord);
+
+ absl::Cord small_buffer_cord("small cord");
+ VerifyCharIterator(small_buffer_cord);
+
+ absl::Cord flat_node_cord("larger than small buffer optimization");
+ VerifyCharIterator(flat_node_cord);
+
+ VerifyCharIterator(
+ absl::MakeFragmentedCord({"a ", "small ", "fragmented ", "cord ", "for ",
+ "testing ", "character ", "iteration."}));
+
+ absl::Cord reused_nodes_cord("ghi");
+ reused_nodes_cord.Prepend(absl::Cord("def"));
+ reused_nodes_cord.Prepend(absl::Cord("abc"));
+ for (int i = 0; i < 4; ++i) {
+ reused_nodes_cord.Prepend(reused_nodes_cord);
+ VerifyCharIterator(reused_nodes_cord);
+ }
+
+ RandomEngine rng(testing::GTEST_FLAG(random_seed));
+ absl::Cord flat_cord(RandomLowercaseString(&rng, 256));
+ absl::Cord subcords;
+ for (int i = 0; i < 4; ++i) subcords.Prepend(flat_cord.Subcord(16 * i, 128));
+ VerifyCharIterator(subcords);
+}
+
+TEST(Cord, StreamingOutput) {
+ absl::Cord c =
+ absl::MakeFragmentedCord({"A ", "small ", "fragmented ", "Cord", "."});
+ std::stringstream output;
+ output << c;
+ EXPECT_EQ("A small fragmented Cord.", output.str());
+}
+
+TEST(Cord, ForEachChunk) {
+ for (int num_elements : {1, 10, 200}) {
+ SCOPED_TRACE(num_elements);
+ std::vector<std::string> cord_chunks;
+ for (int i = 0; i < num_elements; ++i) {
+ cord_chunks.push_back(absl::StrCat("[", i, "]"));
+ }
+ absl::Cord c = absl::MakeFragmentedCord(cord_chunks);
+
+ std::vector<std::string> iterated_chunks;
+ absl::CordTestPeer::ForEachChunk(c,
+ [&iterated_chunks](absl::string_view sv) {
+ iterated_chunks.emplace_back(sv);
+ });
+ EXPECT_EQ(iterated_chunks, cord_chunks);
+ }
+}
+
+TEST(Cord, SmallBufferAssignFromOwnData) {
+ constexpr size_t kMaxInline = 15;
+ std::string contents = "small buff cord";
+ EXPECT_EQ(contents.size(), kMaxInline);
+ for (size_t pos = 0; pos < contents.size(); ++pos) {
+ for (size_t count = contents.size() - pos; count > 0; --count) {
+ absl::Cord c(contents);
+ absl::string_view flat = c.Flatten();
+ c = flat.substr(pos, count);
+ EXPECT_EQ(c, contents.substr(pos, count))
+ << "pos = " << pos << "; count = " << count;
+ }
+ }
+}
diff --git a/absl/strings/cord_test_helpers.h b/absl/strings/cord_test_helpers.h
new file mode 100644
index 00000000..f1036e3b
--- /dev/null
+++ b/absl/strings/cord_test_helpers.h
@@ -0,0 +1,60 @@
+//
+// 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_STRINGS_CORD_TEST_HELPERS_H_
+#define ABSL_STRINGS_CORD_TEST_HELPERS_H_
+
+#include "absl/strings/cord.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
+// Creates a multi-segment Cord from an iterable container of strings. The
+// resulting Cord is guaranteed to have one segment for every string in the
+// container. This allows code to be unit tested with multi-segment Cord
+// inputs.
+//
+// Example:
+//
+// absl::Cord c = absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});
+// EXPECT_FALSE(c.GetFlat(&unused));
+//
+// The mechanism by which this Cord is created is an implementation detail. Any
+// implementation that produces a multi-segment Cord may produce a flat Cord in
+// the future as new optimizations are added to the Cord class.
+// MakeFragmentedCord will, however, always be updated to return a multi-segment
+// Cord.
+template <typename Container>
+Cord MakeFragmentedCord(const Container& c) {
+ Cord result;
+ for (const auto& s : c) {
+ auto* external = new std::string(s);
+ Cord tmp = absl::MakeCordFromExternal(
+ *external, [external](absl::string_view) { delete external; });
+ tmp.Prepend(result);
+ result = tmp;
+ }
+ return result;
+}
+
+inline Cord MakeFragmentedCord(std::initializer_list<absl::string_view> list) {
+ return MakeFragmentedCord<std::initializer_list<absl::string_view>>(list);
+}
+
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STRINGS_CORD_TEST_HELPERS_H_
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc
index eb0974dc..7adc1b65 100644
--- a/absl/strings/escaping.cc
+++ b/absl/strings/escaping.cc
@@ -26,6 +26,7 @@
#include "absl/base/internal/raw_logging.h"
#include "absl/base/internal/unaligned_access.h"
#include "absl/strings/internal/char_map.h"
+#include "absl/strings/internal/escaping.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/internal/utf8.h"
#include "absl/strings/str_cat.h"
@@ -33,30 +34,9 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
-// Digit conversion.
-constexpr char kHexChar[] = "0123456789abcdef";
-
-constexpr char kHexTable[513] =
- "000102030405060708090a0b0c0d0e0f"
- "101112131415161718191a1b1c1d1e1f"
- "202122232425262728292a2b2c2d2e2f"
- "303132333435363738393a3b3c3d3e3f"
- "404142434445464748494a4b4c4d4e4f"
- "505152535455565758595a5b5c5d5e5f"
- "606162636465666768696a6b6c6d6e6f"
- "707172737475767778797a7b7c7d7e7f"
- "808182838485868788898a8b8c8d8e8f"
- "909192939495969798999a9b9c9d9e9f"
- "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
- "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
- "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
- "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
- "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
- "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
-
// These are used for the leave_nulls_escaped argument to CUnescapeInternal().
constexpr bool kUnescapeNulls = false;
@@ -349,14 +329,14 @@ std::string CEscapeInternal(absl::string_view src, bool use_hex,
(last_hex_escape && absl::ascii_isxdigit(c)))) {
if (use_hex) {
dest.append("\\" "x");
- dest.push_back(kHexChar[c / 16]);
- dest.push_back(kHexChar[c % 16]);
+ dest.push_back(numbers_internal::kHexChar[c / 16]);
+ dest.push_back(numbers_internal::kHexChar[c % 16]);
is_hex_escape = true;
} else {
dest.append("\\");
- dest.push_back(kHexChar[c / 64]);
- dest.push_back(kHexChar[(c % 64) / 8]);
- dest.push_back(kHexChar[c % 8]);
+ dest.push_back(numbers_internal::kHexChar[c / 64]);
+ dest.push_back(numbers_internal::kHexChar[(c % 64) / 8]);
+ dest.push_back(numbers_internal::kHexChar[c % 8]);
}
} else {
dest.push_back(c);
@@ -785,177 +765,10 @@ constexpr signed char kUnWebSafeBase64[] = {
};
/* clang-format on */
-size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) {
- // Base64 encodes three bytes of input at a time. If the input is not
- // divisible by three, we pad as appropriate.
- //
- // (from https://tools.ietf.org/html/rfc3548)
- // Special processing is performed if fewer than 24 bits are available
- // at the end of the data being encoded. A full encoding quantum is
- // always completed at the end of a quantity. When fewer than 24 input
- // bits are available in an input group, zero bits are added (on the
- // right) to form an integral number of 6-bit groups. Padding at the
- // end of the data is performed using the '=' character. Since all base
- // 64 input is an integral number of octets, only the following cases
- // can arise:
-
- // Base64 encodes each three bytes of input into four bytes of output.
- size_t len = (input_len / 3) * 4;
-
- if (input_len % 3 == 0) {
- // (from https://tools.ietf.org/html/rfc3548)
- // (1) the final quantum of encoding input is an integral multiple of 24
- // bits; here, the final unit of encoded output will be an integral
- // multiple of 4 characters with no "=" padding,
- } else if (input_len % 3 == 1) {
- // (from https://tools.ietf.org/html/rfc3548)
- // (2) the final quantum of encoding input is exactly 8 bits; here, the
- // final unit of encoded output will be two characters followed by two
- // "=" padding characters, or
- len += 2;
- if (do_padding) {
- len += 2;
- }
- } else { // (input_len % 3 == 2)
- // (from https://tools.ietf.org/html/rfc3548)
- // (3) the final quantum of encoding input is exactly 16 bits; here, the
- // final unit of encoded output will be three characters followed by one
- // "=" padding character.
- len += 3;
- if (do_padding) {
- len += 1;
- }
- }
-
- assert(len >= input_len); // make sure we didn't overflow
- return len;
-}
-
-size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest,
- size_t szdest, const char* base64,
- bool do_padding) {
- static const char kPad64 = '=';
-
- if (szsrc * 4 > szdest * 3) return 0;
-
- char* cur_dest = dest;
- const unsigned char* cur_src = src;
-
- char* const limit_dest = dest + szdest;
- const unsigned char* const limit_src = src + szsrc;
-
- // Three bytes of data encodes to four characters of cyphertext.
- // So we can pump through three-byte chunks atomically.
- if (szsrc >= 3) { // "limit_src - 3" is UB if szsrc < 3.
- while (cur_src < limit_src - 3) { // While we have >= 32 bits.
- uint32_t in = absl::big_endian::Load32(cur_src) >> 8;
-
- cur_dest[0] = base64[in >> 18];
- in &= 0x3FFFF;
- cur_dest[1] = base64[in >> 12];
- in &= 0xFFF;
- cur_dest[2] = base64[in >> 6];
- in &= 0x3F;
- cur_dest[3] = base64[in];
-
- cur_dest += 4;
- cur_src += 3;
- }
- }
- // To save time, we didn't update szdest or szsrc in the loop. So do it now.
- szdest = limit_dest - cur_dest;
- szsrc = limit_src - cur_src;
-
- /* now deal with the tail (<=3 bytes) */
- switch (szsrc) {
- case 0:
- // Nothing left; nothing more to do.
- break;
- case 1: {
- // One byte left: this encodes to two characters, and (optionally)
- // two pad characters to round out the four-character cypherblock.
- if (szdest < 2) return 0;
- uint32_t in = cur_src[0];
- cur_dest[0] = base64[in >> 2];
- in &= 0x3;
- cur_dest[1] = base64[in << 4];
- cur_dest += 2;
- szdest -= 2;
- if (do_padding) {
- if (szdest < 2) return 0;
- cur_dest[0] = kPad64;
- cur_dest[1] = kPad64;
- cur_dest += 2;
- szdest -= 2;
- }
- break;
- }
- case 2: {
- // Two bytes left: this encodes to three characters, and (optionally)
- // one pad character to round out the four-character cypherblock.
- if (szdest < 3) return 0;
- uint32_t in = absl::big_endian::Load16(cur_src);
- cur_dest[0] = base64[in >> 10];
- in &= 0x3FF;
- cur_dest[1] = base64[in >> 4];
- in &= 0x00F;
- cur_dest[2] = base64[in << 2];
- cur_dest += 3;
- szdest -= 3;
- if (do_padding) {
- if (szdest < 1) return 0;
- cur_dest[0] = kPad64;
- cur_dest += 1;
- szdest -= 1;
- }
- break;
- }
- case 3: {
- // Three bytes left: same as in the big loop above. We can't do this in
- // the loop because the loop above always reads 4 bytes, and the fourth
- // byte is past the end of the input.
- if (szdest < 4) return 0;
- uint32_t in = (cur_src[0] << 16) + absl::big_endian::Load16(cur_src + 1);
- cur_dest[0] = base64[in >> 18];
- in &= 0x3FFFF;
- cur_dest[1] = base64[in >> 12];
- in &= 0xFFF;
- cur_dest[2] = base64[in >> 6];
- in &= 0x3F;
- cur_dest[3] = base64[in];
- cur_dest += 4;
- szdest -= 4;
- break;
- }
- default:
- // Should not be reached: blocks of 4 bytes are handled
- // in the while loop before this switch statement.
- ABSL_RAW_LOG(FATAL, "Logic problem? szsrc = %zu", szsrc);
- break;
- }
- return (cur_dest - dest);
-}
-
-constexpr char kBase64Chars[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
constexpr char kWebSafeBase64Chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
template <typename String>
-void Base64EscapeInternal(const unsigned char* src, size_t szsrc, String* dest,
- bool do_padding, const char* base64_chars) {
- const size_t calc_escaped_size =
- CalculateBase64EscapedLenInternal(szsrc, do_padding);
- strings_internal::STLStringResizeUninitialized(dest, calc_escaped_size);
-
- const size_t escaped_len = Base64EscapeInternal(
- src, szsrc, &(*dest)[0], dest->size(), base64_chars, do_padding);
- assert(calc_escaped_size == escaped_len);
- dest->erase(escaped_len);
-}
-
-template <typename String>
bool Base64UnescapeInternal(const char* src, size_t slen, String* dest,
const signed char* unbase64) {
// Determine the size of the output std::string. Base64 encodes every 3 bytes into
@@ -983,7 +796,7 @@ bool Base64UnescapeInternal(const char* src, size_t slen, String* dest,
}
/* clang-format off */
-constexpr char kHexValue[256] = {
+constexpr char kHexValueLenient[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -999,8 +812,9 @@ constexpr char kHexValue[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
+
/* clang-format on */
// This is a templated function so that T can be either a char*
@@ -1009,8 +823,8 @@ constexpr char kHexValue[256] = {
template <typename T>
void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) {
for (int i = 0; i < num; i++) {
- to[i] = (kHexValue[from[i * 2] & 0xFF] << 4) +
- (kHexValue[from[i * 2 + 1] & 0xFF]);
+ to[i] = (kHexValueLenient[from[i * 2] & 0xFF] << 4) +
+ (kHexValueLenient[from[i * 2 + 1] & 0xFF]);
}
}
@@ -1020,7 +834,7 @@ template <typename T>
void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) {
auto dest_ptr = &dest[0];
for (auto src_ptr = src; src_ptr != (src + num); ++src_ptr, dest_ptr += 2) {
- const char* hex_p = &kHexTable[*src_ptr * 2];
+ const char* hex_p = &numbers_internal::kHexTable[*src_ptr * 2];
std::copy(hex_p, hex_p + 2, dest_ptr);
}
}
@@ -1088,26 +902,30 @@ bool WebSafeBase64Unescape(absl::string_view src, std::string* dest) {
}
void Base64Escape(absl::string_view src, std::string* dest) {
- Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()),
- src.size(), dest, true, kBase64Chars);
+ strings_internal::Base64EscapeInternal(
+ reinterpret_cast<const unsigned char*>(src.data()), src.size(), dest,
+ true, strings_internal::kBase64Chars);
}
void WebSafeBase64Escape(absl::string_view src, std::string* dest) {
- Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()),
- src.size(), dest, false, kWebSafeBase64Chars);
+ strings_internal::Base64EscapeInternal(
+ reinterpret_cast<const unsigned char*>(src.data()), src.size(), dest,
+ false, kWebSafeBase64Chars);
}
std::string Base64Escape(absl::string_view src) {
std::string dest;
- Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()),
- src.size(), &dest, true, kBase64Chars);
+ strings_internal::Base64EscapeInternal(
+ reinterpret_cast<const unsigned char*>(src.data()), src.size(), &dest,
+ true, strings_internal::kBase64Chars);
return dest;
}
std::string WebSafeBase64Escape(absl::string_view src) {
std::string dest;
- Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()),
- src.size(), &dest, false, kWebSafeBase64Chars);
+ strings_internal::Base64EscapeInternal(
+ reinterpret_cast<const unsigned char*>(src.data()), src.size(), &dest,
+ false, kWebSafeBase64Chars);
return dest;
}
@@ -1127,5 +945,5 @@ std::string BytesToHexString(absl::string_view from) {
return result;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/escaping.h b/absl/strings/escaping.h
index 7f1c5d46..f5ca26c5 100644
--- a/absl/strings/escaping.h
+++ b/absl/strings/escaping.h
@@ -33,7 +33,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// CUnescape()
//
@@ -158,7 +158,7 @@ std::string HexStringToBytes(absl::string_view from);
// `2*from.size()`.
std::string BytesToHexString(absl::string_view from);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_ESCAPING_H_
diff --git a/absl/strings/internal/char_map.h b/absl/strings/internal/char_map.h
index 772ae869..a76e6036 100644
--- a/absl/strings/internal/char_map.h
+++ b/absl/strings/internal/char_map.h
@@ -28,7 +28,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
class Charmap {
@@ -150,7 +150,7 @@ constexpr Charmap GraphCharmap() { return PrintCharmap() & ~SpaceCharmap(); }
constexpr Charmap PunctCharmap() { return GraphCharmap() & ~AlnumCharmap(); }
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_CHAR_MAP_H_
diff --git a/absl/strings/internal/charconv_bigint.cc b/absl/strings/internal/charconv_bigint.cc
index 58c909f4..66f33e72 100644
--- a/absl/strings/internal/charconv_bigint.cc
+++ b/absl/strings/internal/charconv_bigint.cc
@@ -19,7 +19,7 @@
#include <string>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
namespace {
@@ -158,12 +158,12 @@ const uint32_t* LargePowerOfFiveData(int i) {
int LargePowerOfFiveSize(int i) { return 2 * i; }
} // namespace
-const uint32_t kFiveToNth[14] = {
+ABSL_DLL const uint32_t kFiveToNth[14] = {
1, 5, 25, 125, 625, 3125, 15625,
78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125,
};
-const uint32_t kTenToNth[10] = {
+ABSL_DLL const uint32_t kTenToNth[10] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
};
@@ -355,5 +355,5 @@ template class BigUnsigned<4>;
template class BigUnsigned<84>;
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/charconv_bigint.h b/absl/strings/internal/charconv_bigint.h
index 5aef416b..999e9ae3 100644
--- a/absl/strings/internal/charconv_bigint.h
+++ b/absl/strings/internal/charconv_bigint.h
@@ -20,12 +20,13 @@
#include <iostream>
#include <string>
+#include "absl/base/config.h"
#include "absl/strings/ascii.h"
#include "absl/strings/internal/charconv_parse.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
// The largest power that 5 that can be raised to, and still fit in a uint32_t.
@@ -33,8 +34,9 @@ constexpr int kMaxSmallPowerOfFive = 13;
// The largest power that 10 that can be raised to, and still fit in a uint32_t.
constexpr int kMaxSmallPowerOfTen = 9;
-extern const uint32_t kFiveToNth[kMaxSmallPowerOfFive + 1];
-extern const uint32_t kTenToNth[kMaxSmallPowerOfTen + 1];
+ABSL_DLL extern const uint32_t
+ kFiveToNth[kMaxSmallPowerOfFive + 1];
+ABSL_DLL extern const uint32_t kTenToNth[kMaxSmallPowerOfTen + 1];
// Large, fixed-width unsigned integer.
//
@@ -415,7 +417,7 @@ extern template class BigUnsigned<4>;
extern template class BigUnsigned<84>;
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_
diff --git a/absl/strings/internal/charconv_bigint_test.cc b/absl/strings/internal/charconv_bigint_test.cc
index 590511d0..363bcb03 100644
--- a/absl/strings/internal/charconv_bigint_test.cc
+++ b/absl/strings/internal/charconv_bigint_test.cc
@@ -19,7 +19,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
TEST(BigUnsigned, ShiftLeft) {
@@ -201,5 +201,5 @@ TEST(BigUnsigned, TenToTheNth) {
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc
index 4dd4ecb3..d9a57a78 100644
--- a/absl/strings/internal/charconv_parse.cc
+++ b/absl/strings/internal/charconv_parse.cc
@@ -22,7 +22,7 @@
#include "absl/strings/internal/memutil.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
// ParseFloat<10> will read the first 19 significant digits of the mantissa.
@@ -254,6 +254,12 @@ std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits,
assert(max_digits * 4 <= std::numeric_limits<T>::digits);
}
const char* const original_begin = begin;
+
+ // Skip leading zeros, but only if *out is zero.
+ // They don't cause an overflow so we don't have to count them for
+ // `max_digits`.
+ while (!*out && end != begin && *begin == '0') ++begin;
+
T accumulator = *out;
const char* significant_digits_end =
(end - begin > max_digits) ? begin + max_digits : end;
@@ -494,5 +500,5 @@ template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
chars_format format_flags);
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/charconv_parse.h b/absl/strings/internal/charconv_parse.h
index ddfc87f8..505998b5 100644
--- a/absl/strings/internal/charconv_parse.h
+++ b/absl/strings/internal/charconv_parse.h
@@ -17,10 +17,11 @@
#include <cstdint>
+#include "absl/base/config.h"
#include "absl/strings/charconv.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
// Enum indicating whether a parsed float is a number or special value.
@@ -93,6 +94,6 @@ extern template ParsedFloat ParseFloat<16>(const char* begin, const char* end,
absl::chars_format format_flags);
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
new file mode 100644
index 00000000..5b5d1083
--- /dev/null
+++ b/absl/strings/internal/cord_internal.h
@@ -0,0 +1,151 @@
+// 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_INTERNAL_H_
+#define ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
+
+#include <atomic>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+#include "absl/meta/type_traits.h"
+#include "absl/strings/string_view.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// Wraps std::atomic for reference counting.
+class Refcount {
+ public:
+ Refcount() : count_{1} {}
+ ~Refcount() {}
+
+ // Increments the reference count by 1. Imposes no memory ordering.
+ inline void Increment() { count_.fetch_add(1, std::memory_order_relaxed); }
+
+ // Asserts that the current refcount is greater than 0. If the refcount is
+ // greater than 1, decrements the reference count by 1.
+ //
+ // Returns false if there are no references outstanding; true otherwise.
+ // Inserts barriers to ensure that state written before this method returns
+ // false will be visible to a thread that just observed this method returning
+ // false.
+ inline bool Decrement() {
+ int32_t refcount = count_.load(std::memory_order_acquire);
+ assert(refcount > 0);
+ return refcount != 1 && count_.fetch_sub(1, std::memory_order_acq_rel) != 1;
+ }
+
+ // Same as Decrement but expect that refcount is greater than 1.
+ inline bool DecrementExpectHighRefcount() {
+ int32_t refcount = count_.fetch_sub(1, std::memory_order_acq_rel);
+ assert(refcount > 0);
+ return refcount != 1;
+ }
+
+ // Returns the current reference count using acquire semantics.
+ inline int32_t Get() const { return count_.load(std::memory_order_acquire); }
+
+ // Returns whether the atomic integer is 1.
+ // If the reference count is used in the conventional way, a
+ // reference count of 1 implies that the current thread owns the
+ // reference and no other thread shares it.
+ // This call performs the test for a reference count of one, and
+ // performs the memory barrier needed for the owning thread
+ // to act on the object, knowing that it has exclusive access to the
+ // object.
+ inline bool IsOne() { return count_.load(std::memory_order_acquire) == 1; }
+
+ private:
+ std::atomic<int32_t> count_;
+};
+
+// The overhead of a vtable is too much for Cord, so we roll our own subclasses
+// using only a single byte to differentiate classes from each other - the "tag"
+// byte. Define the subclasses first so we can provide downcasting helper
+// functions in the base class.
+
+struct CordRepConcat;
+struct CordRepSubstring;
+struct CordRepExternal;
+
+struct CordRep {
+ // The following three fields have to be less than 32 bytes since
+ // that is the smallest supported flat node size.
+ // We use uint64_t for the length even in 32-bit binaries.
+ uint64_t length;
+ Refcount refcount;
+ // If tag < FLAT, it represents CordRepKind and indicates the type of node.
+ // Otherwise, the node type is CordRepFlat and the tag is the encoded size.
+ uint8_t tag;
+ char data[1]; // Starting point for flat array: MUST BE LAST FIELD of CordRep
+
+ inline CordRepConcat* concat();
+ inline const CordRepConcat* concat() const;
+ inline CordRepSubstring* substring();
+ inline const CordRepSubstring* substring() const;
+ inline CordRepExternal* external();
+ inline const CordRepExternal* external() const;
+};
+
+struct CordRepConcat : public CordRep {
+ CordRep* left;
+ CordRep* right;
+
+ uint8_t depth() const { return static_cast<uint8_t>(data[0]); }
+ void set_depth(uint8_t depth) { data[0] = static_cast<char>(depth); }
+};
+
+struct CordRepSubstring : public CordRep {
+ size_t start; // Starting offset of substring in child
+ CordRep* child;
+};
+
+// TODO(strel): replace the following logic (and related functions in cord.cc)
+// with container_internal::Layout.
+
+// Alignment requirement for CordRepExternal so that the type erased releaser
+// will be stored at a suitably aligned address.
+constexpr size_t ExternalRepAlignment() {
+#if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ return __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+ return alignof(max_align_t);
+#endif
+}
+
+// Type for function pointer that will invoke and destroy the type-erased
+// releaser function object. Accepts a pointer to the releaser and the
+// `string_view` that were passed in to `NewExternalRep` below. The return value
+// is the size of the `Releaser` type.
+using ExternalReleaserInvoker = size_t (*)(void*, absl::string_view);
+
+// External CordReps are allocated together with a type erased releaser. The
+// releaser is stored in the memory directly following the CordRepExternal.
+struct alignas(ExternalRepAlignment()) CordRepExternal : public CordRep {
+ const char* base;
+ // Pointer to function that knows how to call and destroy the releaser.
+ ExternalReleaserInvoker releaser_invoker;
+};
+
+// TODO(strel): look into removing, it doesn't seem like anything relies on this
+static_assert(sizeof(CordRepConcat) == sizeof(CordRepSubstring), "");
+
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+#endif // ABSL_STRINGS_INTERNAL_CORD_INTERNAL_H_
diff --git a/absl/strings/internal/escaping.cc b/absl/strings/internal/escaping.cc
new file mode 100644
index 00000000..c5271286
--- /dev/null
+++ b/absl/strings/internal/escaping.cc
@@ -0,0 +1,180 @@
+// Copyright 2020 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/strings/internal/escaping.h"
+
+#include "absl/base/internal/endian.h"
+#include "absl/base/internal/raw_logging.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace strings_internal {
+
+const char kBase64Chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) {
+ // Base64 encodes three bytes of input at a time. If the input is not
+ // divisible by three, we pad as appropriate.
+ //
+ // (from https://tools.ietf.org/html/rfc3548)
+ // Special processing is performed if fewer than 24 bits are available
+ // at the end of the data being encoded. A full encoding quantum is
+ // always completed at the end of a quantity. When fewer than 24 input
+ // bits are available in an input group, zero bits are added (on the
+ // right) to form an integral number of 6-bit groups. Padding at the
+ // end of the data is performed using the '=' character. Since all base
+ // 64 input is an integral number of octets, only the following cases
+ // can arise:
+
+ // Base64 encodes each three bytes of input into four bytes of output.
+ size_t len = (input_len / 3) * 4;
+
+ if (input_len % 3 == 0) {
+ // (from https://tools.ietf.org/html/rfc3548)
+ // (1) the final quantum of encoding input is an integral multiple of 24
+ // bits; here, the final unit of encoded output will be an integral
+ // multiple of 4 characters with no "=" padding,
+ } else if (input_len % 3 == 1) {
+ // (from https://tools.ietf.org/html/rfc3548)
+ // (2) the final quantum of encoding input is exactly 8 bits; here, the
+ // final unit of encoded output will be two characters followed by two
+ // "=" padding characters, or
+ len += 2;
+ if (do_padding) {
+ len += 2;
+ }
+ } else { // (input_len % 3 == 2)
+ // (from https://tools.ietf.org/html/rfc3548)
+ // (3) the final quantum of encoding input is exactly 16 bits; here, the
+ // final unit of encoded output will be three characters followed by one
+ // "=" padding character.
+ len += 3;
+ if (do_padding) {
+ len += 1;
+ }
+ }
+
+ assert(len >= input_len); // make sure we didn't overflow
+ return len;
+}
+
+size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest,
+ size_t szdest, const char* base64,
+ bool do_padding) {
+ static const char kPad64 = '=';
+
+ if (szsrc * 4 > szdest * 3) return 0;
+
+ char* cur_dest = dest;
+ const unsigned char* cur_src = src;
+
+ char* const limit_dest = dest + szdest;
+ const unsigned char* const limit_src = src + szsrc;
+
+ // Three bytes of data encodes to four characters of cyphertext.
+ // So we can pump through three-byte chunks atomically.
+ if (szsrc >= 3) { // "limit_src - 3" is UB if szsrc < 3.
+ while (cur_src < limit_src - 3) { // While we have >= 32 bits.
+ uint32_t in = absl::big_endian::Load32(cur_src) >> 8;
+
+ cur_dest[0] = base64[in >> 18];
+ in &= 0x3FFFF;
+ cur_dest[1] = base64[in >> 12];
+ in &= 0xFFF;
+ cur_dest[2] = base64[in >> 6];
+ in &= 0x3F;
+ cur_dest[3] = base64[in];
+
+ cur_dest += 4;
+ cur_src += 3;
+ }
+ }
+ // To save time, we didn't update szdest or szsrc in the loop. So do it now.
+ szdest = limit_dest - cur_dest;
+ szsrc = limit_src - cur_src;
+
+ /* now deal with the tail (<=3 bytes) */
+ switch (szsrc) {
+ case 0:
+ // Nothing left; nothing more to do.
+ break;
+ case 1: {
+ // One byte left: this encodes to two characters, and (optionally)
+ // two pad characters to round out the four-character cypherblock.
+ if (szdest < 2) return 0;
+ uint32_t in = cur_src[0];
+ cur_dest[0] = base64[in >> 2];
+ in &= 0x3;
+ cur_dest[1] = base64[in << 4];
+ cur_dest += 2;
+ szdest -= 2;
+ if (do_padding) {
+ if (szdest < 2) return 0;
+ cur_dest[0] = kPad64;
+ cur_dest[1] = kPad64;
+ cur_dest += 2;
+ szdest -= 2;
+ }
+ break;
+ }
+ case 2: {
+ // Two bytes left: this encodes to three characters, and (optionally)
+ // one pad character to round out the four-character cypherblock.
+ if (szdest < 3) return 0;
+ uint32_t in = absl::big_endian::Load16(cur_src);
+ cur_dest[0] = base64[in >> 10];
+ in &= 0x3FF;
+ cur_dest[1] = base64[in >> 4];
+ in &= 0x00F;
+ cur_dest[2] = base64[in << 2];
+ cur_dest += 3;
+ szdest -= 3;
+ if (do_padding) {
+ if (szdest < 1) return 0;
+ cur_dest[0] = kPad64;
+ cur_dest += 1;
+ szdest -= 1;
+ }
+ break;
+ }
+ case 3: {
+ // Three bytes left: same as in the big loop above. We can't do this in
+ // the loop because the loop above always reads 4 bytes, and the fourth
+ // byte is past the end of the input.
+ if (szdest < 4) return 0;
+ uint32_t in = (cur_src[0] << 16) + absl::big_endian::Load16(cur_src + 1);
+ cur_dest[0] = base64[in >> 18];
+ in &= 0x3FFFF;
+ cur_dest[1] = base64[in >> 12];
+ in &= 0xFFF;
+ cur_dest[2] = base64[in >> 6];
+ in &= 0x3F;
+ cur_dest[3] = base64[in];
+ cur_dest += 4;
+ szdest -= 4;
+ break;
+ }
+ default:
+ // Should not be reached: blocks of 4 bytes are handled
+ // in the while loop before this switch statement.
+ ABSL_RAW_LOG(FATAL, "Logic problem? szsrc = %zu", szsrc);
+ break;
+ }
+ return (cur_dest - dest);
+}
+
+} // namespace strings_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/strings/internal/escaping.h b/absl/strings/internal/escaping.h
new file mode 100644
index 00000000..6a9ce602
--- /dev/null
+++ b/absl/strings/internal/escaping.h
@@ -0,0 +1,58 @@
+// 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_ESCAPING_H_
+#define ABSL_STRINGS_INTERNAL_ESCAPING_H_
+
+#include <cassert>
+
+#include "absl/strings/internal/resize_uninitialized.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace strings_internal {
+
+ABSL_CONST_INIT extern const char kBase64Chars[];
+
+// Calculates how long a string will be when it is base64 encoded given its
+// length and whether or not the result should be padded.
+size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding);
+
+// Base64-encodes `src` using the alphabet provided in `base64` and writes the
+// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars
+// until its length is a multiple of 3. Returns the length of `dest`.
+size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest,
+ size_t szdest, const char* base64, bool do_padding);
+
+// Base64-encodes `src` using the alphabet provided in `base64` and writes the
+// result to `dest`. If `do_padding` is true, `dest` is padded with '=' chars
+// until its length is a multiple of 3.
+template <typename String>
+void Base64EscapeInternal(const unsigned char* src, size_t szsrc, String* dest,
+ bool do_padding, const char* base64_chars) {
+ const size_t calc_escaped_size =
+ CalculateBase64EscapedLenInternal(szsrc, do_padding);
+ STLStringResizeUninitialized(dest, calc_escaped_size);
+
+ const size_t escaped_len = Base64EscapeInternal(
+ src, szsrc, &(*dest)[0], dest->size(), base64_chars, do_padding);
+ assert(calc_escaped_size == escaped_len);
+ dest->erase(escaped_len);
+}
+
+} // namespace strings_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_ESCAPING_H_
diff --git a/absl/strings/internal/escaping_test_common.h b/absl/strings/internal/escaping_test_common.h
index ecd3aa35..7b18017a 100644
--- a/absl/strings/internal/escaping_test_common.h
+++ b/absl/strings/internal/escaping_test_common.h
@@ -22,7 +22,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
struct base64_testcase {
@@ -127,7 +127,7 @@ inline const std::array<base64_testcase, 5>& base64_strings() {
}
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_ESCAPING_TEST_COMMON_H_
diff --git a/absl/strings/internal/memutil.cc b/absl/strings/internal/memutil.cc
index 05251377..2519c688 100644
--- a/absl/strings/internal/memutil.cc
+++ b/absl/strings/internal/memutil.cc
@@ -17,7 +17,7 @@
#include <cstdlib>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
int memcasecmp(const char* s1, const char* s2, size_t len) {
@@ -108,5 +108,5 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
}
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/memutil.h b/absl/strings/internal/memutil.h
index 4efac989..9ad05358 100644
--- a/absl/strings/internal/memutil.h
+++ b/absl/strings/internal/memutil.h
@@ -69,7 +69,7 @@
#include "absl/strings/ascii.h" // for absl::ascii_tolower
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
inline char* memcat(char* dest, size_t destlen, const char* src,
@@ -142,7 +142,7 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle,
size_t neelen);
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_MEMUTIL_H_
diff --git a/absl/strings/internal/numbers_test_common.h b/absl/strings/internal/numbers_test_common.h
index 3f6965f2..1a1e50c4 100644
--- a/absl/strings/internal/numbers_test_common.h
+++ b/absl/strings/internal/numbers_test_common.h
@@ -23,8 +23,10 @@
#include <limits>
#include <string>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
template <typename IntType>
@@ -43,8 +45,9 @@ inline bool Itoa(IntType value, int base, std::string* destination) {
while (value != 0) {
const IntType next_value = value / base;
// Can't use std::abs here because of problems when IntType is unsigned.
- int remainder = value > next_value * base ? value - next_value * base
- : next_value * base - value;
+ int remainder =
+ static_cast<int>(value > next_value * base ? value - next_value * base
+ : next_value * base - value);
char c = remainder < 10 ? '0' + remainder : 'A' + remainder - 10;
destination->insert(0, 1, c);
value = next_value;
@@ -175,7 +178,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() {
}
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_NUMBERS_TEST_COMMON_H_
diff --git a/absl/strings/internal/ostringstream.cc b/absl/strings/internal/ostringstream.cc
index ce2dd6c7..05324c78 100644
--- a/absl/strings/internal/ostringstream.cc
+++ b/absl/strings/internal/ostringstream.cc
@@ -15,7 +15,7 @@
#include "absl/strings/internal/ostringstream.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
OStringStream::Buf::int_type OStringStream::overflow(int c) {
@@ -32,5 +32,5 @@ std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) {
}
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/ostringstream.h b/absl/strings/internal/ostringstream.h
index 2cf65133..d25d6047 100644
--- a/absl/strings/internal/ostringstream.h
+++ b/absl/strings/internal/ostringstream.h
@@ -23,7 +23,7 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
// The same as std::ostringstream but appends to a user-specified std::string,
@@ -83,7 +83,7 @@ class OStringStream : private std::basic_streambuf<char>, public std::ostream {
};
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_
diff --git a/absl/strings/internal/pow10_helper.cc b/absl/strings/internal/pow10_helper.cc
index 5c02ab8f..42e96c34 100644
--- a/absl/strings/internal/pow10_helper.cc
+++ b/absl/strings/internal/pow10_helper.cc
@@ -17,7 +17,7 @@
#include <cmath>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
namespace {
@@ -118,5 +118,5 @@ double Pow10(int exp) {
}
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/pow10_helper.h b/absl/strings/internal/pow10_helper.h
index c9a1b27f..c37c2c3f 100644
--- a/absl/strings/internal/pow10_helper.h
+++ b/absl/strings/internal/pow10_helper.h
@@ -22,8 +22,10 @@
#include <vector>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
// Computes the precise value of 10^exp. (I.e. the nearest representable
@@ -32,7 +34,7 @@ namespace strings_internal {
double Pow10(int exp);
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_POW10_HELPER_H_
diff --git a/absl/strings/internal/pow10_helper_test.cc b/absl/strings/internal/pow10_helper_test.cc
index 4a62a70d..a4ff76d3 100644
--- a/absl/strings/internal/pow10_helper_test.cc
+++ b/absl/strings/internal/pow10_helper_test.cc
@@ -20,7 +20,7 @@
#include "absl/strings/str_format.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
namespace {
@@ -118,5 +118,5 @@ TEST(Pow10HelperTest, Works) {
} // namespace
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h
index ab1d8684..e42628e3 100644
--- a/absl/strings/internal/resize_uninitialized.h
+++ b/absl/strings/internal/resize_uninitialized.h
@@ -25,7 +25,7 @@
#include "absl/meta/type_traits.h" // for void_t
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
// Is a subclass of true_type or false_type, depending on whether or not
@@ -36,8 +36,7 @@ struct ResizeUninitializedTraits {
static void Resize(string_type* s, size_t new_size) { s->resize(new_size); }
};
-// __resize_default_init is provided by libc++ >= 8.0 and by Google's internal
-// ::string implementation.
+// __resize_default_init is provided by libc++ >= 8.0
template <typename string_type>
struct ResizeUninitializedTraits<
string_type, absl::void_t<decltype(std::declval<string_type&>()
@@ -68,7 +67,7 @@ inline void STLStringResizeUninitialized(string_type* s, size_t new_size) {
}
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_
diff --git a/absl/strings/internal/resize_uninitialized_test.cc b/absl/strings/internal/resize_uninitialized_test.cc
index c5be0b12..0f8b3c2a 100644
--- a/absl/strings/internal/resize_uninitialized_test.cc
+++ b/absl/strings/internal/resize_uninitialized_test.cc
@@ -20,13 +20,27 @@ namespace {
int resize_call_count = 0;
+// A mock string class whose only purpose is to track how many times its
+// resize() method has been called.
struct resizable_string {
+ size_t size() const { return 0; }
+ char& operator[](size_t) {
+ static char c = '\0';
+ return c;
+ }
void resize(size_t) { resize_call_count += 1; }
};
int resize_default_init_call_count = 0;
+// A mock string class whose only purpose is to track how many times its
+// resize() and __resize_default_init() methods have been called.
struct resize_default_init_string {
+ size_t size() const { return 0; }
+ char& operator[](size_t) {
+ static char c = '\0';
+ return c;
+ }
void resize(size_t) { resize_call_count += 1; }
void __resize_default_init(size_t) { resize_default_init_call_count += 1; }
};
diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h
index af50be7c..6035ca45 100644
--- a/absl/strings/internal/stl_type_traits.h
+++ b/absl/strings/internal/stl_type_traits.h
@@ -40,7 +40,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
template <typename C, template <typename...> class T>
@@ -243,6 +243,6 @@ struct IsStrictlyBaseOfAndConvertibleToSTLContainer
IsConvertibleToSTLContainer<C>> {};
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index 667cc133..4d0604e0 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -14,7 +14,7 @@
#include "absl/strings/internal/str_format/float_conversion.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
@@ -33,6 +33,10 @@ void ReducePadding(size_t n, size_t *capacity) {
template <typename T>
struct MakeUnsigned : std::make_unsigned<T> {};
template <>
+struct MakeUnsigned<absl::int128> {
+ using type = absl::uint128;
+};
+template <>
struct MakeUnsigned<absl::uint128> {
using type = absl::uint128;
};
@@ -40,6 +44,8 @@ struct MakeUnsigned<absl::uint128> {
template <typename T>
struct IsSigned : std::is_signed<T> {};
template <>
+struct IsSigned<absl::int128> : std::true_type {};
+template <>
struct IsSigned<absl::uint128> : std::false_type {};
class ConvertedIntInfo {
@@ -82,7 +88,7 @@ class ConvertedIntInfo {
template <typename T>
void UnsignedToStringRight(T u, ConversionChar conv) {
char *p = end();
- switch (conv.radix()) {
+ switch (FormatConversionCharRadix(conv)) {
default:
case 10:
for (; u; u /= 10)
@@ -93,7 +99,7 @@ class ConvertedIntInfo {
*--p = static_cast<char>('0' + static_cast<size_t>(u % 8));
break;
case 16: {
- const char *digits = kDigit[conv.upper() ? 1 : 0];
+ const char *digits = kDigit[FormatConversionCharIsUpper(conv) ? 1 : 0];
for (; u; u /= 16) *--p = digits[static_cast<size_t>(u % 16)];
break;
}
@@ -115,21 +121,20 @@ class ConvertedIntInfo {
string_view BaseIndicator(const ConvertedIntInfo &info,
const ConversionSpec conv) {
bool alt = conv.flags().alt;
- int radix = conv.conv().radix();
- if (conv.conv().id() == ConversionChar::p)
- alt = true; // always show 0x for %p.
+ int radix = FormatConversionCharRadix(conv.conv());
+ if (conv.conv() == ConversionChar::p) alt = true; // always show 0x for %p.
// From the POSIX description of '#' flag:
// "For x or X conversion specifiers, a non-zero result shall have
// 0x (or 0X) prefixed to it."
if (alt && radix == 16 && !info.digits().empty()) {
- if (conv.conv().upper()) return "0X";
+ if (FormatConversionCharIsUpper(conv.conv())) return "0X";
return "0x";
}
return {};
}
string_view SignColumn(bool neg, const ConversionSpec conv) {
- if (conv.conv().is_signed()) {
+ if (FormatConversionCharIsSigned(conv.conv())) {
if (neg) return "-";
if (conv.flags().show_pos) return "+";
if (conv.flags().sign_col) return " ";
@@ -169,7 +174,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info,
if (!precision_specified)
precision = 1;
- if (conv.flags().alt && conv.conv().id() == ConversionChar::o) {
+ if (conv.flags().alt && conv.conv() == ConversionChar::o) {
// From POSIX description of the '#' (alt) flag:
// "For o conversion, it increases the precision (if necessary) to
// force the first digit of the result to be zero."
@@ -205,7 +210,7 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info,
template <typename T>
bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
ConvertedIntInfo info(v, conv.conv());
- if (conv.flags().basic && conv.conv().id() != ConversionChar::p) {
+ if (conv.flags().basic && (conv.conv() != ConversionChar::p)) {
if (info.is_neg()) sink->Append(1, '-');
if (info.digits().empty()) {
sink->Append(1, '0');
@@ -219,14 +224,13 @@ bool ConvertIntImplInner(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
template <typename T>
bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
- if (conv.conv().is_float()) {
+ if (FormatConversionCharIsFloat(conv.conv())) {
return FormatConvertImpl(static_cast<double>(v), conv, sink).value;
}
- if (conv.conv().id() == ConversionChar::c)
+ if (conv.conv() == ConversionChar::c)
return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink);
- if (!conv.conv().is_integral())
- return false;
- if (!conv.conv().is_signed() && IsSigned<T>::value) {
+ if (!FormatConversionCharIsIntegral(conv.conv())) return false;
+ if (!FormatConversionCharIsSigned(conv.conv()) && IsSigned<T>::value) {
using U = typename MakeUnsigned<T>::type;
return FormatConvertImpl(static_cast<U>(v), conv, sink).value;
}
@@ -235,13 +239,13 @@ bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
template <typename T>
bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) {
- return conv.conv().is_float() && ConvertFloatImpl(v, conv, sink);
+ return FormatConversionCharIsFloat(conv.conv()) &&
+ ConvertFloatImpl(v, conv, sink);
}
inline bool ConvertStringArg(string_view v, const ConversionSpec conv,
FormatSinkImpl *sink) {
- if (conv.conv().id() != ConversionChar::s)
- return false;
+ if (conv.conv() != ConversionChar::s) return false;
if (conv.flags().basic) {
sink->Append(v);
return true;
@@ -268,7 +272,7 @@ ConvertResult<Conv::s> FormatConvertImpl(string_view v,
ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
const ConversionSpec conv,
FormatSinkImpl *sink) {
- if (conv.conv().id() == ConversionChar::p)
+ if (conv.conv() == ConversionChar::p)
return {FormatConvertImpl(VoidPtr(v), conv, sink).value};
size_t len;
if (v == nullptr) {
@@ -276,7 +280,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
} else if (conv.precision() < 0) {
len = std::strlen(v);
} else {
- // If precision is set, we look for the null terminator on the valid range.
+ // If precision is set, we look for the NUL-terminator on the valid range.
len = std::find(v, v + conv.precision(), '\0') - v;
}
return {ConvertStringArg(string_view(v, len), conv, sink)};
@@ -285,8 +289,7 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v,
// ==================== Raw pointers ====================
ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv,
FormatSinkImpl *sink) {
- if (conv.conv().id() != ConversionChar::p)
- return {false};
+ if (conv.conv() != ConversionChar::p) return {false};
if (!v.value) {
sink->Append("(nil)");
return {true};
@@ -364,6 +367,11 @@ IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT
FormatSinkImpl *sink) {
return {ConvertIntArg(v, conv, sink)};
}
+IntegralConvertResult FormatConvertImpl(absl::int128 v,
+ const ConversionSpec conv,
+ FormatSinkImpl *sink) {
+ return {ConvertIntArg(v, conv, sink)};
+}
IntegralConvertResult FormatConvertImpl(absl::uint128 v,
const ConversionSpec conv,
FormatSinkImpl *sink) {
@@ -373,7 +381,8 @@ IntegralConvertResult FormatConvertImpl(absl::uint128 v,
ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_();
+
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index b6f708c6..7a937563 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -18,12 +18,10 @@
#include "absl/strings/internal/str_format/extension.h"
#include "absl/strings/string_view.h"
-class Cord;
-class CordReader;
-
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
+class Cord;
class FormatCountCapture;
class FormatSink;
@@ -68,12 +66,11 @@ ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v,
FormatSinkImpl* sink);
template <class AbslCord,
typename std::enable_if<
- std::is_same<AbslCord, ::Cord>::value>::type* = nullptr,
- class AbslCordReader = ::CordReader>
+ std::is_same<AbslCord, absl::Cord>::value>::type* = nullptr>
ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value,
ConversionSpec conv,
FormatSinkImpl* sink) {
- if (conv.conv().id() != ConversionChar::s) return {false};
+ if (conv.conv() != ConversionChar::s) return {false};
bool is_left = conv.flags().left;
size_t space_remaining = 0;
@@ -91,11 +88,17 @@ ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value,
if (space_remaining > 0 && !is_left) sink->Append(space_remaining, ' ');
- string_view piece;
- for (AbslCordReader reader(value);
- to_write > 0 && reader.ReadFragment(&piece); to_write -= piece.size()) {
- if (piece.size() > to_write) piece.remove_suffix(piece.size() - to_write);
+ for (string_view piece : value.Chunks()) {
+ if (piece.size() > to_write) {
+ piece.remove_suffix(piece.size() - to_write);
+ to_write = 0;
+ } else {
+ to_write -= piece.size();
+ }
sink->Append(piece);
+ if (to_write == 0) {
+ break;
+ }
}
if (space_remaining > 0 && is_left) sink->Append(space_remaining, ' ');
@@ -145,6 +148,8 @@ IntegralConvertResult FormatConvertImpl(long long v, // NOLINT
IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT
ConversionSpec conv,
FormatSinkImpl* sink);
+IntegralConvertResult FormatConvertImpl(int128 v, ConversionSpec conv,
+ FormatSinkImpl* sink);
IntegralConvertResult FormatConvertImpl(uint128 v, ConversionSpec conv,
FormatSinkImpl* sink);
template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0>
@@ -180,8 +185,7 @@ struct FormatCountCaptureHelper {
FormatSinkImpl* sink) {
const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v;
- if (conv.conv().id() != str_format_internal::ConversionChar::n)
- return {false};
+ if (conv.conv() != str_format_internal::ConversionChar::n) return {false};
*v2.p_ = static_cast<int>(sink->size());
return {true};
}
@@ -373,7 +377,7 @@ class FormatArgImpl {
template <typename T>
static bool Dispatch(Data arg, ConversionSpec spec, void* out) {
// A `none` conv indicates that we want the `int` conversion.
- if (ABSL_PREDICT_FALSE(spec.conv().id() == ConversionChar::none)) {
+ if (ABSL_PREDICT_FALSE(spec.conv() == ConversionChar::none)) {
return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(),
std::is_enum<T>());
}
@@ -409,6 +413,7 @@ class FormatArgImpl {
__VA_ARGS__); \
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */ \
__VA_ARGS__); \
+ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int128, __VA_ARGS__); \
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__); \
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__); \
ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__); \
@@ -419,8 +424,9 @@ class FormatArgImpl {
ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
+
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_
diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc
index c9c51951..8d30d8b8 100644
--- a/absl/strings/internal/str_format/arg_test.cc
+++ b/absl/strings/internal/str_format/arg_test.cc
@@ -14,7 +14,7 @@
#include "absl/strings/str_format.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
@@ -96,10 +96,10 @@ TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) {
std::string s;
FormatSinkImpl sink(&s);
ConversionSpec conv;
- conv.set_conv(ConversionChar::FromChar('s'));
- conv.set_flags(Flags());
- conv.set_width(-1);
- conv.set_precision(-1);
+ FormatConversionSpecImplFriend::SetConversionChar(ConversionChar::s, &conv);
+ FormatConversionSpecImplFriend::SetFlags(Flags(), &conv);
+ FormatConversionSpecImplFriend::SetWidth(-1, &conv);
+ FormatConversionSpecImplFriend::SetPrecision(-1, &conv);
EXPECT_TRUE(
FormatArgImplFriend::Convert(FormatArgImpl(kMyArray), conv, &sink));
sink.Flush();
@@ -109,5 +109,5 @@ const char kMyArray[] = "ABCDE";
} // namespace
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc
index 48a306d4..27522fdb 100644
--- a/absl/strings/internal/str_format/bind.cc
+++ b/absl/strings/internal/str_format/bind.cc
@@ -6,7 +6,7 @@
#include <string>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
@@ -66,19 +66,22 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound,
return false;
}
- bound->set_width(width);
- bound->set_precision(precision);
- bound->set_flags(unbound->flags);
- if (force_left)
- bound->set_left(true);
+ FormatConversionSpecImplFriend::SetWidth(width, bound);
+ FormatConversionSpecImplFriend::SetPrecision(precision, bound);
+
+ if (force_left) {
+ Flags flags = unbound->flags;
+ flags.left = true;
+ FormatConversionSpecImplFriend::SetFlags(flags, bound);
+ } else {
+ FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
+ }
} else {
- bound->set_flags(unbound->flags);
- bound->set_width(-1);
- bound->set_precision(-1);
+ FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
+ FormatConversionSpecImplFriend::SetWidth(-1, bound);
+ FormatConversionSpecImplFriend::SetPrecision(-1, bound);
}
-
- bound->set_length_mod(unbound->length_mod);
- bound->set_conv(unbound->conv);
+ FormatConversionSpecImplFriend::SetConversionChar(unbound->conv, bound);
bound->set_arg(arg);
return true;
}
@@ -140,10 +143,11 @@ class SummarizingConverter {
UntypedFormatSpecImpl spec("%d");
std::ostringstream ss;
- ss << "{" << Streamable(spec, {*bound.arg()}) << ":" << bound.flags();
+ ss << "{" << Streamable(spec, {*bound.arg()}) << ":"
+ << FormatConversionSpecImplFriend::FlagsToString(bound);
if (bound.width() >= 0) ss << bound.width();
if (bound.precision() >= 0) ss << "." << bound.precision();
- ss << bound.length_mod() << bound.conv() << "}";
+ ss << bound.conv() << "}";
Append(ss.str());
return true;
}
@@ -197,6 +201,15 @@ std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format,
return *out;
}
+std::string FormatPack(const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args) {
+ std::string out;
+ if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) {
+ out.clear();
+ }
+ return out;
+}
+
int FprintF(std::FILE* output, const UntypedFormatSpecImpl format,
absl::Span<const FormatArgImpl> args) {
FILERawSink sink(output);
@@ -228,5 +241,5 @@ int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format,
}
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h
index db79eb6c..cf41b197 100644
--- a/absl/strings/internal/str_format/bind.h
+++ b/absl/strings/internal/str_format/bind.h
@@ -13,7 +13,7 @@
#include "absl/types/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
class UntypedFormatSpec;
@@ -74,7 +74,7 @@ class FormatSpecTemplate
using Base = typename MakeDependent<UntypedFormatSpec, Args...>::type;
public:
-#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
// Honeypot overload for when the std::string is not constexpr.
// We use the 'unavailable' attribute to give a better compiler error than
@@ -122,8 +122,8 @@ class FormatSpecTemplate
#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
template <Conv... C, typename = typename std::enable_if<
- sizeof...(C) == sizeof...(Args) &&
- AllOf(Contains(ArgumentToConv<Args>(),
+ AllOf(sizeof...(C) == sizeof...(Args),
+ Contains(ArgumentToConv<Args>(),
C)...)>::type>
FormatSpecTemplate(const ExtendedParsedFormat<C...>& pc) // NOLINT
: Base(&pc) {}
@@ -179,12 +179,8 @@ bool FormatUntyped(FormatRawSinkImpl raw_sink,
std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format,
absl::Span<const FormatArgImpl> args);
-inline std::string FormatPack(const UntypedFormatSpecImpl format,
- absl::Span<const FormatArgImpl> args) {
- std::string out;
- AppendPack(&out, format, args);
- return out;
-}
+std::string FormatPack(const UntypedFormatSpecImpl format,
+ absl::Span<const FormatArgImpl> args);
int FprintF(std::FILE* output, UntypedFormatSpecImpl format,
absl::Span<const FormatArgImpl> args);
@@ -207,7 +203,7 @@ class StreamedWrapper {
};
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_
diff --git a/absl/strings/internal/str_format/bind_test.cc b/absl/strings/internal/str_format/bind_test.cc
index 8bccc92a..64790a85 100644
--- a/absl/strings/internal/str_format/bind_test.cc
+++ b/absl/strings/internal/str_format/bind_test.cc
@@ -6,7 +6,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
@@ -113,18 +113,18 @@ TEST_F(FormatBindTest, FormatPack) {
FormatArgImpl(ia[2]), FormatArgImpl(ia[3]),
FormatArgImpl(ia[4])};
const Expectation kExpect[] = {
- {__LINE__, "a%4db%dc", "a{10:4d}b{20:d}c"},
- {__LINE__, "a%.4db%dc", "a{10:.4d}b{20:d}c"},
- {__LINE__, "a%4.5db%dc", "a{10:4.5d}b{20:d}c"},
- {__LINE__, "a%db%4.5dc", "a{10:d}b{20:4.5d}c"},
- {__LINE__, "a%db%*.*dc", "a{10:d}b{40:20.30d}c"},
- {__LINE__, "a%.*fb", "a{20:.10f}b"},
- {__LINE__, "a%1$db%2$*3$.*4$dc", "a{10:d}b{20:30.40d}c"},
- {__LINE__, "a%4$db%3$*2$.*1$dc", "a{40:d}b{30:20.10d}c"},
- {__LINE__, "a%04ldb", "a{10:04ld}b"},
- {__LINE__, "a%-#04lldb", "a{10:-#04lld}b"},
- {__LINE__, "a%1$*5$db", "a{10:-10d}b"},
- {__LINE__, "a%1$.*5$db", "a{10:d}b"},
+ {__LINE__, "a%4db%dc", "a{10:4d}b{20:d}c"},
+ {__LINE__, "a%.4db%dc", "a{10:.4d}b{20:d}c"},
+ {__LINE__, "a%4.5db%dc", "a{10:4.5d}b{20:d}c"},
+ {__LINE__, "a%db%4.5dc", "a{10:d}b{20:4.5d}c"},
+ {__LINE__, "a%db%*.*dc", "a{10:d}b{40:20.30d}c"},
+ {__LINE__, "a%.*fb", "a{20:.10f}b"},
+ {__LINE__, "a%1$db%2$*3$.*4$dc", "a{10:d}b{20:30.40d}c"},
+ {__LINE__, "a%4$db%3$*2$.*1$dc", "a{40:d}b{30:20.10d}c"},
+ {__LINE__, "a%04ldb", "a{10:04d}b"},
+ {__LINE__, "a%-#04lldb", "a{10:-#04d}b"},
+ {__LINE__, "a%1$*5$db", "a{10:-10d}b"},
+ {__LINE__, "a%1$.*5$db", "a{10:d}b"},
};
for (const Expectation &e : kExpect) {
absl::string_view fmt = e.fmt;
@@ -139,5 +139,5 @@ TEST_F(FormatBindTest, FormatPack) {
} // namespace
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h
index 262887cd..8993a79b 100644
--- a/absl/strings/internal/str_format/checker.h
+++ b/absl/strings/internal/str_format/checker.h
@@ -1,21 +1,20 @@
#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
+#include "absl/base/attributes.h"
#include "absl/strings/internal/str_format/arg.h"
#include "absl/strings/internal/str_format/extension.h"
// Compile time check support for entry points.
#ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
-#if defined(__clang__) && !defined(__native_client__)
-#if __has_attribute(enable_if)
+#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__)
#define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1
-#endif // __has_attribute(enable_if)
-#endif // defined(__clang__) && !defined(__native_client__)
+#endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__)
#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
constexpr bool AllOf() { return true; }
@@ -32,7 +31,7 @@ constexpr Conv ArgumentToConv() {
std::declval<FormatSinkImpl*>()))::kConv;
}
-#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
constexpr bool ContainsChar(const char* chars, char c) {
return *chars == c || (*chars && ContainsChar(chars + 1, c));
@@ -321,7 +320,7 @@ constexpr bool ValidFormatImpl(string_view format) {
#endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_
diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc
index 2aa3b128..ea2a7681 100644
--- a/absl/strings/internal/str_format/checker_test.cc
+++ b/absl/strings/internal/str_format/checker_test.cc
@@ -5,7 +5,7 @@
#include "absl/strings/str_format.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
@@ -13,7 +13,7 @@ std::string ConvToString(Conv conv) {
std::string out;
#define CONV_SET_CASE(c) \
if (Contains(conv, Conv::c)) out += #c;
- ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
+ ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
#undef CONV_SET_CASE
if (Contains(conv, Conv::star)) out += "*";
return out;
@@ -36,7 +36,7 @@ TEST(StrFormatChecker, ArgumentToConv) {
EXPECT_EQ(ConvToString(conv), "p");
}
-#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
struct Case {
bool result;
@@ -148,5 +148,5 @@ TEST(StrFormatChecker, LongFormat) {
} // namespace
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 751bfc34..cbcd7caf 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -5,11 +5,13 @@
#include <cmath>
#include <string>
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/internal/raw_logging.h"
#include "absl/strings/internal/str_format/bind.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
@@ -152,18 +154,26 @@ TEST_F(FormatConvertTest, StringPrecision) {
UntypedFormatSpecImpl format("%.1s");
EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)}));
- // We cap at the nul terminator.
+ // We cap at the NUL-terminator.
p = "ABC";
UntypedFormatSpecImpl format2("%.10s");
EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)}));
}
+// Pointer formatting is implementation defined. This checks that the argument
+// can be matched to `ptr`.
+MATCHER_P(MatchesPointerString, ptr, "") {
+ if (ptr == nullptr && arg == "(nil)") {
+ return true;
+ }
+ void* parsed = nullptr;
+ if (sscanf(arg.c_str(), "%p", &parsed) != 1) {
+ ABSL_RAW_LOG(FATAL, "Could not parse %s", arg.c_str());
+ }
+ return ptr == parsed;
+}
+
TEST_F(FormatConvertTest, Pointer) {
-#ifdef _MSC_VER
- // MSVC's printf implementation prints pointers differently. We can't easily
- // compare our implementation to theirs.
- return;
-#endif
static int x = 0;
const int *xp = &x;
char c = 'h';
@@ -174,48 +184,62 @@ TEST_F(FormatConvertTest, Pointer) {
using VoidF = void (*)();
VoidF fp = [] {}, fnil = nullptr;
volatile char vc;
- volatile char* vcp = &vc;
- volatile char* vcnil = nullptr;
- const FormatArgImpl args[] = {
+ volatile char *vcp = &vc;
+ volatile char *vcnil = nullptr;
+ const FormatArgImpl args_array[] = {
FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(inil),
FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp),
FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil),
};
- struct Expectation {
- std::string out;
- const char *fmt;
- };
- const Expectation kExpect[] = {
- {StrPrint("%p", &x), "%p"},
- {StrPrint("%20p", &x), "%20p"},
- {StrPrint("%.1p", &x), "%.1p"},
- {StrPrint("%.20p", &x), "%.20p"},
- {StrPrint("%30.20p", &x), "%30.20p"},
-
- {StrPrint("%-p", &x), "%-p"},
- {StrPrint("%-20p", &x), "%-20p"},
- {StrPrint("%-.1p", &x), "%-.1p"},
- {StrPrint("%.20p", &x), "%.20p"},
- {StrPrint("%-30.20p", &x), "%-30.20p"},
-
- {StrPrint("%p", cp), "%2$p"}, // const char*
- {"(nil)", "%3$p"}, // null const char *
- {"(nil)", "%4$p"}, // null const int *
- {StrPrint("%p", mcp), "%5$p"}, // nonconst char*
-
- {StrPrint("%p", fp), "%6$p"}, // function pointer
- {StrPrint("%p", vcp), "%8$p"}, // function pointer
-
-#ifndef __APPLE__
- // Apple's printf differs here (0x0 vs. nil)
- {StrPrint("%p", fnil), "%7$p"}, // null function pointer
- {StrPrint("%p", vcnil), "%9$p"}, // null function pointer
-#endif
- };
- for (const Expectation &e : kExpect) {
- UntypedFormatSpecImpl format(e.fmt);
- EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args))) << e.fmt;
- }
+ auto args = absl::MakeConstSpan(args_array);
+
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%p"), args),
+ MatchesPointerString(&x));
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%20p"), args),
+ MatchesPointerString(&x));
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.1p"), args),
+ MatchesPointerString(&x));
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
+ MatchesPointerString(&x));
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%30.20p"), args),
+ MatchesPointerString(&x));
+
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-p"), args),
+ MatchesPointerString(&x));
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-20p"), args),
+ MatchesPointerString(&x));
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-.1p"), args),
+ MatchesPointerString(&x));
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args),
+ MatchesPointerString(&x));
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args),
+ MatchesPointerString(&x));
+
+ // const char*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args),
+ MatchesPointerString(cp));
+ // null const int*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args),
+ MatchesPointerString(nullptr));
+ // null const char*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args),
+ MatchesPointerString(nullptr));
+ // nonconst char*
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args),
+ MatchesPointerString(mcp));
+
+ // function pointers
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args),
+ MatchesPointerString(reinterpret_cast<const void*>(fp)));
+ EXPECT_THAT(
+ FormatPack(UntypedFormatSpecImpl("%8$p"), args),
+ MatchesPointerString(reinterpret_cast<volatile const void *>(vcp)));
+
+ // null function pointers
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args),
+ MatchesPointerString(nullptr));
+ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args),
+ MatchesPointerString(nullptr));
}
struct Cardinal {
@@ -367,7 +391,6 @@ typedef ::testing::Types<
AllIntTypes;
INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes,
TypedFormatConvertTest, AllIntTypes);
-
TEST_F(FormatConvertTest, VectorBool) {
// Make sure vector<bool>'s values behave as bools.
std::vector<bool> v = {true, false};
@@ -379,6 +402,42 @@ TEST_F(FormatConvertTest, VectorBool) {
FormatArgImpl(cv[0]), FormatArgImpl(cv[1])})));
}
+
+TEST_F(FormatConvertTest, Int128) {
+ absl::int128 positive = static_cast<absl::int128>(0x1234567890abcdef) * 1979;
+ absl::int128 negative = -positive;
+ absl::int128 max = absl::Int128Max(), min = absl::Int128Min();
+ const FormatArgImpl args[] = {FormatArgImpl(positive),
+ FormatArgImpl(negative), FormatArgImpl(max),
+ FormatArgImpl(min)};
+
+ struct Case {
+ const char* format;
+ const char* expected;
+ } cases[] = {
+ {"%1$d", "2595989796776606496405"},
+ {"%1$30d", " 2595989796776606496405"},
+ {"%1$-30d", "2595989796776606496405 "},
+ {"%1$u", "2595989796776606496405"},
+ {"%1$x", "8cba9876066020f695"},
+ {"%2$d", "-2595989796776606496405"},
+ {"%2$30d", " -2595989796776606496405"},
+ {"%2$-30d", "-2595989796776606496405 "},
+ {"%2$u", "340282366920938460867384810655161715051"},
+ {"%2$x", "ffffffffffffff73456789f99fdf096b"},
+ {"%3$d", "170141183460469231731687303715884105727"},
+ {"%3$u", "170141183460469231731687303715884105727"},
+ {"%3$x", "7fffffffffffffffffffffffffffffff"},
+ {"%4$d", "-170141183460469231731687303715884105728"},
+ {"%4$x", "80000000000000000000000000000000"},
+ };
+
+ for (auto c : cases) {
+ UntypedFormatSpecImpl format(c.format);
+ EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args)));
+ }
+}
+
TEST_F(FormatConvertTest, Uint128) {
absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979;
absl::uint128 max = absl::Uint128Max();
@@ -588,5 +647,5 @@ TEST_F(FormatConvertTest, ExpectedFailures) {
} // namespace
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc
index 1a0c4172..2e5bc2ce 100644
--- a/absl/strings/internal/str_format/extension.cc
+++ b/absl/strings/internal/str_format/extension.cc
@@ -20,39 +20,8 @@
#include <string>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
-namespace {
-// clang-format off
-#define ABSL_LENGTH_MODS_EXPAND_ \
- X_VAL(h) X_SEP \
- X_VAL(hh) X_SEP \
- X_VAL(l) X_SEP \
- X_VAL(ll) X_SEP \
- X_VAL(L) X_SEP \
- X_VAL(j) X_SEP \
- X_VAL(z) X_SEP \
- X_VAL(t) X_SEP \
- X_VAL(q)
-// clang-format on
-} // namespace
-
-const LengthMod::Spec LengthMod::kSpecs[] = {
-#define X_VAL(id) { LengthMod::id, #id, strlen(#id) }
-#define X_SEP ,
- ABSL_LENGTH_MODS_EXPAND_, {LengthMod::none, "", 0}
-#undef X_VAL
-#undef X_SEP
-};
-
-const ConversionChar::Spec ConversionChar::kSpecs[] = {
-#define X_VAL(id) { ConversionChar::id, #id[0] }
-#define X_SEP ,
- ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP),
- {ConversionChar::none, '\0'},
-#undef X_VAL
-#undef X_SEP
-};
std::string Flags::ToString() const {
std::string s;
@@ -64,10 +33,6 @@ std::string Flags::ToString() const {
return s;
}
-const size_t LengthMod::kNumValues;
-
-const size_t ConversionChar::kNumValues;
-
bool FormatSinkImpl::PutPaddedString(string_view v, int w, int p, bool l) {
size_t space_remaining = 0;
if (w >= 0) space_remaining = w;
@@ -82,5 +47,5 @@ bool FormatSinkImpl::PutPaddedString(string_view v, int w, int p, bool l) {
}
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index 4fbe4a06..d1665753 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -17,19 +17,18 @@
#define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
#include <limits.h>
+
#include <cstddef>
#include <cstring>
#include <ostream>
+#include "absl/base/config.h"
#include "absl/base/port.h"
#include "absl/strings/internal/str_format/output.h"
#include "absl/strings/string_view.h"
-class Cord;
-
namespace absl {
-inline namespace lts_2019_08_08 {
-
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
class FormatRawSinkImpl {
@@ -137,56 +136,8 @@ struct Flags {
}
};
-struct LengthMod {
- public:
- enum Id : uint8_t {
- h, hh, l, ll, L, j, z, t, q, none
- };
- static const size_t kNumValues = none + 1;
-
- LengthMod() : id_(none) {}
-
- // Index into the opaque array of LengthMod enums.
- // Requires: i < kNumValues
- static LengthMod FromIndex(size_t i) {
- return LengthMod(kSpecs[i].value);
- }
-
- static LengthMod FromId(Id id) { return LengthMod(id); }
-
- // The length modifier std::string associated with a specified LengthMod.
- string_view name() const {
- const Spec& spec = kSpecs[id_];
- return {spec.name, spec.name_length};
- }
-
- Id id() const { return id_; }
-
- friend bool operator==(const LengthMod& a, const LengthMod& b) {
- return a.id() == b.id();
- }
- friend bool operator!=(const LengthMod& a, const LengthMod& b) {
- return !(a == b);
- }
- friend std::ostream& operator<<(std::ostream& os, const LengthMod& v) {
- return os << v.name();
- }
-
- private:
- struct Spec {
- Id value;
- const char *name;
- size_t name_length;
- };
- static const Spec kSpecs[];
-
- explicit LengthMod(Id id) : id_(id) {}
-
- Id id_;
-};
-
// clang-format off
-#define ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \
+#define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \
/* text */ \
X_VAL(c) X_SEP X_VAL(C) X_SEP X_VAL(s) X_SEP X_VAL(S) X_SEP \
/* ints */ \
@@ -197,121 +148,135 @@ struct LengthMod {
X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \
/* misc */ \
X_VAL(n) X_SEP X_VAL(p)
-// clang-format on
-struct ConversionChar {
- public:
- enum Id : uint8_t {
+enum class FormatConversionChar : uint8_t {
c, C, s, S, // text
d, i, o, u, x, X, // int
f, F, e, E, g, G, a, A, // float
n, p, // misc
- none
- };
- static const size_t kNumValues = none + 1;
-
- ConversionChar() : id_(none) {}
-
- public:
- // Index into the opaque array of ConversionChar enums.
- // Requires: i < kNumValues
- static ConversionChar FromIndex(size_t i) {
- return ConversionChar(kSpecs[i].value);
- }
+ kNone,
+ none = kNone
+};
+// clang-format on
- static ConversionChar FromChar(char c) {
- ConversionChar::Id out_id = ConversionChar::none;
- switch (c) {
-#define X_VAL(id) \
- case #id[0]: \
- out_id = ConversionChar::id; \
- break;
- ABSL_CONVERSION_CHARS_EXPAND_(X_VAL, )
-#undef X_VAL
- default:
- break;
- }
- return ConversionChar(out_id);
+inline FormatConversionChar FormatConversionCharFromChar(char c) {
+ switch (c) {
+#define ABSL_INTERNAL_X_VAL(id) \
+ case #id[0]: \
+ return FormatConversionChar::id;
+ ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
+#undef ABSL_INTERNAL_X_VAL
}
+ return FormatConversionChar::kNone;
+}
- static ConversionChar FromId(Id id) { return ConversionChar(id); }
- Id id() const { return id_; }
-
- int radix() const {
- switch (id()) {
- case x: case X: case a: case A: case p: return 16;
- case o: return 8;
- default: return 10;
- }
+inline int FormatConversionCharRadix(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::x:
+ case FormatConversionChar::X:
+ case FormatConversionChar::a:
+ case FormatConversionChar::A:
+ case FormatConversionChar::p:
+ return 16;
+ case FormatConversionChar::o:
+ return 8;
+ default:
+ return 10;
}
+}
- bool upper() const {
- switch (id()) {
- case X: case F: case E: case G: case A: return true;
- default: return false;
- }
+inline bool FormatConversionCharIsUpper(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::X:
+ case FormatConversionChar::F:
+ case FormatConversionChar::E:
+ case FormatConversionChar::G:
+ case FormatConversionChar::A:
+ return true;
+ default:
+ return false;
}
+}
- bool is_signed() const {
- switch (id()) {
- case d: case i: return true;
- default: return false;
- }
+inline bool FormatConversionCharIsSigned(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::d:
+ case FormatConversionChar::i:
+ return true;
+ default:
+ return false;
}
+}
- bool is_integral() const {
- switch (id()) {
- case d: case i: case u: case o: case x: case X:
- return true;
- default: return false;
- }
+inline bool FormatConversionCharIsIntegral(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::d:
+ case FormatConversionChar::i:
+ case FormatConversionChar::u:
+ case FormatConversionChar::o:
+ case FormatConversionChar::x:
+ case FormatConversionChar::X:
+ return true;
+ default:
+ return false;
}
+}
- bool is_float() const {
- switch (id()) {
- case a: case e: case f: case g: case A: case E: case F: case G:
- return true;
- default: return false;
- }
+inline bool FormatConversionCharIsFloat(FormatConversionChar c) {
+ switch (c) {
+ case FormatConversionChar::a:
+ case FormatConversionChar::e:
+ case FormatConversionChar::f:
+ case FormatConversionChar::g:
+ case FormatConversionChar::A:
+ case FormatConversionChar::E:
+ case FormatConversionChar::F:
+ case FormatConversionChar::G:
+ return true;
+ default:
+ return false;
}
+}
- bool IsValid() const { return id() != none; }
-
- // The associated char.
- char Char() const { return kSpecs[id_].name; }
-
- friend bool operator==(const ConversionChar& a, const ConversionChar& b) {
- return a.id() == b.id();
- }
- friend bool operator!=(const ConversionChar& a, const ConversionChar& b) {
- return !(a == b);
+inline char FormatConversionCharToChar(FormatConversionChar c) {
+ switch (c) {
+#define ABSL_INTERNAL_X_VAL(e) \
+ case FormatConversionChar::e: \
+ return #e[0];
+#define ABSL_INTERNAL_X_SEP
+ ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL,
+ ABSL_INTERNAL_X_SEP)
+ case FormatConversionChar::kNone:
+ return '\0';
+#undef ABSL_INTERNAL_X_VAL
+#undef ABSL_INTERNAL_X_SEP
}
- friend std::ostream& operator<<(std::ostream& os, const ConversionChar& v) {
- char c = v.Char();
- if (!c) c = '?';
- return os << c;
- }
-
- private:
- struct Spec {
- Id value;
- char name;
- };
- static const Spec kSpecs[];
+ return '\0';
+}
- explicit ConversionChar(Id id) : id_(id) {}
+// The associated char.
+inline std::ostream& operator<<(std::ostream& os, FormatConversionChar v) {
+ char c = FormatConversionCharToChar(v);
+ if (!c) c = '?';
+ return os << c;
+}
- Id id_;
-};
+struct FormatConversionSpecImplFriend;
-class ConversionSpec {
+class FormatConversionSpec {
public:
- Flags flags() const { return flags_; }
- LengthMod length_mod() const { return length_mod_; }
- ConversionChar conv() const {
+ // Width and precison are not specified, no flags are set.
+ bool is_basic() const { return flags_.basic; }
+ bool has_left_flag() const { return flags_.left; }
+ bool has_show_pos_flag() const { return flags_.show_pos; }
+ bool has_sign_col_flag() const { return flags_.sign_col; }
+ bool has_alt_flag() const { return flags_.alt; }
+ bool has_zero_flag() const { return flags_.zero; }
+
+ FormatConversionChar conversion_char() const {
// Keep this field first in the struct . It generates better code when
// accessing it when ConversionSpec is passed by value in registers.
- static_assert(offsetof(ConversionSpec, conv_) == 0, "");
+ static_assert(offsetof(FormatConversionSpec, conv_) == 0, "");
return conv_;
}
@@ -322,46 +287,71 @@ class ConversionSpec {
// negative value.
int precision() const { return precision_; }
- void set_flags(Flags f) { flags_ = f; }
- void set_length_mod(LengthMod lm) { length_mod_ = lm; }
- void set_conv(ConversionChar c) { conv_ = c; }
- void set_width(int w) { width_ = w; }
- void set_precision(int p) { precision_ = p; }
- void set_left(bool b) { flags_.left = b; }
+ // Deprecated (use has_x_flag() instead).
+ Flags flags() const { return flags_; }
+ // Deprecated
+ FormatConversionChar conv() const { return conversion_char(); }
private:
- ConversionChar conv_;
+ friend struct str_format_internal::FormatConversionSpecImplFriend;
+ FormatConversionChar conv_ = FormatConversionChar::kNone;
Flags flags_;
- LengthMod length_mod_;
int width_;
int precision_;
};
-constexpr uint64_t ConversionCharToConvValue(char conv) {
+struct FormatConversionSpecImplFriend final {
+ static void SetFlags(Flags f, FormatConversionSpec* conv) {
+ conv->flags_ = f;
+ }
+ static void SetConversionChar(FormatConversionChar c,
+ FormatConversionSpec* conv) {
+ conv->conv_ = c;
+ }
+ static void SetWidth(int w, FormatConversionSpec* conv) { conv->width_ = w; }
+ static void SetPrecision(int p, FormatConversionSpec* conv) {
+ conv->precision_ = p;
+ }
+ static std::string FlagsToString(const FormatConversionSpec& spec) {
+ return spec.flags_.ToString();
+ }
+};
+
+constexpr uint64_t FormatConversionCharToConvValue(char conv) {
return
-#define CONV_SET_CASE(c) \
- conv == #c[0] ? (uint64_t{1} << (1 + ConversionChar::Id::c)):
- ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
-#undef CONV_SET_CASE
+#define ABSL_INTERNAL_CHAR_SET_CASE(c) \
+ conv == #c[0] \
+ ? (uint64_t{1} << (1 + static_cast<uint8_t>(FormatConversionChar::c))) \
+ :
+ ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
+#undef ABSL_INTERNAL_CHAR_SET_CASE
conv == '*'
? 1
: 0;
}
-enum class Conv : uint64_t {
-#define CONV_SET_CASE(c) c = ConversionCharToConvValue(#c[0]),
- ABSL_CONVERSION_CHARS_EXPAND_(CONV_SET_CASE, )
-#undef CONV_SET_CASE
+enum class FormatConversionCharSet : uint64_t {
+#define ABSL_INTERNAL_CHAR_SET_CASE(c) \
+ c = FormatConversionCharToConvValue(#c[0]),
+ ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
+#undef ABSL_INTERNAL_CHAR_SET_CASE
// Used for width/precision '*' specification.
- star = ConversionCharToConvValue('*'),
-
+ kStar = FormatConversionCharToConvValue('*'),
// Some predefined values:
- integral = d | i | u | o | x | X,
- floating = a | e | f | g | A | E | F | G,
- numeric = integral | floating,
- string = s,
- pointer = p
+ kIntegral = d | i | u | o | x | X,
+ kFloating = a | e | f | g | A | E | F | G,
+ kNumeric = kIntegral | kFloating,
+ kString = s,
+ kPointer = p,
+
+ // The following are deprecated
+ star = kStar,
+ integral = kIntegral,
+ floating = kFloating,
+ numeric = kNumeric,
+ string = kString,
+ pointer = kPointer
};
// Type safe OR operator.
@@ -369,45 +359,57 @@ enum class Conv : uint64_t {
// 1. operator| on enums makes them decay to integers and the result is an
// integer. We need the result to stay as an enum.
// 2. We use "enum class" which would not work even if we accepted the decay.
-constexpr Conv operator|(Conv a, Conv b) {
- return Conv(static_cast<uint64_t>(a) | static_cast<uint64_t>(b));
+constexpr FormatConversionCharSet operator|(FormatConversionCharSet a,
+ FormatConversionCharSet b) {
+ return FormatConversionCharSet(static_cast<uint64_t>(a) |
+ static_cast<uint64_t>(b));
}
// Get a conversion with a single character in it.
-constexpr Conv ConversionCharToConv(char c) {
- return Conv(ConversionCharToConvValue(c));
+constexpr FormatConversionCharSet ConversionCharToConv(char c) {
+ return FormatConversionCharSet(FormatConversionCharToConvValue(c));
}
// Checks whether `c` exists in `set`.
-constexpr bool Contains(Conv set, char c) {
- return (static_cast<uint64_t>(set) & ConversionCharToConvValue(c)) != 0;
+constexpr bool Contains(FormatConversionCharSet set, char c) {
+ return (static_cast<uint64_t>(set) & FormatConversionCharToConvValue(c)) != 0;
}
// Checks whether all the characters in `c` are contained in `set`
-constexpr bool Contains(Conv set, Conv c) {
+constexpr bool Contains(FormatConversionCharSet set,
+ FormatConversionCharSet c) {
return (static_cast<uint64_t>(set) & static_cast<uint64_t>(c)) ==
static_cast<uint64_t>(c);
}
// Return type of the AbslFormatConvert() functions.
-// The Conv template parameter is used to inform the framework of what
-// conversion characters are supported by that AbslFormatConvert routine.
-template <Conv C>
-struct ConvertResult {
- static constexpr Conv kConv = C;
+// The FormatConversionCharSet template parameter is used to inform the
+// framework of what conversion characters are supported by that
+// AbslFormatConvert routine.
+template <FormatConversionCharSet C>
+struct FormatConvertResult {
+ static constexpr FormatConversionCharSet kConv = C;
bool value;
};
-template <Conv C>
-constexpr Conv ConvertResult<C>::kConv;
+
+template <FormatConversionCharSet C>
+constexpr FormatConversionCharSet FormatConvertResult<C>::kConv;
// Return capacity - used, clipped to a minimum of 0.
inline size_t Excess(size_t used, size_t capacity) {
return used < capacity ? capacity - used : 0;
}
+// Type alias for use during migration.
+using ConversionChar = FormatConversionChar;
+using ConversionSpec = FormatConversionSpec;
+using Conv = FormatConversionCharSet;
+template <FormatConversionCharSet C>
+using ConvertResult = FormatConvertResult<C>;
+
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc
index a0484feb..d4c647c3 100644
--- a/absl/strings/internal/str_format/float_conversion.cc
+++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -9,7 +9,7 @@
#include "absl/base/config.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
@@ -28,12 +28,12 @@ bool FallbackToSnprintf(const Float v, const ConversionSpec &conv,
{
char *fp = fmt;
*fp++ = '%';
- fp = CopyStringTo(conv.flags().ToString(), fp);
+ fp = CopyStringTo(FormatConversionSpecImplFriend::FlagsToString(conv), fp);
fp = CopyStringTo("*.*", fp);
if (std::is_same<long double, Float>()) {
*fp++ = 'L';
}
- *fp++ = conv.conv().Char();
+ *fp++ = FormatConversionCharToChar(conv.conv());
*fp = 0;
assert(fp < fmt + sizeof(fmt));
}
@@ -100,9 +100,11 @@ bool ConvertNonNumericFloats(char sign_char, Float v,
char text[4], *ptr = text;
if (sign_char) *ptr++ = sign_char;
if (std::isnan(v)) {
- ptr = std::copy_n(conv.conv().upper() ? "NAN" : "nan", 3, ptr);
+ ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "NAN" : "nan",
+ 3, ptr);
} else if (std::isinf(v)) {
- ptr = std::copy_n(conv.conv().upper() ? "INF" : "inf", 3, ptr);
+ ptr = std::copy_n(FormatConversionCharIsUpper(conv.conv()) ? "INF" : "inf",
+ 3, ptr);
} else {
return false;
}
@@ -399,7 +401,7 @@ bool FloatToSink(const Float v, const ConversionSpec &conv,
Buffer buffer;
- switch (conv.conv().id()) {
+ switch (conv.conv()) {
case ConversionChar::f:
case ConversionChar::F:
if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer,
@@ -416,7 +418,8 @@ bool FloatToSink(const Float v, const ConversionSpec &conv,
return FallbackToSnprintf(v, conv, sink);
}
if (!conv.flags().alt && buffer.back() == '.') buffer.pop_back();
- PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer);
+ PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e',
+ &buffer);
break;
case ConversionChar::g:
@@ -447,7 +450,10 @@ bool FloatToSink(const Float v, const ConversionSpec &conv,
while (buffer.back() == '0') buffer.pop_back();
if (buffer.back() == '.') buffer.pop_back();
}
- if (exp) PrintExponent(exp, conv.conv().upper() ? 'E' : 'e', &buffer);
+ if (exp) {
+ PrintExponent(exp, FormatConversionCharIsUpper(conv.conv()) ? 'E' : 'e',
+ &buffer);
+ }
break;
case ConversionChar::a:
@@ -483,5 +489,5 @@ bool ConvertFloatImpl(double v, const ConversionSpec &conv,
}
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/float_conversion.h b/absl/strings/internal/str_format/float_conversion.h
index 5d76ce2b..49a6a636 100644
--- a/absl/strings/internal/str_format/float_conversion.h
+++ b/absl/strings/internal/str_format/float_conversion.h
@@ -4,7 +4,7 @@
#include "absl/strings/internal/str_format/extension.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
bool ConvertFloatImpl(float v, const ConversionSpec &conv,
@@ -17,7 +17,7 @@ bool ConvertFloatImpl(long double v, const ConversionSpec &conv,
FormatSinkImpl *sink);
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_
diff --git a/absl/strings/internal/str_format/output.cc b/absl/strings/internal/str_format/output.cc
index d5ead8d5..c4b24706 100644
--- a/absl/strings/internal/str_format/output.cc
+++ b/absl/strings/internal/str_format/output.cc
@@ -18,7 +18,7 @@
#include <cstring>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
@@ -68,5 +68,5 @@ void FILERawSink::Write(string_view v) {
}
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/output.h b/absl/strings/internal/str_format/output.h
index ba847471..28b288b7 100644
--- a/absl/strings/internal/str_format/output.h
+++ b/absl/strings/internal/str_format/output.h
@@ -28,10 +28,11 @@
#include "absl/base/port.h"
#include "absl/strings/string_view.h"
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+
class Cord;
-namespace absl {
-inline namespace lts_2019_08_08 {
namespace str_format_internal {
// RawSink implementation that writes into a char* buffer.
@@ -77,7 +78,7 @@ inline void AbslFormatFlush(std::ostream* out, string_view s) {
}
template <class AbslCord, typename = typename std::enable_if<
- std::is_same<AbslCord, ::Cord>::value>::type>
+ std::is_same<AbslCord, absl::Cord>::value>::type>
inline void AbslFormatFlush(AbslCord* out, string_view s) {
out->Append(s);
}
@@ -97,7 +98,7 @@ auto InvokeFlush(T* out, string_view s)
}
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_
diff --git a/absl/strings/internal/str_format/output_test.cc b/absl/strings/internal/str_format/output_test.cc
index 5c3c4afb..e54e6f70 100644
--- a/absl/strings/internal/str_format/output_test.cc
+++ b/absl/strings/internal/str_format/output_test.cc
@@ -21,7 +21,7 @@
#include "gtest/gtest.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
TEST(InvokeFlush, String) {
@@ -68,6 +68,6 @@ TEST(BufferRawSink, Limits) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc
index 4b8ca4d2..aab68db9 100644
--- a/absl/strings/internal/str_format/parser.cc
+++ b/absl/strings/internal/str_format/parser.cc
@@ -14,11 +14,12 @@
#include <unordered_set>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
-using CC = ConversionChar::Id;
-using LM = LengthMod::Id;
+using CC = ConversionChar;
+using LM = LengthMod;
+
ABSL_CONST_INIT const ConvTag kTags[256] = {
{}, {}, {}, {}, {}, {}, {}, {}, // 00-07
{}, {}, {}, {}, {}, {}, {}, {}, // 08-0f
@@ -205,11 +206,11 @@ flags_done:
using str_format_internal::LengthMod;
LengthMod length_mod = tag.as_length();
ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
- if (c == 'h' && length_mod.id() == LengthMod::h) {
- conv->length_mod = LengthMod::FromId(LengthMod::hh);
+ if (c == 'h' && length_mod == LengthMod::h) {
+ conv->length_mod = LengthMod::hh;
ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
- } else if (c == 'l' && length_mod.id() == LengthMod::l) {
- conv->length_mod = LengthMod::FromId(LengthMod::ll);
+ } else if (c == 'l' && length_mod == LengthMod::l) {
+ conv->length_mod = LengthMod::ll;
ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR();
} else {
conv->length_mod = length_mod;
@@ -228,6 +229,32 @@ flags_done:
} // namespace
+std::string LengthModToString(LengthMod v) {
+ switch (v) {
+ case LengthMod::h:
+ return "h";
+ case LengthMod::hh:
+ return "hh";
+ case LengthMod::l:
+ return "l";
+ case LengthMod::ll:
+ return "ll";
+ case LengthMod::L:
+ return "L";
+ case LengthMod::j:
+ return "j";
+ case LengthMod::z:
+ return "z";
+ case LengthMod::t:
+ return "t";
+ case LengthMod::q:
+ return "q";
+ case LengthMod::none:
+ return "";
+ }
+ return "";
+}
+
const char *ConsumeUnboundConversion(const char *p, const char *end,
UnboundConversion *conv, int *next_arg) {
if (*next_arg < 0) return ConsumeConversion<true>(p, end, conv, next_arg);
@@ -295,11 +322,13 @@ bool ParsedFormatBase::MatchesConversions(
if (conv.width.is_from_arg() &&
!add_if_valid_conv(conv.width.get_from_arg(), '*'))
return false;
- if (!add_if_valid_conv(conv.arg_position, conv.conv.Char())) return false;
+ if (!add_if_valid_conv(conv.arg_position,
+ FormatConversionCharToChar(conv.conv)))
+ return false;
}
return used.size() == convs.size() || allow_ignored;
}
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h
index d3357fde..45c90d1d 100644
--- a/absl/strings/internal/str_format/parser.h
+++ b/absl/strings/internal/str_format/parser.h
@@ -6,19 +6,25 @@
#include <stdlib.h>
#include <cassert>
+#include <cstdint>
#include <initializer_list>
#include <iosfwd>
#include <iterator>
#include <memory>
+#include <string>
#include <vector>
#include "absl/strings/internal/str_format/checker.h"
#include "absl/strings/internal/str_format/extension.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
+enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none };
+
+std::string LengthModToString(LengthMod v);
+
// The analyzed properties of a single specified conversion.
struct UnboundConversion {
UnboundConversion()
@@ -60,8 +66,8 @@ struct UnboundConversion {
InputValue precision;
Flags flags;
- LengthMod length_mod;
- ConversionChar conv;
+ LengthMod length_mod = LengthMod::none;
+ ConversionChar conv = FormatConversionChar::kNone;
};
// Consume conversion spec prefix (not including '%') of [p, end) if valid.
@@ -73,13 +79,16 @@ const char* ConsumeUnboundConversion(const char* p, const char* end,
UnboundConversion* conv, int* next_arg);
// Helper tag class for the table below.
-// It allows fast `char -> ConversionChar/LengthMod` checking and conversions.
+// It allows fast `char -> ConversionChar/LengthMod` checking and
+// conversions.
class ConvTag {
public:
- constexpr ConvTag(ConversionChar::Id id) : tag_(id) {} // NOLINT
+ constexpr ConvTag(ConversionChar conversion_char) // NOLINT
+ : tag_(static_cast<int8_t>(conversion_char)) {}
// We invert the length modifiers to make them negative so that we can easily
// test for them.
- constexpr ConvTag(LengthMod::Id id) : tag_(~id) {} // NOLINT
+ constexpr ConvTag(LengthMod length_mod) // NOLINT
+ : tag_(~static_cast<std::int8_t>(length_mod)) {}
// Everything else is -128, which is negative to make is_conv() simpler.
constexpr ConvTag() : tag_(-128) {}
@@ -87,11 +96,11 @@ class ConvTag {
bool is_length() const { return tag_ < 0 && tag_ != -128; }
ConversionChar as_conv() const {
assert(is_conv());
- return ConversionChar::FromId(static_cast<ConversionChar::Id>(tag_));
+ return static_cast<ConversionChar>(tag_);
}
LengthMod as_length() const {
assert(is_length());
- return LengthMod::FromId(static_cast<LengthMod::Id>(~tag_));
+ return static_cast<LengthMod>(~tag_);
}
private:
@@ -275,7 +284,7 @@ template <str_format_internal::Conv... C>
class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase {
public:
explicit ExtendedParsedFormat(string_view format)
-#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
+#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER
__attribute__((
enable_if(str_format_internal::EnsureConstexpr(format),
"Format std::string is not constexpr."),
@@ -318,7 +327,7 @@ class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase {
: ParsedFormatBase(s, allow_ignored, {C...}) {}
};
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc
index d77a8ea5..1b1ee030 100644
--- a/absl/strings/internal/str_format/parser_test.cc
+++ b/absl/strings/internal/str_format/parser_test.cc
@@ -7,7 +7,7 @@
#include "absl/base/macros.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
@@ -17,7 +17,7 @@ using testing::Pair;
TEST(LengthModTest, Names) {
struct Expectation {
int line;
- LengthMod::Id id;
+ LengthMod mod;
const char *name;
};
const Expectation kExpect[] = {
@@ -32,18 +32,16 @@ TEST(LengthModTest, Names) {
{__LINE__, LengthMod::t, "t" },
{__LINE__, LengthMod::q, "q" },
};
- EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), LengthMod::kNumValues);
+ EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), 10);
for (auto e : kExpect) {
SCOPED_TRACE(e.line);
- LengthMod mod = LengthMod::FromId(e.id);
- EXPECT_EQ(e.id, mod.id());
- EXPECT_EQ(e.name, mod.name());
+ EXPECT_EQ(e.name, LengthModToString(e.mod));
}
}
TEST(ConversionCharTest, Names) {
struct Expectation {
- ConversionChar::Id id;
+ ConversionChar id;
char name;
};
// clang-format off
@@ -57,12 +55,10 @@ TEST(ConversionCharTest, Names) {
{ConversionChar::none, '\0'},
};
// clang-format on
- EXPECT_EQ(ABSL_ARRAYSIZE(kExpect), ConversionChar::kNumValues);
for (auto e : kExpect) {
SCOPED_TRACE(e.name);
- ConversionChar v = ConversionChar::FromId(e.id);
- EXPECT_EQ(e.id, v.id());
- EXPECT_EQ(e.name, v.Char());
+ ConversionChar v = e.id;
+ EXPECT_EQ(e.name, FormatConversionCharToChar(v));
}
}
@@ -121,13 +117,12 @@ TEST_F(ConsumeUnboundConversionTest, BasicConversion) {
EXPECT_FALSE(Run("dd")); // no excess allowed
EXPECT_TRUE(Run("d"));
- EXPECT_EQ('d', o.conv.Char());
+ EXPECT_EQ('d', FormatConversionCharToChar(o.conv));
EXPECT_FALSE(o.width.is_from_arg());
EXPECT_LT(o.width.value(), 0);
EXPECT_FALSE(o.precision.is_from_arg());
EXPECT_LT(o.precision.value(), 0);
EXPECT_EQ(1, o.arg_position);
- EXPECT_EQ(LengthMod::none, o.length_mod.id());
}
TEST_F(ConsumeUnboundConversionTest, ArgPosition) {
@@ -163,7 +158,7 @@ TEST_F(ConsumeUnboundConversionTest, ArgPosition) {
TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) {
EXPECT_TRUE(Run("14d"));
- EXPECT_EQ('d', o.conv.Char());
+ EXPECT_EQ('d', FormatConversionCharToChar(o.conv));
EXPECT_FALSE(o.width.is_from_arg());
EXPECT_EQ(14, o.width.value());
EXPECT_FALSE(o.precision.is_from_arg());
@@ -290,6 +285,29 @@ TEST_F(ConsumeUnboundConversionTest, BasicFlag) {
}
}
+TEST_F(ConsumeUnboundConversionTest, LengthMod) {
+ EXPECT_TRUE(Run("d"));
+ EXPECT_EQ(LengthMod::none, o.length_mod);
+ EXPECT_TRUE(Run("hd"));
+ EXPECT_EQ(LengthMod::h, o.length_mod);
+ EXPECT_TRUE(Run("hhd"));
+ EXPECT_EQ(LengthMod::hh, o.length_mod);
+ EXPECT_TRUE(Run("ld"));
+ EXPECT_EQ(LengthMod::l, o.length_mod);
+ EXPECT_TRUE(Run("lld"));
+ EXPECT_EQ(LengthMod::ll, o.length_mod);
+ EXPECT_TRUE(Run("Lf"));
+ EXPECT_EQ(LengthMod::L, o.length_mod);
+ EXPECT_TRUE(Run("qf"));
+ EXPECT_EQ(LengthMod::q, o.length_mod);
+ EXPECT_TRUE(Run("jd"));
+ EXPECT_EQ(LengthMod::j, o.length_mod);
+ EXPECT_TRUE(Run("zd"));
+ EXPECT_EQ(LengthMod::z, o.length_mod);
+ EXPECT_TRUE(Run("td"));
+ EXPECT_EQ(LengthMod::t, o.length_mod);
+}
+
struct SummarizeConsumer {
std::string* out;
explicit SummarizeConsumer(std::string* out) : out(out) {}
@@ -310,7 +328,7 @@ struct SummarizeConsumer {
if (conv.precision.is_from_arg()) {
*out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
}
- *out += conv.conv.Char();
+ *out += FormatConversionCharToChar(conv.conv);
*out += "}";
return true;
}
@@ -390,5 +408,5 @@ TEST_F(ParsedFormatTest, ParsingFlagOrder) {
} // namespace
} // namespace str_format_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h
index 02787dd1..31dbf672 100644
--- a/absl/strings/internal/str_join_internal.h
+++ b/absl/strings/internal/str_join_internal.h
@@ -43,7 +43,7 @@
#include "absl/strings/str_cat.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
//
@@ -308,7 +308,7 @@ std::string JoinRange(const Range& range, absl::string_view separator) {
}
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h
index 92a678e0..b54f6ebe 100644
--- a/absl/strings/internal/str_split_internal.h
+++ b/absl/strings/internal/str_split_internal.h
@@ -47,7 +47,7 @@
#endif // _GLIBCXX_DEBUG
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
// This class is implicitly constructible from everything that absl::string_view
@@ -449,7 +449,7 @@ class Splitter {
};
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_
diff --git a/absl/strings/internal/utf8.cc b/absl/strings/internal/utf8.cc
index fe4a9beb..8fd8edc1 100644
--- a/absl/strings/internal/utf8.cc
+++ b/absl/strings/internal/utf8.cc
@@ -17,7 +17,7 @@
#include "absl/strings/internal/utf8.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) {
@@ -49,5 +49,5 @@ size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) {
}
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/internal/utf8.h b/absl/strings/internal/utf8.h
index b1bb954e..32fb1093 100644
--- a/absl/strings/internal/utf8.h
+++ b/absl/strings/internal/utf8.h
@@ -20,8 +20,10 @@
#include <cstddef>
#include <cstdint>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
// For Unicode code points 0 through 0x10FFFF, EncodeUTF8Char writes
@@ -42,7 +44,7 @@ enum { kMaxEncodedUTF8Size = 4 };
size_t EncodeUTF8Char(char *buffer, char32_t utf8_char);
} // namespace strings_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_UTF8_H_
diff --git a/absl/strings/match.cc b/absl/strings/match.cc
index 01ed1f15..8127cb0c 100644
--- a/absl/strings/match.cc
+++ b/absl/strings/match.cc
@@ -17,7 +17,7 @@
#include "absl/strings/internal/memutil.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) {
return (piece1.size() == piece2.size() &&
@@ -36,5 +36,5 @@ bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) {
EqualsIgnoreCase(text.substr(text.size() - suffix.size()), suffix);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/match.h b/absl/strings/match.h
index 15fdc0c8..90fca98a 100644
--- a/absl/strings/match.h
+++ b/absl/strings/match.h
@@ -20,7 +20,7 @@
// This file contains simple utilities for performing string matching checks.
// All of these function parameters are specified as `absl::string_view`,
// meaning that these functions can accept `std::string`, `absl::string_view` or
-// nul-terminated C-style strings.
+// NUL-terminated C-style strings.
//
// Examples:
// std::string s = "foo";
@@ -38,7 +38,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// StrContains()
//
@@ -84,7 +84,7 @@ bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix);
// case in the comparison.
bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_MATCH_H_
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc
index 2bac4adc..68c26dd6 100644
--- a/absl/strings/numbers.cc
+++ b/absl/strings/numbers.cc
@@ -19,8 +19,8 @@
#include <algorithm>
#include <cassert>
-#include <cfloat> // for DBL_DIG and FLT_DIG
-#include <cmath> // for HUGE_VAL
+#include <cfloat> // for DBL_DIG and FLT_DIG
+#include <cmath> // for HUGE_VAL
#include <cstdint>
#include <cstdio>
#include <cstdlib>
@@ -30,16 +30,18 @@
#include <memory>
#include <utility>
+#include "absl/base/attributes.h"
#include "absl/base/internal/bits.h"
#include "absl/base/internal/raw_logging.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"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
bool SimpleAtof(absl::string_view str, float* out) {
*out = 0.0;
@@ -93,43 +95,6 @@ bool SimpleAtod(absl::string_view str, double* out) {
return true;
}
-namespace {
-
-// Writes a two-character representation of 'i' to 'buf'. 'i' must be in the
-// range 0 <= i < 100, and buf must have space for two characters. Example:
-// char buf[2];
-// PutTwoDigits(42, buf);
-// // buf[0] == '4'
-// // buf[1] == '2'
-inline void PutTwoDigits(size_t i, char* buf) {
- static const char two_ASCII_digits[100][2] = {
- {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'},
- {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'},
- {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'},
- {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'},
- {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'},
- {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
- {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'},
- {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'},
- {'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'},
- {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'},
- {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'},
- {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
- {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'},
- {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'},
- {'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'},
- {'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'},
- {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'},
- {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
- {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'},
- {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}
- };
- assert(i < 100);
- memcpy(buf, two_ASCII_digits[i], 2);
-}
-
-} // namespace
-
bool SimpleAtob(absl::string_view str, bool* out) {
ABSL_RAW_CHECK(out != nullptr, "Output pointer must not be nullptr.");
if (EqualsIgnoreCase(str, "true") || EqualsIgnoreCase(str, "t") ||
@@ -496,13 +461,13 @@ static ExpDigits SplitToSix(const double value) {
int two_digits = dddddd / 10000;
dddddd -= two_digits * 10000;
- PutTwoDigits(two_digits, &exp_dig.digits[0]);
+ numbers_internal::PutTwoDigits(two_digits, &exp_dig.digits[0]);
two_digits = dddddd / 100;
dddddd -= two_digits * 100;
- PutTwoDigits(two_digits, &exp_dig.digits[2]);
+ numbers_internal::PutTwoDigits(two_digits, &exp_dig.digits[2]);
- PutTwoDigits(dddddd, &exp_dig.digits[4]);
+ numbers_internal::PutTwoDigits(dddddd, &exp_dig.digits[4]);
return exp_dig;
}
@@ -755,8 +720,8 @@ inline bool safe_parse_sign_and_base(absl::string_view* text /*inout*/,
// commonly used bases.
template <typename IntType>
struct LookupTables {
- static const IntType kVmaxOverBase[];
- static const IntType kVminOverBase[];
+ ABSL_CONST_INIT static const IntType kVmaxOverBase[];
+ ABSL_CONST_INIT static const IntType kVminOverBase[];
};
// An array initializer macro for X/base where base in [0, 36].
@@ -771,6 +736,49 @@ struct LookupTables {
X / 35, X / 36, \
}
+// uint128& operator/=(uint128) is not constexpr, so hardcode the resulting
+// array to avoid a static initializer.
+template <>
+const uint128 LookupTables<uint128>::kVmaxOverBase[] = {
+ 0,
+ 0,
+ MakeUint128(9223372036854775807u, 18446744073709551615u),
+ MakeUint128(6148914691236517205u, 6148914691236517205u),
+ MakeUint128(4611686018427387903u, 18446744073709551615u),
+ MakeUint128(3689348814741910323u, 3689348814741910323u),
+ MakeUint128(3074457345618258602u, 12297829382473034410u),
+ MakeUint128(2635249153387078802u, 5270498306774157604u),
+ MakeUint128(2305843009213693951u, 18446744073709551615u),
+ MakeUint128(2049638230412172401u, 14347467612885206812u),
+ MakeUint128(1844674407370955161u, 11068046444225730969u),
+ MakeUint128(1676976733973595601u, 8384883669867978007u),
+ MakeUint128(1537228672809129301u, 6148914691236517205u),
+ MakeUint128(1418980313362273201u, 4256940940086819603u),
+ MakeUint128(1317624576693539401u, 2635249153387078802u),
+ MakeUint128(1229782938247303441u, 1229782938247303441u),
+ MakeUint128(1152921504606846975u, 18446744073709551615u),
+ MakeUint128(1085102592571150095u, 1085102592571150095u),
+ MakeUint128(1024819115206086200u, 16397105843297379214u),
+ MakeUint128(970881267037344821u, 16504981539634861972u),
+ MakeUint128(922337203685477580u, 14757395258967641292u),
+ MakeUint128(878416384462359600u, 14054662151397753612u),
+ MakeUint128(838488366986797800u, 13415813871788764811u),
+ MakeUint128(802032351030850070u, 4812194106185100421u),
+ MakeUint128(768614336404564650u, 12297829382473034410u),
+ MakeUint128(737869762948382064u, 11805916207174113034u),
+ MakeUint128(709490156681136600u, 11351842506898185609u),
+ MakeUint128(683212743470724133u, 17080318586768103348u),
+ MakeUint128(658812288346769700u, 10540996613548315209u),
+ MakeUint128(636094623231363848u, 15266270957552732371u),
+ MakeUint128(614891469123651720u, 9838263505978427528u),
+ MakeUint128(595056260442243600u, 9520900167075897608u),
+ MakeUint128(576460752303423487u, 18446744073709551615u),
+ MakeUint128(558992244657865200u, 8943875914525843207u),
+ MakeUint128(542551296285575047u, 9765923333140350855u),
+ MakeUint128(527049830677415760u, 8432797290838652167u),
+ MakeUint128(512409557603043100u, 8198552921648689607u),
+};
+
template <typename IntType>
const IntType LookupTables<IntType>::kVmaxOverBase[] =
X_OVER_BASE_INITIALIZER(std::numeric_limits<IntType>::max());
@@ -790,6 +798,8 @@ inline bool safe_parse_positive_int(absl::string_view text, int base,
assert(base >= 0);
assert(vmax >= static_cast<IntType>(base));
const IntType vmax_over_base = LookupTables<IntType>::kVmaxOverBase[base];
+ assert(base < 2 ||
+ std::numeric_limits<IntType>::max() / base == vmax_over_base);
const char* start = text.data();
const char* end = start + text.size();
// loop over digits
@@ -823,6 +833,8 @@ inline bool safe_parse_negative_int(absl::string_view text, int base,
assert(vmin < 0);
assert(vmin <= 0 - base);
IntType vmin_over_base = LookupTables<IntType>::kVminOverBase[base];
+ assert(base < 2 ||
+ std::numeric_limits<IntType>::min() / base == vmin_over_base);
// 2003 c++ standard [expr.mul]
// "... the sign of the remainder is implementation-defined."
// Although (vmin/base)*base + vmin%base is always vmin.
@@ -886,6 +898,48 @@ inline bool safe_uint_internal(absl::string_view text, IntType* value_p,
} // anonymous namespace
namespace numbers_internal {
+
+// Digit conversion.
+ABSL_CONST_INIT ABSL_DLL const char kHexChar[] =
+ "0123456789abcdef";
+
+ABSL_CONST_INIT ABSL_DLL const char kHexTable[513] =
+ "000102030405060708090a0b0c0d0e0f"
+ "101112131415161718191a1b1c1d1e1f"
+ "202122232425262728292a2b2c2d2e2f"
+ "303132333435363738393a3b3c3d3e3f"
+ "404142434445464748494a4b4c4d4e4f"
+ "505152535455565758595a5b5c5d5e5f"
+ "606162636465666768696a6b6c6d6e6f"
+ "707172737475767778797a7b7c7d7e7f"
+ "808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f"
+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
+
+ABSL_CONST_INIT ABSL_DLL const char two_ASCII_digits[100][2] = {
+ {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
+ {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
+ {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
+ {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
+ {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
+ {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
+ {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
+ {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
+ {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
+ {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
+ {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
+ {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
+ {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
+ {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
+ {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
+ {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
+ {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
+
bool safe_strto32_base(absl::string_view text, int32_t* value, int base) {
return safe_int_internal<int32_t>(text, value, base);
}
@@ -901,7 +955,11 @@ 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) {
return safe_uint_internal<uint64_t>(text, value, base);
}
-} // namespace numbers_internal
-} // inline namespace lts_2019_08_08
+bool safe_strtou128_base(absl::string_view text, uint128* value, int base) {
+ return safe_uint_internal<absl::uint128>(text, value, base);
+}
+
+} // namespace numbers_internal
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h
index e9878016..d872cca5 100644
--- a/absl/strings/numbers.h
+++ b/absl/strings/numbers.h
@@ -24,6 +24,10 @@
#ifndef ABSL_STRINGS_NUMBERS_H_
#define ABSL_STRINGS_NUMBERS_H_
+#ifdef __SSE4_2__
+#include <x86intrin.h>
+#endif
+
#include <cstddef>
#include <cstdlib>
#include <cstring>
@@ -32,39 +36,54 @@
#include <string>
#include <type_traits>
+#include "absl/base/config.h"
+#include "absl/base/internal/bits.h"
+#ifdef __SSE4_2__
+// TODO(jorg): Remove this when we figure out the right way
+// to swap bytes on SSE 4.2 that works with the compilers
+// we claim to support. Also, add tests for the compiler
+// that doesn't support the Intel _bswap64 intrinsic but
+// does support all the SSE 4.2 intrinsics
+#include "absl/base/internal/endian.h"
+#endif
#include "absl/base/macros.h"
#include "absl/base/port.h"
#include "absl/numeric/int128.h"
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// SimpleAtoi()
//
-// Converts the given string into an integer value, returning `true` if
-// successful. The string must reflect a base-10 integer (optionally followed or
-// preceded by ASCII whitespace) whose value falls within the range of the
-// integer type. If any errors are encountered, this function returns `false`,
-// leaving `out` in an unspecified state.
+// Converts the given string (optionally followed or preceded by ASCII
+// whitespace) into an integer value, returning `true` if successful. The string
+// must reflect a base-10 integer whose value falls within the range of the
+// integer type (optionally preceded by a `+` or `-`). If any errors are
+// 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);
// SimpleAtof()
//
// Converts the given string (optionally followed or preceded by ASCII
-// whitespace) into a float, which may be rounded on overflow or underflow.
+// whitespace) into a float, which may be rounded on overflow or underflow,
+// returning `true` if successful.
// See https://en.cppreference.com/w/c/string/byte/strtof for details about the
-// allowed formats for `str`. If any errors are encountered, this function
+// 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);
// SimpleAtod()
//
// Converts the given string (optionally followed or preceded by ASCII
-// whitespace) into a double, which may be rounded on overflow or underflow.
+// whitespace) into a double, which may be rounded on overflow or underflow,
+// returning `true` if successful.
// See https://en.cppreference.com/w/c/string/byte/strtof for details about the
-// allowed formats for `str`. If any errors are encountered, this function
+// 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);
@@ -78,20 +97,40 @@ ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out);
// unspecified state.
ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// End of public API. Implementation details follow.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace numbers_internal {
+// Digit conversion.
+ABSL_DLL extern const char kHexChar[17]; // 0123456789abcdef
+ABSL_DLL extern const char
+ kHexTable[513]; // 000102030405060708090a0b0c0d0e0f1011...
+ABSL_DLL extern const char
+ two_ASCII_digits[100][2]; // 00, 01, 02, 03...
+
+// Writes a two-character representation of 'i' to 'buf'. 'i' must be in the
+// range 0 <= i < 100, and buf must have space for two characters. Example:
+// char buf[2];
+// PutTwoDigits(42, buf);
+// // buf[0] == '4'
+// // buf[1] == '2'
+inline void PutTwoDigits(size_t i, char* buf) {
+ assert(i < 100);
+ memcpy(buf, two_ASCII_digits[i], 2);
+}
+
// 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_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);
static const int kFastToBufferSize = 32;
static const int kSixDigitsToBufferSize = 16;
@@ -173,6 +212,35 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out,
return parsed;
}
+// FastHexToBufferZeroPad16()
+//
+// Outputs `val` into `out` as if by `snprintf(out, 17, "%016x", val)` but
+// 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) {
+#ifdef __SSE4_2__
+ uint64_t be = absl::big_endian::FromHost64(val);
+ const auto kNibbleMask = _mm_set1_epi8(0xf);
+ const auto kHexDigits = _mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
+ auto v = _mm_loadl_epi64(reinterpret_cast<__m128i*>(&be)); // load lo dword
+ auto v4 = _mm_srli_epi64(v, 4); // shift 4 right
+ auto il = _mm_unpacklo_epi8(v4, v); // interleave bytes
+ auto m = _mm_and_si128(il, kNibbleMask); // mask out nibbles
+ auto hexchars = _mm_shuffle_epi8(kHexDigits, m); // hex chars
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(out), hexchars);
+#else
+ for (int i = 0; i < 8; ++i) {
+ auto byte = (val >> (56 - 8 * i)) & 0xFF;
+ auto* hex = &absl::numbers_internal::kHexTable[byte * 2];
+ std::memcpy(out + 2 * i, hex, 2);
+ }
+#endif
+ // | 0x1 so that even 0 has 1 digit.
+ return 16 - absl::base_internal::CountLeadingZeros64(val | 0x1) / 4;
+}
+
} // namespace numbers_internal
// SimpleAtoi()
@@ -187,7 +255,12 @@ ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) {
return numbers_internal::safe_strtoi_base(str, out, 10);
}
-} // inline namespace lts_2019_08_08
+ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str,
+ absl::uint128* out) {
+ return numbers_internal::safe_strtou128_base(str, out, 10);
+}
+
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_NUMBERS_H_
diff --git a/absl/strings/numbers_benchmark.cc b/absl/strings/numbers_benchmark.cc
index 54dbedd3..6e79b3e8 100644
--- a/absl/strings/numbers_benchmark.cc
+++ b/absl/strings/numbers_benchmark.cc
@@ -20,6 +20,8 @@
#include "benchmark/benchmark.h"
#include "absl/base/internal/raw_logging.h"
+#include "absl/random/distributions.h"
+#include "absl/random/random.h"
#include "absl/strings/numbers.h"
namespace {
@@ -260,4 +262,25 @@ BENCHMARK_TEMPLATE(BM_SimpleAtod, std::string)
->ArgPair(10, 4)
->ArgPair(10, 8);
+void BM_FastHexToBufferZeroPad16(benchmark::State& state) {
+ absl::BitGen rng;
+ std::vector<uint64_t> nums;
+ nums.resize(1000);
+ auto min = std::numeric_limits<uint64_t>::min();
+ auto max = std::numeric_limits<uint64_t>::max();
+ for (auto& num : nums) {
+ num = absl::LogUniform(rng, min, max);
+ }
+
+ char buf[16];
+ while (state.KeepRunningBatch(nums.size())) {
+ for (auto num : nums) {
+ auto digits = absl::numbers_internal::FastHexToBufferZeroPad16(num, buf);
+ benchmark::DoNotOptimize(digits);
+ benchmark::DoNotOptimize(buf);
+ }
+ }
+}
+BENCHMARK(BM_FastHexToBufferZeroPad16);
+
} // namespace
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc
index 77d7e783..68229b15 100644
--- a/absl/strings/numbers_test.cc
+++ b/absl/strings/numbers_test.cc
@@ -17,6 +17,7 @@
#include "absl/strings/numbers.h"
#include <sys/types.h>
+
#include <cfenv> // NOLINT(build/c++11)
#include <cinttypes>
#include <climits>
@@ -36,10 +37,11 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
-#include "absl/strings/str_cat.h"
-
+#include "absl/random/distributions.h"
+#include "absl/random/random.h"
#include "absl/strings/internal/numbers_test_common.h"
#include "absl/strings/internal/pow10_helper.h"
+#include "absl/strings/str_cat.h"
namespace {
@@ -204,6 +206,9 @@ void CheckHex64(uint64_t v) {
std::string actual = absl::StrCat(absl::Hex(v, absl::kZeroPad16));
snprintf(expected, sizeof(expected), "%016" PRIx64, static_cast<uint64_t>(v));
EXPECT_EQ(expected, actual) << " Input " << v;
+ actual = absl::StrCat(absl::Hex(v, absl::kSpacePad16));
+ snprintf(expected, sizeof(expected), "%16" PRIx64, static_cast<uint64_t>(v));
+ EXPECT_EQ(expected, actual) << " Input " << v;
}
TEST(Numbers, TestFastPrints) {
@@ -244,7 +249,9 @@ TEST(Numbers, TestFastPrints) {
template <typename int_type, typename in_val_type>
void VerifySimpleAtoiGood(in_val_type in_value, int_type exp_value) {
- std::string s = absl::StrCat(in_value);
+ std::string s;
+ // uint128 can be streamed but not StrCat'd
+ absl::strings_internal::OStringStream(&s) << in_value;
int_type x = static_cast<int_type>(~exp_value);
EXPECT_TRUE(SimpleAtoi(s, &x))
<< "in_value=" << in_value << " s=" << s << " x=" << x;
@@ -320,6 +327,25 @@ TEST(NumbersTest, Atoi) {
VerifySimpleAtoiGood<uint64_t>(std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::max());
+ // SimpleAtoi(absl::string_view, absl::uint128)
+ VerifySimpleAtoiGood<absl::uint128>(0, 0);
+ VerifySimpleAtoiGood<absl::uint128>(42, 42);
+ VerifySimpleAtoiBad<absl::uint128>(-42);
+
+ VerifySimpleAtoiBad<absl::uint128>(std::numeric_limits<int32_t>::min());
+ VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::max());
+ VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<uint32_t>::max(),
+ std::numeric_limits<uint32_t>::max());
+ VerifySimpleAtoiBad<absl::uint128>(std::numeric_limits<int64_t>::min());
+ VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<int64_t>::max());
+ VerifySimpleAtoiGood<absl::uint128>(std::numeric_limits<uint64_t>::max(),
+ std::numeric_limits<uint64_t>::max());
+ VerifySimpleAtoiGood<absl::uint128>(
+ std::numeric_limits<absl::uint128>::max(),
+ std::numeric_limits<absl::uint128>::max());
+
// Some other types
VerifySimpleAtoiGood<int>(-42, -42);
VerifySimpleAtoiGood<int32_t>(-42, -42);
@@ -652,6 +678,46 @@ TEST(stringtest, safe_strtou32_random) {
TEST(stringtest, safe_strtou64_random) {
test_random_integer_parse_base<uint64_t>(&safe_strtou64_base);
}
+TEST(stringtest, safe_strtou128_random) {
+ // random number generators don't work for uint128, and
+ // uint128 can be streamed but not StrCat'd, so this code must be custom
+ // implemented for uint128, but is generally the same as what's above.
+ // test_random_integer_parse_base<absl::uint128>(
+ // &absl::numbers_internal::safe_strtou128_base);
+ using RandomEngine = std::minstd_rand0;
+ using IntType = absl::uint128;
+ constexpr auto parse_func = &absl::numbers_internal::safe_strtou128_base;
+
+ std::random_device rd;
+ RandomEngine rng(rd());
+ std::uniform_int_distribution<uint64_t> random_uint64(
+ std::numeric_limits<uint64_t>::min());
+ std::uniform_int_distribution<int> random_base(2, 35);
+
+ for (size_t i = 0; i < kNumRandomTests; i++) {
+ IntType value = random_uint64(rng);
+ value = (value << 64) + random_uint64(rng);
+ int base = random_base(rng);
+ std::string str_value;
+ EXPECT_TRUE(Itoa<IntType>(value, base, &str_value));
+ IntType parsed_value;
+
+ // Test successful parse
+ EXPECT_TRUE(parse_func(str_value, &parsed_value, base));
+ EXPECT_EQ(parsed_value, value);
+
+ // Test overflow
+ std::string s;
+ absl::strings_internal::OStringStream(&s)
+ << std::numeric_limits<IntType>::max() << value;
+ EXPECT_FALSE(parse_func(s, &parsed_value, base));
+
+ // Test underflow
+ s.clear();
+ absl::strings_internal::OStringStream(&s) << "-" << value;
+ EXPECT_FALSE(parse_func(s, &parsed_value, base));
+ }
+}
TEST(stringtest, safe_strtou32_base) {
for (int i = 0; strtouint32_test_cases()[i].str != nullptr; ++i) {
@@ -713,11 +779,11 @@ TEST(stringtest, safe_strtou64_base_length_delimited) {
}
}
-// feenableexcept() and fedisableexcept() are missing on macOS, MSVC,
-// and WebAssembly.
-#if defined(_MSC_VER) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
-#define ABSL_MISSING_FEENABLEEXCEPT 1
-#define ABSL_MISSING_FEDISABLEEXCEPT 1
+// feenableexcept() and fedisableexcept() are extensions supported by some libc
+// implementations.
+#if defined(__GLIBC__) || defined(__BIONIC__)
+#define ABSL_HAVE_FEENABLEEXCEPT 1
+#define ABSL_HAVE_FEDISABLEEXCEPT 1
#endif
class SimpleDtoaTest : public testing::Test {
@@ -725,7 +791,7 @@ class SimpleDtoaTest : public testing::Test {
void SetUp() override {
// Store the current floating point env & clear away any pending exceptions.
feholdexcept(&fp_env_);
-#ifndef ABSL_MISSING_FEENABLEEXCEPT
+#ifdef ABSL_HAVE_FEENABLEEXCEPT
// Turn on floating point exceptions.
feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
#endif
@@ -735,7 +801,7 @@ class SimpleDtoaTest : public testing::Test {
// Restore the floating point environment to the original state.
// In theory fedisableexcept is unnecessary; fesetenv will also do it.
// In practice, our toolchains have subtle bugs.
-#ifndef ABSL_MISSING_FEDISABLEEXCEPT
+#ifdef ABSL_HAVE_FEDISABLEEXCEPT
fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
#endif
fesetenv(&fp_env_);
@@ -1184,4 +1250,28 @@ TEST(StrToUint64Base, PrefixOnly) {
}
}
+void TestFastHexToBufferZeroPad16(uint64_t v) {
+ char buf[16];
+ auto digits = absl::numbers_internal::FastHexToBufferZeroPad16(v, buf);
+ absl::string_view res(buf, 16);
+ char buf2[17];
+ snprintf(buf2, sizeof(buf2), "%016" PRIx64, v);
+ EXPECT_EQ(res, buf2) << v;
+ size_t expected_digits = snprintf(buf2, sizeof(buf2), "%" PRIx64, v);
+ EXPECT_EQ(digits, expected_digits) << v;
+}
+
+TEST(FastHexToBufferZeroPad16, Smoke) {
+ TestFastHexToBufferZeroPad16(std::numeric_limits<uint64_t>::min());
+ TestFastHexToBufferZeroPad16(std::numeric_limits<uint64_t>::max());
+ TestFastHexToBufferZeroPad16(std::numeric_limits<int64_t>::min());
+ TestFastHexToBufferZeroPad16(std::numeric_limits<int64_t>::max());
+ absl::BitGen rng;
+ for (int i = 0; i < 100000; ++i) {
+ TestFastHexToBufferZeroPad16(
+ absl::LogUniform(rng, std::numeric_limits<uint64_t>::min(),
+ std::numeric_limits<uint64_t>::max()));
+ }
+}
+
} // namespace
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 73b9e0ba..d9afe2f3 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -15,35 +15,34 @@
#include "absl/strings/str_cat.h"
#include <assert.h>
+
#include <algorithm>
#include <cstdint>
#include <cstring>
#include "absl/strings/ascii.h"
#include "absl/strings/internal/resize_uninitialized.h"
+#include "absl/strings/numbers.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
AlphaNum::AlphaNum(Hex hex) {
+ static_assert(numbers_internal::kFastToBufferSize >= 32,
+ "This function only works when output buffer >= 32 bytes long");
char* const end = &digits_[numbers_internal::kFastToBufferSize];
- char* writer = end;
- uint64_t value = hex.value;
- static const char hexdigits[] = "0123456789abcdef";
- do {
- *--writer = hexdigits[value & 0xF];
- value >>= 4;
- } while (value != 0);
-
- char* beg;
- if (end - writer < hex.width) {
- beg = end - hex.width;
- std::fill_n(beg, writer - beg, hex.fill);
+ auto real_width =
+ absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16);
+ if (real_width >= hex.width) {
+ piece_ = absl::string_view(end - real_width, real_width);
} else {
- beg = writer;
+ // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and
+ // max pad width can be up to 20.
+ std::memset(end - 32, hex.fill, 16);
+ // Patch up everything else up to the real_width.
+ std::memset(end - real_width - 16, hex.fill, 16);
+ piece_ = absl::string_view(end - hex.width, hex.width);
}
-
- piece_ = absl::string_view(beg, end - beg);
}
AlphaNum::AlphaNum(Dec dec) {
@@ -100,7 +99,7 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b) {
std::string result;
absl::strings_internal::STLStringResizeUninitialized(&result,
a.size() + b.size());
- char* const begin = &*result.begin();
+ char* const begin = &result[0];
char* out = begin;
out = Append(out, a);
out = Append(out, b);
@@ -112,7 +111,7 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) {
std::string result;
strings_internal::STLStringResizeUninitialized(
&result, a.size() + b.size() + c.size());
- char* const begin = &*result.begin();
+ char* const begin = &result[0];
char* out = begin;
out = Append(out, a);
out = Append(out, b);
@@ -126,7 +125,7 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c,
std::string result;
strings_internal::STLStringResizeUninitialized(
&result, a.size() + b.size() + c.size() + d.size());
- char* const begin = &*result.begin();
+ char* const begin = &result[0];
char* out = begin;
out = Append(out, a);
out = Append(out, b);
@@ -145,7 +144,7 @@ std::string CatPieces(std::initializer_list<absl::string_view> pieces) {
for (const absl::string_view piece : pieces) total_size += piece.size();
strings_internal::STLStringResizeUninitialized(&result, total_size);
- char* const begin = &*result.begin();
+ char* const begin = &result[0];
char* out = begin;
for (const absl::string_view piece : pieces) {
const size_t this_size = piece.size();
@@ -177,7 +176,7 @@ void AppendPieces(std::string* dest,
}
strings_internal::STLStringResizeUninitialized(dest, total_size);
- char* const begin = &*dest->begin();
+ char* const begin = &(*dest)[0];
char* out = begin + old_size;
for (const absl::string_view piece : pieces) {
const size_t this_size = piece.size();
@@ -202,7 +201,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) {
std::string::size_type old_size = dest->size();
strings_internal::STLStringResizeUninitialized(
dest, old_size + a.size() + b.size());
- char* const begin = &*dest->begin();
+ char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
out = Append(out, b);
@@ -217,7 +216,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
std::string::size_type old_size = dest->size();
strings_internal::STLStringResizeUninitialized(
dest, old_size + a.size() + b.size() + c.size());
- char* const begin = &*dest->begin();
+ char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
out = Append(out, b);
@@ -234,7 +233,7 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
std::string::size_type old_size = dest->size();
strings_internal::STLStringResizeUninitialized(
dest, old_size + a.size() + b.size() + c.size() + d.size());
- char* const begin = &*dest->begin();
+ char* const begin = &(*dest)[0];
char* out = begin + old_size;
out = Append(out, a);
out = Append(out, b);
@@ -243,5 +242,5 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b,
assert(out == begin + dest->size());
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index c2700475..292fa235 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -64,7 +64,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
// AlphaNumBuffer allows a way to pass a string to StrCat without having to do
@@ -291,7 +291,8 @@ class AlphaNum {
// StrCat()
// -----------------------------------------------------------------------------
//
-// Merges given strings or numbers, using no delimiter(s).
+// Merges given strings or numbers, using no delimiter(s), returning the merged
+// result as a string.
//
// `StrCat()` is designed to be the fastest possible way to construct a string
// out of a mix of raw C strings, string_views, strings, bool values,
@@ -401,7 +402,7 @@ SixDigits(double d) {
return result;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_STR_CAT_H_
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index 29db9c02..be39880b 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -195,6 +195,21 @@ TEST(StrCat, Basics) {
EXPECT_EQ(result, "12333444455555666666777777788888888999999999");
}
+TEST(StrCat, CornerCases) {
+ std::string result;
+
+ result = absl::StrCat(""); // NOLINT
+ EXPECT_EQ(result, "");
+ result = absl::StrCat("", "");
+ EXPECT_EQ(result, "");
+ result = absl::StrCat("", "", "");
+ EXPECT_EQ(result, "");
+ result = absl::StrCat("", "", "", "");
+ EXPECT_EQ(result, "");
+ result = absl::StrCat("", "", "", "", "");
+ EXPECT_EQ(result, "");
+}
+
// A minimal allocator that uses malloc().
template <typename T>
struct Mallocator {
@@ -433,10 +448,34 @@ TEST(StrAppend, Death) {
}
#endif // GTEST_HAS_DEATH_TEST
-TEST(StrAppend, EmptyString) {
- std::string s = "";
- absl::StrAppend(&s, s);
- EXPECT_EQ(s, "");
+TEST(StrAppend, CornerCases) {
+ std::string result;
+ absl::StrAppend(&result, "");
+ EXPECT_EQ(result, "");
+ absl::StrAppend(&result, "", "");
+ EXPECT_EQ(result, "");
+ absl::StrAppend(&result, "", "", "");
+ EXPECT_EQ(result, "");
+ absl::StrAppend(&result, "", "", "", "");
+ EXPECT_EQ(result, "");
+ absl::StrAppend(&result, "", "", "", "", "");
+ EXPECT_EQ(result, "");
+}
+
+TEST(StrAppend, CornerCasesNonEmptyAppend) {
+ for (std::string result : {"hello", "a std::string too long to fit in the SSO"}) {
+ const std::string expected = result;
+ absl::StrAppend(&result, "");
+ EXPECT_EQ(result, expected);
+ absl::StrAppend(&result, "", "");
+ EXPECT_EQ(result, expected);
+ absl::StrAppend(&result, "", "", "");
+ EXPECT_EQ(result, expected);
+ absl::StrAppend(&result, "", "", "", "");
+ EXPECT_EQ(result, expected);
+ absl::StrAppend(&result, "", "", "", "", "");
+ EXPECT_EQ(result, expected);
+ }
}
template <typename IntType>
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h
index b4d1b7bd..2f9b4b27 100644
--- a/absl/strings/str_format.h
+++ b/absl/strings/str_format.h
@@ -82,7 +82,7 @@
#include "absl/strings/internal/str_format/parser.h" // IWYU pragma: export
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// UntypedFormatSpec
//
@@ -401,6 +401,12 @@ int FPrintF(std::FILE* output, const FormatSpec<Args...>& format,
// This function is functionally equivalent to `std::snprintf()` (and
// type-safe); prefer `absl::SNPrintF()` over `std::snprintf()`.
//
+// In particular, a successful call to `absl::SNPrintF()` writes at most `size`
+// bytes of the formatted output to `output`, including a NUL-terminator, and
+// returns the number of bytes that would have been written if truncation did
+// not occur. In the event of an error, a negative value is returned and `errno`
+// is set.
+//
// Example:
//
// std::string_view s = "Ulaanbaatar";
@@ -525,7 +531,7 @@ ABSL_MUST_USE_RESULT inline bool FormatUntyped(
str_format_internal::UntypedFormatSpecImpl::Extract(format), args);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_STR_FORMAT_H_
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index bb0f58bf..acbdbf4a 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -10,7 +10,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
using str_format_internal::FormatArgImpl;
@@ -450,7 +450,7 @@ struct SummarizeConsumer {
if (conv.precision.is_from_arg()) {
*out += "." + std::to_string(conv.precision.get_from_arg()) + "$*";
}
- *out += conv.conv.Char();
+ *out += FormatConversionCharToChar(conv.conv);
*out += "}";
return true;
}
@@ -623,7 +623,7 @@ TEST_F(FormatWrapperTest, ParsedFormat) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// Some codegen thunks that we can use to easily dump the generated assembly for
diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h
index c6c0c98a..ae5731a4 100644
--- a/absl/strings/str_join.h
+++ b/absl/strings/str_join.h
@@ -60,7 +60,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// Concept: Formatter
@@ -287,7 +287,7 @@ std::string StrJoin(const std::tuple<T...>& value,
return strings_internal::JoinAlgorithm(value, separator, AlphaNumFormatter());
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_STR_JOIN_H_
diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc
index 3bd8bae1..2bd5fa98 100644
--- a/absl/strings/str_replace.cc
+++ b/absl/strings/str_replace.cc
@@ -17,7 +17,7 @@
#include "absl/strings/str_cat.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace strings_internal {
using FixedMapping =
@@ -78,5 +78,5 @@ int StrReplaceAll(strings_internal::FixedMapping replacements,
return StrReplaceAll<strings_internal::FixedMapping>(replacements, target);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h
index cb995456..273c7077 100644
--- a/absl/strings/str_replace.h
+++ b/absl/strings/str_replace.h
@@ -46,7 +46,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// StrReplaceAll()
//
@@ -213,7 +213,7 @@ int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) {
return substitutions;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_STR_REPLACE_H_
diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc
index f1e03717..d0f86669 100644
--- a/absl/strings/str_split.cc
+++ b/absl/strings/str_split.cc
@@ -27,7 +27,7 @@
#include "absl/strings/ascii.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
@@ -135,5 +135,5 @@ absl::string_view ByLength::Find(absl::string_view text,
return absl::string_view(substr.data() + length_, 0);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h
index 463ca008..a79cd4a0 100644
--- a/absl/strings/str_split.h
+++ b/absl/strings/str_split.h
@@ -49,7 +49,7 @@
#include "absl/strings/strip.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
//------------------------------------------------------------------------------
// Delimiters
@@ -507,7 +507,7 @@ StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d,
std::move(text), DelimiterType(d), std::move(p));
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_STR_SPLIT_H_
diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc
index 9d241e51..c5f5de93 100644
--- a/absl/strings/string_view.cc
+++ b/absl/strings/string_view.cc
@@ -14,7 +14,7 @@
#include "absl/strings/string_view.h"
-#ifndef ABSL_HAVE_STD_STRING_VIEW
+#ifndef ABSL_USES_STD_STRING_VIEW
#include <algorithm>
#include <climits>
@@ -24,7 +24,7 @@
#include "absl/strings/internal/memutil.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
void WritePadding(std::ostream& o, size_t pad) {
@@ -229,7 +229,7 @@ constexpr string_view::size_type string_view::npos;
ABSL_STRING_VIEW_SELECTANY
constexpr string_view::size_type string_view::kMaxSize;
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#endif // ABSL_HAVE_STD_STRING_VIEW
+#endif // ABSL_USES_STD_STRING_VIEW
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h
index 3a0a4609..1861ea62 100644
--- a/absl/strings/string_view.h
+++ b/absl/strings/string_view.h
@@ -28,20 +28,6 @@
#define ABSL_STRINGS_STRING_VIEW_H_
#include <algorithm>
-#include "absl/base/config.h"
-
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-
-#include <string_view> // IWYU pragma: export
-
-namespace absl {
-inline namespace lts_2019_08_08 {
-using std::string_view;
-} // inline namespace lts_2019_08_08
-} // namespace absl
-
-#else // ABSL_HAVE_STD_STRING_VIEW
-
#include <cassert>
#include <cstddef>
#include <cstring>
@@ -50,13 +36,33 @@ using std::string_view;
#include <limits>
#include <string>
+#include "absl/base/config.h"
#include "absl/base/internal/throw_delegate.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h"
#include "absl/base/port.h"
+#ifdef ABSL_USES_STD_STRING_VIEW
+
+#include <string_view> // IWYU pragma: export
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+using std::string_view;
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#else // ABSL_USES_STD_STRING_VIEW
+
+#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \
+ (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_INTERNAL_STRING_VIEW_MEMCMP __builtin_memcmp
+#else // ABSL_HAVE_BUILTIN(__builtin_memcmp)
+#define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp
+#endif // ABSL_HAVE_BUILTIN(__builtin_memcmp)
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// absl::string_view
//
@@ -105,17 +111,17 @@ inline namespace lts_2019_08_08 {
// example, when splitting a string, `std::vector<absl::string_view>` is a
// natural data type for the output.
//
-// When constructed from a source which is nul-terminated, the `string_view`
-// itself will not include the nul-terminator unless a specific size (including
-// the nul) is passed to the constructor. As a result, common idioms that work
-// on nul-terminated strings do not work on `string_view` objects. If you write
+// When constructed from a source which is NUL-terminated, the `string_view`
+// itself will not include the NUL-terminator unless a specific size (including
+// the NUL) is passed to the constructor. As a result, common idioms that work
+// on NUL-terminated strings do not work on `string_view` objects. If you write
// code that scans a `string_view`, you must check its length rather than test
// for nul, for example. Note, however, that nuls may still be embedded within
// a `string_view` explicitly.
//
// You may create a null `string_view` in two ways:
//
-// absl::string_view sv();
+// absl::string_view sv;
// absl::string_view sv(nullptr, 0);
//
// For the above, `sv.data() == nullptr`, `sv.length() == 0`, and
@@ -171,9 +177,11 @@ class string_view {
string_view( // NOLINT(runtime/explicit)
const std::basic_string<char, std::char_traits<char>, Allocator>&
str) noexcept
- : ptr_(str.data()), length_(CheckLengthInternal(str.size())) {}
+ // This is implemented in terms of `string_view(p, n)` so `str.size()`
+ // doesn't need to be reevaluated after `ptr_` is set.
+ : string_view(str.data(), str.size()) {}
- // Implicit constructor of a `string_view` from nul-terminated `str`. When
+ // Implicit constructor of a `string_view` from NUL-terminated `str`. When
// accepting possibly null strings, use `absl::NullSafeStringView(str)`
// instead (see below).
constexpr string_view(const char* str) // NOLINT(runtime/explicit)
@@ -272,26 +280,45 @@ class string_view {
// string_view::operator[]
//
- // Returns the ith element of an `string_view` using the array operator.
+ // Returns the ith element of the `string_view` using the array operator.
// Note that this operator does not perform any bounds checking.
- constexpr const_reference operator[](size_type i) const { return ptr_[i]; }
+ constexpr const_reference operator[](size_type i) const {
+ return ABSL_ASSERT(i < size()), ptr_[i];
+ }
+
+ // string_view::at()
+ //
+ // Returns the ith element of the `string_view`. Bounds checking is performed,
+ // and an exception of type `std::out_of_range` will be thrown on invalid
+ // access.
+ constexpr const_reference at(size_type i) const {
+ return ABSL_PREDICT_TRUE(i < size())
+ ? ptr_[i]
+ : ((void)base_internal::ThrowStdOutOfRange(
+ "absl::string_view::at"),
+ ptr_[i]);
+ }
// string_view::front()
//
// Returns the first element of a `string_view`.
- constexpr const_reference front() const { return ptr_[0]; }
+ constexpr const_reference front() const {
+ return ABSL_ASSERT(!empty()), ptr_[0];
+ }
// string_view::back()
//
// Returns the last element of a `string_view`.
- constexpr const_reference back() const { return ptr_[size() - 1]; }
+ constexpr const_reference back() const {
+ return ABSL_ASSERT(!empty()), ptr_[size() - 1];
+ }
// string_view::data()
//
// Returns a pointer to the underlying character array (which is of course
// stored elsewhere). Note that `string_view::data()` may contain embedded nul
- // characters, but the returned buffer may or may not be nul-terminated;
- // therefore, do not pass `data()` to a routine that expects a nul-terminated
+ // characters, but the returned buffer may or may not be NUL-terminated;
+ // therefore, do not pass `data()` to a routine that expects a NUL-terminated
// std::string.
constexpr const_pointer data() const noexcept { return ptr_; }
@@ -345,7 +372,7 @@ class string_view {
size_type rlen = (std::min)(length_ - pos, n);
if (rlen > 0) {
const char* start = ptr_ + pos;
- std::copy(start, start + rlen, buf);
+ traits_type::copy(buf, start, rlen);
}
return rlen;
}
@@ -370,16 +397,12 @@ class string_view {
// view. Note that in the case of data equality, a further comparison is made
// on the respective sizes of the two `string_view`s to determine which is
// smaller, equal, or greater.
- int compare(string_view x) const noexcept {
- auto min_length = (std::min)(length_, x.length_);
- if (min_length > 0) {
- int r = memcmp(ptr_, x.ptr_, min_length);
- if (r < 0) return -1;
- if (r > 0) return 1;
- }
- if (length_ < x.length_) return -1;
- if (length_ > x.length_) return 1;
- return 0;
+ constexpr int compare(string_view x) const noexcept {
+ return CompareImpl(length_, x.length_,
+ Min(length_, x.length_) == 0
+ ? 0
+ : ABSL_INTERNAL_STRING_VIEW_MEMCMP(
+ ptr_, x.ptr_, Min(length_, x.length_)));
}
// Overload of `string_view::compare()` for comparing a substring of the
@@ -496,7 +519,7 @@ class string_view {
(std::numeric_limits<difference_type>::max)();
static constexpr size_type CheckLengthInternal(size_type len) {
- return ABSL_ASSERT(len <= kMaxSize), len;
+ return (void)ABSL_ASSERT(len <= kMaxSize), len;
}
static constexpr size_type StrlenInternal(const char* str) {
@@ -517,6 +540,17 @@ class string_view {
#endif
}
+ static constexpr size_t Min(size_type length_a, size_type length_b) {
+ return length_a < length_b ? length_a : length_b;
+ }
+
+ static constexpr int CompareImpl(size_type length_a, size_type length_b,
+ int compare_result) {
+ return compare_result == 0 ? static_cast<int>(length_a > length_b) -
+ static_cast<int>(length_a < length_b)
+ : (compare_result < 0 ? -1 : 1);
+ }
+
const char* ptr_;
size_type length_;
};
@@ -524,46 +558,44 @@ class string_view {
// This large function is defined inline so that in a fairly common case where
// one of the arguments is a literal, the compiler can elide a lot of the
// following comparisons.
-inline bool operator==(string_view x, string_view y) noexcept {
- auto len = x.size();
- if (len != y.size()) {
- return false;
- }
-
- return x.data() == y.data() || len <= 0 ||
- memcmp(x.data(), y.data(), len) == 0;
+constexpr bool operator==(string_view x, string_view y) noexcept {
+ return x.size() == y.size() &&
+ (x.empty() ||
+ ABSL_INTERNAL_STRING_VIEW_MEMCMP(x.data(), y.data(), x.size()) == 0);
}
-inline bool operator!=(string_view x, string_view y) noexcept {
+constexpr bool operator!=(string_view x, string_view y) noexcept {
return !(x == y);
}
-inline bool operator<(string_view x, string_view y) noexcept {
- auto min_size = (std::min)(x.size(), y.size());
- const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size);
- return (r < 0) || (r == 0 && x.size() < y.size());
+constexpr bool operator<(string_view x, string_view y) noexcept {
+ return x.compare(y) < 0;
}
-inline bool operator>(string_view x, string_view y) noexcept { return y < x; }
+constexpr bool operator>(string_view x, string_view y) noexcept {
+ return y < x;
+}
-inline bool operator<=(string_view x, string_view y) noexcept {
+constexpr bool operator<=(string_view x, string_view y) noexcept {
return !(y < x);
}
-inline bool operator>=(string_view x, string_view y) noexcept {
+constexpr bool operator>=(string_view x, string_view y) noexcept {
return !(x < y);
}
// IO Insertion Operator
std::ostream& operator<<(std::ostream& o, string_view piece);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#endif // ABSL_HAVE_STD_STRING_VIEW
+#undef ABSL_INTERNAL_STRING_VIEW_MEMCMP
+
+#endif // ABSL_USES_STD_STRING_VIEW
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// ClippedSubstr()
//
@@ -580,11 +612,11 @@ 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.
-inline string_view NullSafeStringView(const char* p) {
+constexpr string_view NullSafeStringView(const char* p) {
return p ? string_view(p) : string_view();
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_STRING_VIEW_H_
diff --git a/absl/strings/string_view_benchmark.cc b/absl/strings/string_view_benchmark.cc
index 46909cb0..0d74e23e 100644
--- a/absl/strings/string_view_benchmark.cc
+++ b/absl/strings/string_view_benchmark.cc
@@ -30,6 +30,24 @@
namespace {
+void BM_StringViewFromString(benchmark::State& state) {
+ std::string s(state.range(0), 'x');
+ std::string* ps = &s;
+ struct SV {
+ SV() = default;
+ explicit SV(const std::string& s) : sv(s) {}
+ absl::string_view sv;
+ } sv;
+ SV* psv = &sv;
+ benchmark::DoNotOptimize(ps);
+ benchmark::DoNotOptimize(psv);
+ for (auto _ : state) {
+ new (psv) SV(*ps);
+ benchmark::DoNotOptimize(sv);
+ }
+}
+BENCHMARK(BM_StringViewFromString)->Arg(12)->Arg(128);
+
// Provide a forcibly out-of-line wrapper for operator== that can be used in
// benchmarks to measure the impact of inlining.
ABSL_ATTRIBUTE_NOINLINE
@@ -142,11 +160,45 @@ void BM_CompareSame(benchmark::State& state) {
absl::string_view b = y;
for (auto _ : state) {
+ benchmark::DoNotOptimize(a);
+ benchmark::DoNotOptimize(b);
benchmark::DoNotOptimize(a.compare(b));
}
}
BENCHMARK(BM_CompareSame)->DenseRange(0, 3)->Range(4, 1 << 10);
+void BM_CompareFirstOneLess(benchmark::State& state) {
+ const int len = state.range(0);
+ std::string x(len, 'a');
+ std::string y = x;
+ y.back() = 'b';
+ absl::string_view a = x;
+ absl::string_view b = y;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(a);
+ benchmark::DoNotOptimize(b);
+ benchmark::DoNotOptimize(a.compare(b));
+ }
+}
+BENCHMARK(BM_CompareFirstOneLess)->DenseRange(1, 3)->Range(4, 1 << 10);
+
+void BM_CompareSecondOneLess(benchmark::State& state) {
+ const int len = state.range(0);
+ std::string x(len, 'a');
+ std::string y = x;
+ x.back() = 'b';
+ absl::string_view a = x;
+ absl::string_view b = y;
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(a);
+ benchmark::DoNotOptimize(b);
+ benchmark::DoNotOptimize(a.compare(b));
+ }
+}
+BENCHMARK(BM_CompareSecondOneLess)->DenseRange(1, 3)->Range(4, 1 << 10);
+
void BM_find_string_view_len_one(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc
index 22d43536..7b1d56fa 100644
--- a/absl/strings/string_view_test.cc
+++ b/absl/strings/string_view_test.cc
@@ -29,7 +29,8 @@
#include "absl/base/config.h"
#include "absl/base/dynamic_annotations.h"
-#ifdef __ANDROID__
+#if defined(ABSL_HAVE_STD_STRING_VIEW) || defined(__ANDROID__)
+// We don't control the death messaging when using std::string_view.
// Android assert messages only go to system log, so death tests cannot inspect
// the message for matching.
#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
@@ -372,7 +373,7 @@ TEST(StringViewTest, STL1) {
#ifdef ABSL_HAVE_EXCEPTIONS
EXPECT_THROW(a.copy(buf, 1, 27), std::out_of_range);
#else
- EXPECT_DEATH(a.copy(buf, 1, 27), "absl::string_view::copy");
+ ABSL_EXPECT_DEATH_IF_SUPPORTED(a.copy(buf, 1, 27), "absl::string_view::copy");
#endif
}
@@ -686,7 +687,8 @@ TEST(StringViewTest, STL2Substr) {
#ifdef ABSL_HAVE_EXCEPTIONS
EXPECT_THROW((void)a.substr(99, 2), std::out_of_range);
#else
- EXPECT_DEATH((void)a.substr(99, 2), "absl::string_view::substr");
+ ABSL_EXPECT_DEATH_IF_SUPPORTED((void)a.substr(99, 2),
+ "absl::string_view::substr");
#endif
}
@@ -816,21 +818,35 @@ TEST(StringViewTest, FrontBackSingleChar) {
EXPECT_EQ(&c, &csp.back());
}
+TEST(StringViewTest, FrontBackEmpty) {
+#ifndef ABSL_USES_STD_STRING_VIEW
+#ifndef NDEBUG
+ // Abseil's string_view implementation has debug assertions that check that
+ // front() and back() are not called on an empty string_view.
+ absl::string_view sv;
+ ABSL_EXPECT_DEATH_IF_SUPPORTED(sv.front(), "");
+ ABSL_EXPECT_DEATH_IF_SUPPORTED(sv.back(), "");
+#endif
+#endif
+}
+
// `std::string_view::string_view(const char*)` calls
// `std::char_traits<char>::length(const char*)` to get the string length. In
// libc++, it doesn't allow `nullptr` in the constexpr context, with the error
// "read of dereferenced null pointer is not allowed in a constant expression".
// At run time, the behavior of `std::char_traits::length()` on `nullptr` is
// undefined by the standard and usually results in crash with libc++.
+// GCC also started rejected this in libstdc++ starting in GCC9.
// In MSVC, creating a constexpr string_view from nullptr also triggers an
// "unevaluable pointer value" error. This compiler implementation conforms
// to the standard, but `absl::string_view` implements a different
// behavior for historical reasons. We work around tests that construct
// `string_view` from `nullptr` when using libc++.
-#if !defined(ABSL_HAVE_STD_STRING_VIEW) || \
- (!defined(_LIBCPP_VERSION) && !defined(_MSC_VER))
+#if !defined(ABSL_USES_STD_STRING_VIEW) || \
+ (!(defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 9) && \
+ !defined(_LIBCPP_VERSION) && !defined(_MSC_VER))
#define ABSL_HAVE_STRING_VIEW_FROM_NULLPTR 1
-#endif // !defined(ABSL_HAVE_STD_STRING_VIEW) || !defined(_LIBCPP_VERSION)
+#endif
TEST(StringViewTest, NULLInput) {
absl::string_view s;
@@ -892,6 +908,18 @@ TEST(StringViewTest, Comparisons2) {
EXPECT_LT(digits.compare(0, npos, "0123456789", 3, 5), 0); // 6
}
+TEST(StringViewTest, At) {
+ absl::string_view abc = "abc";
+ EXPECT_EQ(abc.at(0), 'a');
+ EXPECT_EQ(abc.at(1), 'b');
+ EXPECT_EQ(abc.at(2), 'c');
+#ifdef ABSL_HAVE_EXCEPTIONS
+ EXPECT_THROW(abc.at(3), std::out_of_range);
+#else
+ ABSL_EXPECT_DEATH_IF_SUPPORTED(abc.at(3), "absl::string_view::at");
+#endif
+}
+
struct MyCharAlloc : std::allocator<char> {};
TEST(StringViewTest, ExplicitConversionOperator) {
@@ -915,6 +943,31 @@ TEST(StringViewTest, NullSafeStringView) {
}
}
+TEST(StringViewTest, ConstexprNullSafeStringView) {
+ {
+ constexpr absl::string_view s = absl::NullSafeStringView(nullptr);
+ EXPECT_EQ(nullptr, s.data());
+ EXPECT_EQ(0, s.size());
+ EXPECT_EQ(absl::string_view(), s);
+ }
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
+ // MSVC 2017+ is required for good constexpr string_view support.
+ // See the implementation of `absl::string_view::StrlenInternal()`.
+ {
+ static constexpr char kHi[] = "hi";
+ absl::string_view s = absl::NullSafeStringView(kHi);
+ EXPECT_EQ(kHi, s.data());
+ EXPECT_EQ(strlen(kHi), s.size());
+ EXPECT_EQ(absl::string_view("hi"), s);
+ }
+ {
+ constexpr absl::string_view s = absl::NullSafeStringView("hello");
+ EXPECT_EQ(s.size(), 5);
+ EXPECT_EQ("hello", s);
+ }
+#endif
+}
+
TEST(StringViewTest, ConstexprCompiles) {
constexpr absl::string_view sp;
#ifdef ABSL_HAVE_STRING_VIEW_FROM_NULLPTR
@@ -922,7 +975,7 @@ TEST(StringViewTest, ConstexprCompiles) {
#endif
constexpr absl::string_view cstr_len("cstr", 4);
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
+#if defined(ABSL_USES_STD_STRING_VIEW)
// In libstdc++ (as of 7.2), `std::string_view::string_view(const char*)`
// calls `std::char_traits<char>::length(const char*)` to get the std::string
// length, but it is not marked constexpr yet. See GCC bug:
@@ -936,7 +989,7 @@ TEST(StringViewTest, ConstexprCompiles) {
#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1
#endif // !__GLIBCXX__
-#else // ABSL_HAVE_STD_STRING_VIEW
+#else // ABSL_USES_STD_STRING_VIEW
// This duplicates the check for __builtin_strlen in the header.
#if ABSL_HAVE_BUILTIN(__builtin_strlen) || \
@@ -951,13 +1004,36 @@ TEST(StringViewTest, ConstexprCompiles) {
#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1
#endif
-#endif // ABSL_HAVE_STD_STRING_VIEW
+#endif // ABSL_USES_STD_STRING_VIEW
#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR
constexpr absl::string_view cstr_strlen("foo");
EXPECT_EQ(cstr_strlen.length(), 3);
constexpr absl::string_view cstr_strlen2 = "bar";
EXPECT_EQ(cstr_strlen2, "bar");
+
+#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \
+ (defined(__GNUC__) && !defined(__clang__))
+#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON 1
+#endif
+#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON
+ constexpr absl::string_view foo = "foo";
+ constexpr absl::string_view bar = "bar";
+ constexpr bool foo_eq_bar = foo == bar;
+ constexpr bool foo_ne_bar = foo != bar;
+ constexpr bool foo_lt_bar = foo < bar;
+ constexpr bool foo_le_bar = foo <= bar;
+ constexpr bool foo_gt_bar = foo > bar;
+ constexpr bool foo_ge_bar = foo >= bar;
+ constexpr int foo_compare_bar = foo.compare(bar);
+ EXPECT_FALSE(foo_eq_bar);
+ EXPECT_TRUE(foo_ne_bar);
+ EXPECT_FALSE(foo_lt_bar);
+ EXPECT_FALSE(foo_le_bar);
+ EXPECT_TRUE(foo_gt_bar);
+ EXPECT_TRUE(foo_ge_bar);
+ EXPECT_GT(foo_compare_bar, 0);
+#endif
#endif
#if !defined(__clang__) || 3 < __clang_major__ || \
@@ -979,8 +1055,16 @@ TEST(StringViewTest, ConstexprCompiles) {
constexpr absl::string_view::iterator const_end = cstr_len.end();
constexpr absl::string_view::size_type const_size = cstr_len.size();
constexpr absl::string_view::size_type const_length = cstr_len.length();
+ static_assert(const_begin + const_size == const_end,
+ "pointer arithmetic check");
+ static_assert(const_begin + const_length == const_end,
+ "pointer arithmetic check");
+#ifndef _MSC_VER
+ // MSVC has bugs doing constexpr pointer arithmetic.
+ // https://developercommunity.visualstudio.com/content/problem/482192/bad-pointer-arithmetic-in-constepxr-2019-rc1-svc1.html
EXPECT_EQ(const_begin + const_size, const_end);
EXPECT_EQ(const_begin + const_length, const_end);
+#endif
constexpr bool isempty = sp.empty();
EXPECT_TRUE(isempty);
@@ -1036,6 +1120,17 @@ TEST(StringViewTest, Noexcept) {
EXPECT_TRUE(noexcept(sp.find_last_not_of('f')));
}
+TEST(StringViewTest, BoundsCheck) {
+#ifndef ABSL_USES_STD_STRING_VIEW
+#ifndef NDEBUG
+ // Abseil's string_view implementation has bounds-checking in debug mode.
+ absl::string_view h = "hello";
+ ABSL_EXPECT_DEATH_IF_SUPPORTED(h[5], "");
+ ABSL_EXPECT_DEATH_IF_SUPPORTED(h[-1], "");
+#endif
+#endif
+}
+
TEST(ComparisonOpsTest, StringCompareNotAmbiguous) {
EXPECT_EQ("hello", std::string("hello"));
EXPECT_LT("hello", std::string("world"));
@@ -1089,7 +1184,7 @@ TEST(HugeStringView, TwoPointTwoGB) {
}
#endif // THREAD_SANITIZER
-#if !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW)
+#if !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW)
TEST(NonNegativeLenTest, NonNegativeLen) {
ABSL_EXPECT_DEATH_IF_SUPPORTED(absl::string_view("xyz", -1),
"len <= kMaxSize");
@@ -1105,7 +1200,7 @@ TEST(LenExceedsMaxSizeTest, LenExceedsMaxSize) {
ABSL_EXPECT_DEATH_IF_SUPPORTED(absl::string_view("", max_size + 1),
"len <= kMaxSize");
}
-#endif // !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW)
+#endif // !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW)
class StringViewStreamTest : public ::testing::Test {
public:
diff --git a/absl/strings/strip.h b/absl/strings/strip.h
index 8e2ae422..111872ca 100644
--- a/absl/strings/strip.h
+++ b/absl/strings/strip.h
@@ -30,7 +30,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// ConsumePrefix()
//
@@ -85,7 +85,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix(
return str;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_STRIP_H_
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc
index 6bc9641d..5b69a3ef 100644
--- a/absl/strings/substitute.cc
+++ b/absl/strings/substitute.cc
@@ -23,7 +23,7 @@
#include "absl/strings/string_view.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace substitute_internal {
void SubstituteAndAppendArray(std::string* output, absl::string_view format,
@@ -36,7 +36,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format,
if (i + 1 >= format.size()) {
#ifndef NDEBUG
ABSL_RAW_LOG(FATAL,
- "Invalid strings::Substitute() format std::string: \"%s\".",
+ "Invalid absl::Substitute() format std::string: \"%s\".",
absl::CEscape(format).c_str());
#endif
return;
@@ -46,7 +46,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format,
#ifndef NDEBUG
ABSL_RAW_LOG(
FATAL,
- "Invalid strings::Substitute() format std::string: asked for \"$"
+ "Invalid absl::Substitute() format std::string: asked for \"$"
"%d\", but only %d args were given. Full format std::string was: "
"\"%s\".",
index, static_cast<int>(num_args), absl::CEscape(format).c_str());
@@ -61,7 +61,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format,
} else {
#ifndef NDEBUG
ABSL_RAW_LOG(FATAL,
- "Invalid strings::Substitute() format std::string: \"%s\".",
+ "Invalid absl::Substitute() format std::string: \"%s\".",
absl::CEscape(format).c_str());
#endif
return;
@@ -95,7 +95,6 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format,
assert(target == output->data() + output->size());
}
-static const char kHexDigits[] = "0123456789abcdef";
Arg::Arg(const void* value) {
static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2,
"fix sizeof(scratch_)");
@@ -105,7 +104,7 @@ Arg::Arg(const void* value) {
char* ptr = scratch_ + sizeof(scratch_);
uintptr_t num = reinterpret_cast<uintptr_t>(value);
do {
- *--ptr = kHexDigits[num & 0xf];
+ *--ptr = absl::numbers_internal::kHexChar[num & 0xf];
num >>= 4;
} while (num != 0);
*--ptr = 'x';
@@ -120,7 +119,7 @@ Arg::Arg(Hex hex) {
char* writer = end;
uint64_t value = hex.value;
do {
- *--writer = kHexDigits[value & 0xF];
+ *--writer = absl::numbers_internal::kHexChar[value & 0xF];
value >>= 4;
} while (value != 0);
@@ -168,5 +167,5 @@ Arg::Arg(Dec dec) {
}
} // namespace substitute_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h
index 3ba7f4d3..4d0984d3 100644
--- a/absl/strings/substitute.h
+++ b/absl/strings/substitute.h
@@ -86,7 +86,7 @@
#include "absl/strings/strip.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace substitute_internal {
// Arg
@@ -190,7 +190,12 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format,
#if defined(ABSL_BAD_CALL_IF)
constexpr int CalculateOneBit(const char* format) {
- return (*format < '0' || *format > '9') ? 0 : (1 << (*format - '0'));
+ // Returns:
+ // * 2^N for '$N' when N is in [0-9]
+ // * 0 for correct '$' escaping: '$$'.
+ // * -1 otherwise.
+ return (*format < '0' || *format > '9') ? (*format == '$' ? 0 : -1)
+ : (1 << (*format - '0'));
}
constexpr const char* SkipNumber(const char* format) {
@@ -682,7 +687,7 @@ std::string Substitute(
"format std::string doesn't contain all of $0 through $9");
#endif // ABSL_BAD_CALL_IF
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_SUBSTITUTE_H_
diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc
index e27abb17..450cd2bc 100644
--- a/absl/strings/substitute_test.cc
+++ b/absl/strings/substitute_test.cc
@@ -189,14 +189,14 @@ TEST(SubstituteTest, VectorBoolRef) {
TEST(SubstituteDeathTest, SubstituteDeath) {
EXPECT_DEBUG_DEATH(
static_cast<void>(absl::Substitute(absl::string_view("-$2"), "a", "b")),
- "Invalid strings::Substitute\\(\\) format std::string: asked for \"\\$2\", "
+ "Invalid absl::Substitute\\(\\) format std::string: asked for \"\\$2\", "
"but only 2 args were given.");
EXPECT_DEBUG_DEATH(
- static_cast<void>(absl::Substitute("-$z-")),
- "Invalid strings::Substitute\\(\\) format std::string: \"-\\$z-\"");
+ static_cast<void>(absl::Substitute(absl::string_view("-$z-"))),
+ "Invalid absl::Substitute\\(\\) format std::string: \"-\\$z-\"");
EXPECT_DEBUG_DEATH(
- static_cast<void>(absl::Substitute("-$")),
- "Invalid strings::Substitute\\(\\) format std::string: \"-\\$\"");
+ static_cast<void>(absl::Substitute(absl::string_view("-$"))),
+ "Invalid absl::Substitute\\(\\) format std::string: \"-\\$\"");
}
#endif // GTEST_HAS_DEATH_TEST
diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel
index fca8cb69..3f876b9f 100644
--- a/absl/synchronization/BUILD.bazel
+++ b/absl/synchronization/BUILD.bazel
@@ -14,6 +14,7 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@@ -42,8 +43,25 @@ cc_library(
deps = [
"//absl/base",
"//absl/base:base_internal",
+ "//absl/base:config",
"//absl/base:core_headers",
"//absl/base:malloc_internal",
+ "//absl/base:raw_logging_internal",
+ ],
+)
+
+cc_library(
+ name = "kernel_timeout_internal",
+ hdrs = ["internal/kernel_timeout.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ visibility = [
+ "//absl/synchronization:__pkg__",
+ ],
+ deps = [
+ "//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
+ "//absl/time",
],
)
@@ -63,7 +81,6 @@ cc_library(
"barrier.h",
"blocking_counter.h",
"internal/create_thread_identity.h",
- "internal/kernel_timeout.h",
"internal/mutex_nonprod.inc",
"internal/per_thread_sem.h",
"internal/waiter.h",
@@ -77,6 +94,7 @@ cc_library(
}) + ABSL_DEFAULT_LINKOPTS,
deps = [
":graphcycles_internal",
+ ":kernel_timeout_internal",
"//absl/base",
"//absl/base:atomic_hook",
"//absl/base:base_internal",
@@ -84,6 +102,7 @@ cc_library(
"//absl/base:core_headers",
"//absl/base:dynamic_annotations",
"//absl/base:malloc_internal",
+ "//absl/base:raw_logging_internal",
"//absl/debugging:stacktrace",
"//absl/debugging:symbolize",
"//absl/time",
@@ -124,8 +143,8 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":graphcycles_internal",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"@com_google_googletest//:gtest_main",
],
)
@@ -140,7 +159,7 @@ cc_test(
],
deps = [
":graphcycles_internal",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -171,6 +190,7 @@ cc_test(
":thread_pool",
"//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/memory",
"//absl/time",
"@com_google_googletest//:gtest_main",
@@ -243,7 +263,6 @@ cc_test(
deps = [
":per_thread_sem_test_common",
":synchronization",
- "//absl/base",
"//absl/strings",
"//absl/time",
"@com_google_googletest//:gtest_main",
@@ -260,7 +279,7 @@ cc_test(
tags = ["no_test_ios_x86_64"],
deps = [
":synchronization",
- "//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
],
)
diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt
index 4b708823..dfe5d05d 100644
--- a/absl/synchronization/CMakeLists.txt
+++ b/absl/synchronization/CMakeLists.txt
@@ -26,8 +26,23 @@ absl_cc_library(
DEPS
absl::base
absl::base_internal
+ absl::config
absl::core_headers
absl::malloc_internal
+ absl::raw_logging_internal
+)
+
+absl_cc_library(
+ NAME
+ kernel_timeout_internal
+ HDRS
+ "internal/kernel_timeout.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::core_headers
+ absl::raw_logging_internal
+ absl::time
)
absl_cc_library(
@@ -37,7 +52,6 @@ absl_cc_library(
"barrier.h"
"blocking_counter.h"
"internal/create_thread_identity.h"
- "internal/kernel_timeout.h"
"internal/mutex_nonprod.inc"
"internal/per_thread_sem.h"
"internal/waiter.h"
@@ -55,6 +69,7 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::graphcycles_internal
+ absl::kernel_timeout_internal
absl::atomic_hook
absl::base
absl::base_internal
@@ -62,6 +77,7 @@ absl_cc_library(
absl::core_headers
absl::dynamic_annotations
absl::malloc_internal
+ absl::raw_logging_internal
absl::stacktrace
absl::symbolize
absl::time
@@ -104,8 +120,8 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::graphcycles_internal
- absl::base
absl::core_headers
+ absl::raw_logging_internal
gmock_main
)
@@ -135,6 +151,7 @@ absl_cc_test(
absl::base
absl::core_headers
absl::memory
+ absl::raw_logging_internal
absl::time
gmock_main
)
@@ -178,7 +195,6 @@ absl_cc_test(
DEPS
absl::per_thread_sem_test_common
absl::synchronization
- absl::base
absl::strings
absl::time
gmock_main
@@ -193,6 +209,6 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::synchronization
- absl::base
absl::core_headers
+ absl::raw_logging_internal
)
diff --git a/absl/synchronization/barrier.cc b/absl/synchronization/barrier.cc
index 72089c52..0dfd795e 100644
--- a/absl/synchronization/barrier.cc
+++ b/absl/synchronization/barrier.cc
@@ -18,7 +18,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Return whether int *arg is zero.
static bool IsZero(void *arg) {
@@ -48,5 +48,5 @@ bool Barrier::Block() {
return this->num_to_exit_ == 0;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/barrier.h b/absl/synchronization/barrier.h
index 53d5ca26..d8e75440 100644
--- a/absl/synchronization/barrier.h
+++ b/absl/synchronization/barrier.h
@@ -23,7 +23,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Barrier
//
@@ -70,10 +70,10 @@ class Barrier {
private:
Mutex lock_;
- int num_to_block_ GUARDED_BY(lock_);
- int num_to_exit_ GUARDED_BY(lock_);
+ int num_to_block_ ABSL_GUARDED_BY(lock_);
+ int num_to_exit_ ABSL_GUARDED_BY(lock_);
};
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_BARRIER_H_
diff --git a/absl/synchronization/blocking_counter.cc b/absl/synchronization/blocking_counter.cc
index c6968973..3cea7aed 100644
--- a/absl/synchronization/blocking_counter.cc
+++ b/absl/synchronization/blocking_counter.cc
@@ -17,7 +17,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Return whether int *arg is zero.
static bool IsZero(void *arg) {
@@ -53,5 +53,5 @@ void BlockingCounter::Wait() {
// after we return from this method.
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/blocking_counter.h b/absl/synchronization/blocking_counter.h
index 5dab5a94..1f53f9f2 100644
--- a/absl/synchronization/blocking_counter.h
+++ b/absl/synchronization/blocking_counter.h
@@ -24,7 +24,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// BlockingCounter
//
@@ -89,11 +89,11 @@ class BlockingCounter {
private:
Mutex lock_;
- int count_ GUARDED_BY(lock_);
- int num_waiting_ GUARDED_BY(lock_);
+ int count_ ABSL_GUARDED_BY(lock_);
+ int num_waiting_ ABSL_GUARDED_BY(lock_);
};
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_
diff --git a/absl/synchronization/blocking_counter_test.cc b/absl/synchronization/blocking_counter_test.cc
index 62d98738..2926224a 100644
--- a/absl/synchronization/blocking_counter_test.cc
+++ b/absl/synchronization/blocking_counter_test.cc
@@ -22,7 +22,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
void PauseAndDecreaseCounter(BlockingCounter* counter, int* done) {
@@ -64,5 +64,5 @@ TEST(BlockingCounterTest, BasicFunctionality) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc
index 65f6d8fc..fa0070a9 100644
--- a/absl/synchronization/internal/create_thread_identity.cc
+++ b/absl/synchronization/internal/create_thread_identity.cc
@@ -27,7 +27,7 @@
#include "absl/synchronization/internal/per_thread_sem.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
// ThreadIdentity storage is persistent, we maintain a free-list of previously
@@ -38,7 +38,7 @@ static base_internal::ThreadIdentity* thread_identity_freelist;
// A per-thread destructor for reclaiming associated ThreadIdentity objects.
// Since we must preserve their storage we cache them for re-use.
-static void ReclaimThreadIdentity(void* v) {
+void ReclaimThreadIdentity(void* v) {
base_internal::ThreadIdentity* identity =
static_cast<base_internal::ThreadIdentity*>(v);
@@ -48,6 +48,8 @@ static void ReclaimThreadIdentity(void* v) {
base_internal::LowLevelAlloc::Free(identity->per_thread_synch.all_locks);
}
+ PerThreadSem::Destroy(identity);
+
// We must explicitly clear the current thread's identity:
// (a) Subsequent (unrelated) per-thread destructors may require an identity.
// We must guarantee a new identity is used in this case (this instructor
@@ -85,7 +87,6 @@ static void ResetThreadIdentity(base_internal::ThreadIdentity* identity) {
pts->wake = false;
pts->cond_waiter = false;
pts->all_locks = nullptr;
- identity->waiter_state = {};
identity->blocked_count_ptr = nullptr;
identity->ticker.store(0, std::memory_order_relaxed);
identity->wait_start.store(0, std::memory_order_relaxed);
@@ -133,7 +134,7 @@ base_internal::ThreadIdentity* CreateThreadIdentity() {
}
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/absl/synchronization/internal/create_thread_identity.h b/absl/synchronization/internal/create_thread_identity.h
index d743cc3b..e121f683 100644
--- a/absl/synchronization/internal/create_thread_identity.h
+++ b/absl/synchronization/internal/create_thread_identity.h
@@ -29,13 +29,17 @@
#include "absl/base/port.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
// Allocates and attaches a ThreadIdentity object for the calling thread.
// For private use only.
base_internal::ThreadIdentity* CreateThreadIdentity();
+// A per-thread destructor for reclaiming associated ThreadIdentity objects.
+// For private use only.
+void ReclaimThreadIdentity(void* v);
+
// Returns the ThreadIdentity object representing the calling thread; guaranteed
// to be unique for its lifetime. The returned object will remain valid for the
// program's lifetime; although it may be re-assigned to a subsequent thread.
@@ -50,7 +54,7 @@ inline base_internal::ThreadIdentity* GetOrCreateCurrentThreadIdentity() {
}
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_INTERNAL_CREATE_THREAD_IDENTITY_H_
diff --git a/absl/synchronization/internal/graphcycles.cc b/absl/synchronization/internal/graphcycles.cc
index f4fbeadd..6a2bcdf6 100644
--- a/absl/synchronization/internal/graphcycles.cc
+++ b/absl/synchronization/internal/graphcycles.cc
@@ -44,7 +44,7 @@
// Do not use STL. This module does not use standard memory allocation.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
namespace {
@@ -691,7 +691,7 @@ int GraphCycles::GetStackTrace(GraphId id, void*** ptr) {
}
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_LOW_LEVEL_ALLOC_MISSING
diff --git a/absl/synchronization/internal/graphcycles.h b/absl/synchronization/internal/graphcycles.h
index 208527c3..ceba33e4 100644
--- a/absl/synchronization/internal/graphcycles.h
+++ b/absl/synchronization/internal/graphcycles.h
@@ -36,12 +36,14 @@
// InsertEdge() is very fast when the edge already exists, and reasonably fast
// otherwise.
// FindPath() is linear in the size of the graph.
-// The current implemenation uses O(|V|+|E|) space.
+// The current implementation uses O(|V|+|E|) space.
#include <cstdint>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
// Opaque identifier for a graph node.
@@ -133,7 +135,7 @@ class GraphCycles {
};
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif
diff --git a/absl/synchronization/internal/graphcycles_test.cc b/absl/synchronization/internal/graphcycles_test.cc
index fca86219..74eaffe7 100644
--- a/absl/synchronization/internal/graphcycles_test.cc
+++ b/absl/synchronization/internal/graphcycles_test.cc
@@ -25,7 +25,7 @@
#include "absl/base/macros.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
// We emulate a GraphCycles object with a node vector and an edge vector.
@@ -460,5 +460,5 @@ TEST_F(GraphCyclesTest, ManyEdges) {
}
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h
index e0f01e06..d6ac5db0 100644
--- a/absl/synchronization/internal/kernel_timeout.h
+++ b/absl/synchronization/internal/kernel_timeout.h
@@ -34,7 +34,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
class Futex;
@@ -149,7 +149,7 @@ class KernelTimeout {
};
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_
diff --git a/absl/synchronization/internal/mutex_nonprod.cc b/absl/synchronization/internal/mutex_nonprod.cc
index aa1ed83b..4590b98d 100644
--- a/absl/synchronization/internal/mutex_nonprod.cc
+++ b/absl/synchronization/internal/mutex_nonprod.cc
@@ -31,7 +31,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
namespace {
@@ -316,5 +316,5 @@ bool Condition::Eval() const {
void RegisterSymbolizer(bool (*)(const void*, char*, int)) {}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/internal/mutex_nonprod.inc b/absl/synchronization/internal/mutex_nonprod.inc
index ac10879b..a1502e72 100644
--- a/absl/synchronization/internal/mutex_nonprod.inc
+++ b/absl/synchronization/internal/mutex_nonprod.inc
@@ -36,7 +36,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
class Condition;
namespace synchronization_internal {
@@ -252,10 +252,10 @@ class SynchronizationStorage {
absl::once_flag once_;
- // An aligned space for T.
- typename std::aligned_storage<sizeof(T), alignof(T)>::type space_;
+ // An aligned space for the T.
+ alignas(T) unsigned char space_[sizeof(T)];
};
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc
index 284a5df4..821ca9b4 100644
--- a/absl/synchronization/internal/per_thread_sem.cc
+++ b/absl/synchronization/internal/per_thread_sem.cc
@@ -25,7 +25,7 @@
#include "absl/synchronization/internal/waiter.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
void PerThreadSem::SetThreadBlockedCounter(std::atomic<int> *counter) {
@@ -41,12 +41,16 @@ std::atomic<int> *PerThreadSem::GetThreadBlockedCounter() {
}
void PerThreadSem::Init(base_internal::ThreadIdentity *identity) {
- Waiter::GetWaiter(identity)->Init();
+ new (Waiter::GetWaiter(identity)) Waiter();
identity->ticker.store(0, std::memory_order_relaxed);
identity->wait_start.store(0, std::memory_order_relaxed);
identity->is_idle.store(false, std::memory_order_relaxed);
}
+void PerThreadSem::Destroy(base_internal::ThreadIdentity *identity) {
+ Waiter::GetWaiter(identity)->~Waiter();
+}
+
void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) {
const int ticker =
identity->ticker.fetch_add(1, std::memory_order_relaxed) + 1;
@@ -59,7 +63,7 @@ void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) {
}
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
extern "C" {
diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h
index 5bb0978b..8ab43915 100644
--- a/absl/synchronization/internal/per_thread_sem.h
+++ b/absl/synchronization/internal/per_thread_sem.h
@@ -32,7 +32,7 @@
#include "absl/synchronization/internal/kernel_timeout.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
class Mutex;
@@ -66,6 +66,10 @@ class PerThreadSem {
// REQUIRES: May only be called by ThreadIdentity.
static void Init(base_internal::ThreadIdentity* identity);
+ // Destroy the PerThreadSem associated with "identity".
+ // REQUIRES: May only be called by ThreadIdentity.
+ static void Destroy(base_internal::ThreadIdentity* identity);
+
// Increments "identity"'s count.
static inline void Post(base_internal::ThreadIdentity* identity);
@@ -78,10 +82,11 @@ class PerThreadSem {
friend class PerThreadSemTest;
friend class absl::Mutex;
friend absl::base_internal::ThreadIdentity* CreateThreadIdentity();
+ friend void ReclaimThreadIdentity(void* v);
};
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// In some build configurations we pass --detect-odr-violations to the
diff --git a/absl/synchronization/internal/per_thread_sem_test.cc b/absl/synchronization/internal/per_thread_sem_test.cc
index 93bc4244..b5a2f6d4 100644
--- a/absl/synchronization/internal/per_thread_sem_test.cc
+++ b/absl/synchronization/internal/per_thread_sem_test.cc
@@ -33,7 +33,7 @@
// primitives which might use PerThreadSem, most notably absl::Mutex.
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
class SimpleSemaphore {
@@ -176,5 +176,5 @@ TEST_F(PerThreadSemTest, Timeouts) {
} // namespace
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/internal/thread_pool.h b/absl/synchronization/internal/thread_pool.h
index 8941be68..0cb96dac 100644
--- a/absl/synchronization/internal/thread_pool.h
+++ b/absl/synchronization/internal/thread_pool.h
@@ -26,7 +26,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
// A simple ThreadPool implementation for tests.
@@ -61,7 +61,7 @@ class ThreadPool {
}
private:
- bool WorkAvailable() const EXCLUSIVE_LOCKS_REQUIRED(mu_) {
+ bool WorkAvailable() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
return !queue_.empty();
}
@@ -82,12 +82,12 @@ class ThreadPool {
}
absl::Mutex mu_;
- std::queue<std::function<void()>> queue_ GUARDED_BY(mu_);
+ std::queue<std::function<void()>> queue_ ABSL_GUARDED_BY(mu_);
std::vector<std::thread> threads_;
};
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_INTERNAL_THREAD_POOL_H_
diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc
index 17c6a506..2949f5a8 100644
--- a/absl/synchronization/internal/waiter.cc
+++ b/absl/synchronization/internal/waiter.cc
@@ -49,7 +49,7 @@
#include "absl/synchronization/internal/kernel_timeout.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
static void MaybeBecomeIdle() {
@@ -123,16 +123,21 @@ class Futex {
}
};
-void Waiter::Init() {
+Waiter::Waiter() {
futex_.store(0, std::memory_order_relaxed);
}
+Waiter::~Waiter() = default;
+
bool Waiter::Wait(KernelTimeout t) {
// Loop until we can atomically decrement futex from a positive
// value, waiting on a futex while we believe it is zero.
+ // Note that, since the thread ticker is just reset, we don't need to check
+ // whether the thread is idle on the very first pass of the loop.
+ bool first_pass = true;
while (true) {
int32_t x = futex_.load(std::memory_order_relaxed);
- if (x != 0) {
+ while (x != 0) {
if (!futex_.compare_exchange_weak(x, x - 1,
std::memory_order_acquire,
std::memory_order_relaxed)) {
@@ -141,6 +146,8 @@ bool Waiter::Wait(KernelTimeout t) {
return true; // Consumed a wakeup, we are done.
}
+
+ if (!first_pass) MaybeBecomeIdle();
const int err = Futex::WaitUntil(&futex_, 0, t);
if (err != 0) {
if (err == -EINTR || err == -EWOULDBLOCK) {
@@ -151,14 +158,13 @@ bool Waiter::Wait(KernelTimeout t) {
ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err);
}
}
-
- MaybeBecomeIdle();
+ first_pass = false;
}
}
void Waiter::Post() {
if (futex_.fetch_add(1, std::memory_order_release) == 0) {
- // We incremented from 0, need to wake a potential waker.
+ // We incremented from 0, need to wake a potential waiter.
Poke();
}
}
@@ -196,7 +202,7 @@ class PthreadMutexHolder {
pthread_mutex_t *mu_;
};
-void Waiter::Init() {
+Waiter::Waiter() {
const int err = pthread_mutex_init(&mu_, 0);
if (err != 0) {
ABSL_RAW_LOG(FATAL, "pthread_mutex_init failed: %d", err);
@@ -207,8 +213,20 @@ void Waiter::Init() {
ABSL_RAW_LOG(FATAL, "pthread_cond_init failed: %d", err2);
}
- waiter_count_.store(0, std::memory_order_relaxed);
- wakeup_count_.store(0, std::memory_order_relaxed);
+ waiter_count_ = 0;
+ wakeup_count_ = 0;
+}
+
+Waiter::~Waiter() {
+ const int err = pthread_mutex_destroy(&mu_);
+ if (err != 0) {
+ ABSL_RAW_LOG(FATAL, "pthread_mutex_destroy failed: %d", err);
+ }
+
+ const int err2 = pthread_cond_destroy(&cv_);
+ if (err2 != 0) {
+ ABSL_RAW_LOG(FATAL, "pthread_cond_destroy failed: %d", err2);
+ }
}
bool Waiter::Wait(KernelTimeout t) {
@@ -218,21 +236,13 @@ bool Waiter::Wait(KernelTimeout t) {
}
PthreadMutexHolder h(&mu_);
- waiter_count_.fetch_add(1, std::memory_order_relaxed);
+ ++waiter_count_;
// Loop until we find a wakeup to consume or timeout.
- while (true) {
- int x = wakeup_count_.load(std::memory_order_relaxed);
- if (x != 0) {
- if (!wakeup_count_.compare_exchange_weak(x, x - 1,
- std::memory_order_acquire,
- std::memory_order_relaxed)) {
- continue; // Raced with someone, retry.
- }
- // Successfully consumed a wakeup, we're done.
- waiter_count_.fetch_sub(1, std::memory_order_relaxed);
- return true;
- }
-
+ // Note that, since the thread ticker is just reset, we don't need to check
+ // whether the thread is idle on the very first pass of the loop.
+ bool first_pass = true;
+ while (wakeup_count_ == 0) {
+ if (!first_pass) MaybeBecomeIdle();
// No wakeups available, time to wait.
if (!t.has_timeout()) {
const int err = pthread_cond_wait(&cv_, &mu_);
@@ -242,46 +252,56 @@ bool Waiter::Wait(KernelTimeout t) {
} else {
const int err = pthread_cond_timedwait(&cv_, &mu_, &abs_timeout);
if (err == ETIMEDOUT) {
- waiter_count_.fetch_sub(1, std::memory_order_relaxed);
+ --waiter_count_;
return false;
}
if (err != 0) {
- ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err);
+ ABSL_RAW_LOG(FATAL, "pthread_cond_timedwait failed: %d", err);
}
}
- MaybeBecomeIdle();
+ first_pass = false;
}
+ // Consume a wakeup and we're done.
+ --wakeup_count_;
+ --waiter_count_;
+ return true;
}
void Waiter::Post() {
- wakeup_count_.fetch_add(1, std::memory_order_release);
- Poke();
+ PthreadMutexHolder h(&mu_);
+ ++wakeup_count_;
+ InternalCondVarPoke();
}
void Waiter::Poke() {
- if (waiter_count_.load(std::memory_order_relaxed) == 0) {
- return;
- }
- // Potentially a waker. Take the lock and check again.
PthreadMutexHolder h(&mu_);
- if (waiter_count_.load(std::memory_order_relaxed) == 0) {
- return;
- }
- const int err = pthread_cond_signal(&cv_);
- if (err != 0) {
- ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err);
+ InternalCondVarPoke();
+}
+
+void Waiter::InternalCondVarPoke() {
+ if (waiter_count_ != 0) {
+ const int err = pthread_cond_signal(&cv_);
+ if (ABSL_PREDICT_FALSE(err != 0)) {
+ ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err);
+ }
}
}
#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM
-void Waiter::Init() {
+Waiter::Waiter() {
if (sem_init(&sem_, 0, 0) != 0) {
ABSL_RAW_LOG(FATAL, "sem_init failed with errno %d\n", errno);
}
wakeups_.store(0, std::memory_order_relaxed);
}
+Waiter::~Waiter() {
+ if (sem_destroy(&sem_) != 0) {
+ ABSL_RAW_LOG(FATAL, "sem_destroy failed with errno %d\n", errno);
+ }
+}
+
bool Waiter::Wait(KernelTimeout t) {
struct timespec abs_timeout;
if (t.has_timeout()) {
@@ -289,9 +309,12 @@ bool Waiter::Wait(KernelTimeout t) {
}
// Loop until we timeout or consume a wakeup.
+ // Note that, since the thread ticker is just reset, we don't need to check
+ // whether the thread is idle on the very first pass of the loop.
+ bool first_pass = true;
while (true) {
int x = wakeups_.load(std::memory_order_relaxed);
- if (x != 0) {
+ while (x != 0) {
if (!wakeups_.compare_exchange_weak(x, x - 1,
std::memory_order_acquire,
std::memory_order_relaxed)) {
@@ -301,6 +324,7 @@ bool Waiter::Wait(KernelTimeout t) {
return true;
}
+ if (!first_pass) MaybeBecomeIdle();
// Nothing to consume, wait (looping on EINTR).
while (true) {
if (!t.has_timeout()) {
@@ -314,13 +338,16 @@ bool Waiter::Wait(KernelTimeout t) {
ABSL_RAW_LOG(FATAL, "sem_timedwait failed: %d", errno);
}
}
- MaybeBecomeIdle();
+ first_pass = false;
}
}
void Waiter::Post() {
- wakeups_.fetch_add(1, std::memory_order_release); // Post a wakeup.
- Poke();
+ // Post a wakeup.
+ if (wakeups_.fetch_add(1, std::memory_order_release) == 0) {
+ // We incremented from 0, need to wake a potential waiter.
+ Poke();
+ }
}
void Waiter::Poke() {
@@ -341,31 +368,29 @@ class Waiter::WinHelper {
return reinterpret_cast<CONDITION_VARIABLE *>(&w->cv_storage_);
}
- static_assert(sizeof(SRWLOCK) == sizeof(Waiter::SRWLockStorage),
- "SRWLockStorage does not have the same size as SRWLOCK");
+ static_assert(sizeof(SRWLOCK) == sizeof(void *),
+ "`mu_storage_` does not have the same size as SRWLOCK");
+ static_assert(alignof(SRWLOCK) == alignof(void *),
+ "`mu_storage_` does not have the same alignment as SRWLOCK");
+
+ static_assert(sizeof(CONDITION_VARIABLE) == sizeof(void *),
+ "`ABSL_CONDITION_VARIABLE_STORAGE` does not have the same size "
+ "as `CONDITION_VARIABLE`");
static_assert(
- alignof(SRWLOCK) == alignof(Waiter::SRWLockStorage),
- "SRWLockStorage does not have the same alignment as SRWLOCK");
-
- static_assert(sizeof(CONDITION_VARIABLE) ==
- sizeof(Waiter::ConditionVariableStorage),
- "ABSL_CONDITION_VARIABLE_STORAGE does not have the same size "
- "as CONDITION_VARIABLE");
- static_assert(alignof(CONDITION_VARIABLE) ==
- alignof(Waiter::ConditionVariableStorage),
- "ConditionVariableStorage does not have the same "
- "alignment as CONDITION_VARIABLE");
-
- // The SRWLOCK and CONDITION_VARIABLE types must be trivially constuctible
+ alignof(CONDITION_VARIABLE) == alignof(void *),
+ "`cv_storage_` does not have the same alignment as `CONDITION_VARIABLE`");
+
+ // The SRWLOCK and CONDITION_VARIABLE types must be trivially constructible
// and destructible because we never call their constructors or destructors.
static_assert(std::is_trivially_constructible<SRWLOCK>::value,
- "The SRWLOCK type must be trivially constructible");
- static_assert(std::is_trivially_constructible<CONDITION_VARIABLE>::value,
- "The CONDITION_VARIABLE type must be trivially constructible");
+ "The `SRWLOCK` type must be trivially constructible");
+ static_assert(
+ std::is_trivially_constructible<CONDITION_VARIABLE>::value,
+ "The `CONDITION_VARIABLE` type must be trivially constructible");
static_assert(std::is_trivially_destructible<SRWLOCK>::value,
- "The SRWLOCK type must be trivially destructible");
+ "The `SRWLOCK` type must be trivially destructible");
static_assert(std::is_trivially_destructible<CONDITION_VARIABLE>::value,
- "The CONDITION_VARIABLE type must be trivially destructible");
+ "The `CONDITION_VARIABLE` type must be trivially destructible");
};
class LockHolder {
@@ -385,36 +410,33 @@ class LockHolder {
SRWLOCK* mu_;
};
-void Waiter::Init() {
+Waiter::Waiter() {
auto *mu = ::new (static_cast<void *>(&mu_storage_)) SRWLOCK;
auto *cv = ::new (static_cast<void *>(&cv_storage_)) CONDITION_VARIABLE;
InitializeSRWLock(mu);
InitializeConditionVariable(cv);
- waiter_count_.store(0, std::memory_order_relaxed);
- wakeup_count_.store(0, std::memory_order_relaxed);
+ waiter_count_ = 0;
+ wakeup_count_ = 0;
}
+// SRW locks and condition variables do not need to be explicitly destroyed.
+// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock
+// https://stackoverflow.com/questions/28975958/why-does-windows-have-no-deleteconditionvariable-function-to-go-together-with
+Waiter::~Waiter() = default;
+
bool Waiter::Wait(KernelTimeout t) {
SRWLOCK *mu = WinHelper::GetLock(this);
CONDITION_VARIABLE *cv = WinHelper::GetCond(this);
LockHolder h(mu);
- waiter_count_.fetch_add(1, std::memory_order_relaxed);
+ ++waiter_count_;
// Loop until we find a wakeup to consume or timeout.
- while (true) {
- int x = wakeup_count_.load(std::memory_order_relaxed);
- if (x != 0) {
- if (!wakeup_count_.compare_exchange_weak(x, x - 1,
- std::memory_order_acquire,
- std::memory_order_relaxed)) {
- continue; // Raced with someone, retry.
- }
- // Successfully consumed a wakeup, we're done.
- waiter_count_.fetch_sub(1, std::memory_order_relaxed);
- return true;
- }
-
+ // Note that, since the thread ticker is just reset, we don't need to check
+ // whether the thread is idle on the very first pass of the loop.
+ bool first_pass = true;
+ while (wakeup_count_ == 0) {
+ if (!first_pass) MaybeBecomeIdle();
// No wakeups available, time to wait.
if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) {
// GetLastError() returns a Win32 DWORD, but we assign to
@@ -422,32 +444,35 @@ bool Waiter::Wait(KernelTimeout t) {
// initialization guarantees this is not a narrowing conversion.
const unsigned long err{GetLastError()}; // NOLINT(runtime/int)
if (err == ERROR_TIMEOUT) {
- waiter_count_.fetch_sub(1, std::memory_order_relaxed);
+ --waiter_count_;
return false;
} else {
ABSL_RAW_LOG(FATAL, "SleepConditionVariableSRW failed: %lu", err);
}
}
-
- MaybeBecomeIdle();
+ first_pass = false;
}
+ // Consume a wakeup and we're done.
+ --wakeup_count_;
+ --waiter_count_;
+ return true;
}
void Waiter::Post() {
- wakeup_count_.fetch_add(1, std::memory_order_release);
- Poke();
+ LockHolder h(WinHelper::GetLock(this));
+ ++wakeup_count_;
+ InternalCondVarPoke();
}
void Waiter::Poke() {
- if (waiter_count_.load(std::memory_order_relaxed) == 0) {
- return;
- }
- // Potentially a waker. Take the lock and check again.
LockHolder h(WinHelper::GetLock(this));
- if (waiter_count_.load(std::memory_order_relaxed) == 0) {
- return;
+ InternalCondVarPoke();
+}
+
+void Waiter::InternalCondVarPoke() {
+ if (waiter_count_ != 0) {
+ WakeConditionVariable(WinHelper::GetCond(this));
}
- WakeConditionVariable(WinHelper::GetCond(this));
}
#else
@@ -455,5 +480,5 @@ void Waiter::Poke() {
#endif
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h
index 06032642..a6e6d4c7 100644
--- a/absl/synchronization/internal/waiter.h
+++ b/absl/synchronization/internal/waiter.h
@@ -18,10 +18,16 @@
#include "absl/base/config.h"
-#ifndef _WIN32
+#ifdef _WIN32
+#include <sdkddkver.h>
+#else
#include <pthread.h>
#endif
+#ifdef __linux__
+#include <linux/futex.h>
+#endif
+
#ifdef ABSL_HAVE_SEMAPHORE_H
#include <semaphore.h>
#endif
@@ -40,9 +46,14 @@
#if defined(ABSL_FORCE_WAITER_MODE)
#define ABSL_WAITER_MODE ABSL_FORCE_WAITER_MODE
-#elif defined(_WIN32)
+#elif defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
#define ABSL_WAITER_MODE ABSL_WAITER_MODE_WIN32
-#elif defined(__linux__)
+#elif defined(__BIONIC__)
+// Bionic supports all the futex operations we need even when some of the futex
+// definitions are missing.
+#define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX
+#elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME)
+// FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28.
#define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX
#elif defined(ABSL_HAVE_SEMAPHORE_H)
#define ABSL_WAITER_MODE ABSL_WAITER_MODE_SEM
@@ -51,20 +62,21 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {
// Waiter is an OS-specific semaphore.
class Waiter {
public:
- // No constructor, instances use the reserved space in ThreadIdentity.
- // All initialization logic belongs in `Init()`.
- Waiter() = delete;
+ // Prepare any data to track waits.
+ Waiter();
+
+ // Not copyable or movable
Waiter(const Waiter&) = delete;
Waiter& operator=(const Waiter&) = delete;
- // Prepare any data to track waits.
- void Init();
+ // Destroy any data to track waits.
+ ~Waiter();
// Blocks the calling thread until a matching call to `Post()` or
// `t` has passed. Returns `true` if woken (`Post()` called),
@@ -105,10 +117,13 @@ class Waiter {
static_assert(sizeof(int32_t) == sizeof(futex_), "Wrong size for futex");
#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_CONDVAR
+ // REQUIRES: mu_ must be held.
+ void InternalCondVarPoke();
+
pthread_mutex_t mu_;
pthread_cond_t cv_;
- std::atomic<int> waiter_count_;
- std::atomic<int> wakeup_count_; // Unclaimed wakeups, written under lock.
+ int waiter_count_;
+ int wakeup_count_; // Unclaimed wakeups.
#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM
sem_t sem_;
@@ -118,26 +133,19 @@ class Waiter {
std::atomic<int> wakeups_;
#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_WIN32
- // The Windows API has lots of choices for synchronization
- // primivitives. We are using SRWLOCK and CONDITION_VARIABLE
- // because they don't require a destructor to release system
- // resources.
- //
- // However, we can't include Windows.h in our headers, so we use aligned
- // storage buffers to define the storage.
- using SRWLockStorage =
- typename std::aligned_storage<sizeof(void*), alignof(void*)>::type;
- using ConditionVariableStorage =
- typename std::aligned_storage<sizeof(void*), alignof(void*)>::type;
-
// WinHelper - Used to define utilities for accessing the lock and
// condition variable storage once the types are complete.
class WinHelper;
- SRWLockStorage mu_storage_;
- ConditionVariableStorage cv_storage_;
- std::atomic<int> waiter_count_;
- std::atomic<int> wakeup_count_;
+ // REQUIRES: WinHelper::GetLock(this) must be held.
+ void InternalCondVarPoke();
+
+ // We can't include Windows.h in our headers, so we use aligned charachter
+ // buffers to define the storage of SRWLOCK and CONDITION_VARIABLE.
+ alignas(void*) unsigned char mu_storage_[sizeof(void*)];
+ alignas(void*) unsigned char cv_storage_[sizeof(void*)];
+ int waiter_count_;
+ int wakeup_count_;
#else
#error Unknown ABSL_WAITER_MODE
@@ -145,7 +153,7 @@ class Waiter {
};
} // namespace synchronization_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_INTERNAL_WAITER_H_
diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc
index 0279c8f8..cc973a32 100644
--- a/absl/synchronization/lifetime_test.cc
+++ b/absl/synchronization/lifetime_test.cc
@@ -122,6 +122,11 @@ class OnDestruction {
Function fn_;
};
+// These tests require that the compiler correctly supports C++11 constant
+// initialization... but MSVC has a known regression since v19.10:
+// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html
+// TODO(epastor): Limit the affected range once MSVC fixes this bug.
+#if defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900)
// kConstInit
// Test early usage. (Declaration comes first; definitions must appear after
// the test runner.)
@@ -143,14 +148,15 @@ ABSL_CONST_INIT absl::Mutex early_const_init_mutex(absl::kConstInit);
// constructors of globals "happen at link time"; memory is pre-initialized,
// before the constructors of either grab_lock or check_still_locked are run.)
extern absl::Mutex const_init_sanity_mutex;
-OnConstruction grab_lock([]() NO_THREAD_SAFETY_ANALYSIS {
+OnConstruction grab_lock([]() ABSL_NO_THREAD_SAFETY_ANALYSIS {
const_init_sanity_mutex.Lock();
});
ABSL_CONST_INIT absl::Mutex const_init_sanity_mutex(absl::kConstInit);
-OnConstruction check_still_locked([]() NO_THREAD_SAFETY_ANALYSIS {
+OnConstruction check_still_locked([]() ABSL_NO_THREAD_SAFETY_ANALYSIS {
const_init_sanity_mutex.AssertHeld();
const_init_sanity_mutex.Unlock();
});
+#endif // defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900)
// Test shutdown usage. (Declarations come first; definitions must appear after
// the test runner.)
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc
index 07f220f5..e0879b05 100644
--- a/absl/synchronization/mutex.cc
+++ b/absl/synchronization/mutex.cc
@@ -71,7 +71,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalMutexYield() { std::this_thread::yield(); }
} // extern "C"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
@@ -107,13 +107,16 @@ static_assert(
sizeof(MutexGlobals) == ABSL_CACHELINE_SIZE,
"MutexGlobals must occupy an entire cacheline to prevent false sharing");
-ABSL_CONST_INIT absl::base_internal::AtomicHook<void (*)(int64_t wait_cycles)>
- submit_profile_data;
-ABSL_CONST_INIT absl::base_internal::AtomicHook<
- void (*)(const char *msg, const void *obj, int64_t wait_cycles)> mutex_tracer;
-ABSL_CONST_INIT absl::base_internal::AtomicHook<
- void (*)(const char *msg, const void *cv)> cond_var_tracer;
-ABSL_CONST_INIT absl::base_internal::AtomicHook<
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
+ absl::base_internal::AtomicHook<void (*)(int64_t wait_cycles)>
+ submit_profile_data;
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<void (*)(
+ const char *msg, const void *obj, int64_t wait_cycles)>
+ mutex_tracer;
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
+ absl::base_internal::AtomicHook<void (*)(const char *msg, const void *cv)>
+ cond_var_tracer;
+ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES absl::base_internal::AtomicHook<
bool (*)(const void *pc, char *out, int out_size)>
symbolizer(absl::Symbolize);
@@ -208,8 +211,8 @@ static absl::base_internal::SpinLock deadlock_graph_mu(
absl::base_internal::kLinkerInitialized);
// graph used to detect deadlocks.
-static GraphCycles *deadlock_graph GUARDED_BY(deadlock_graph_mu)
- PT_GUARDED_BY(deadlock_graph_mu);
+static GraphCycles *deadlock_graph ABSL_GUARDED_BY(deadlock_graph_mu)
+ ABSL_PT_GUARDED_BY(deadlock_graph_mu);
//------------------------------------------------------------------
// An event mechanism for debugging mutex use.
@@ -280,10 +283,10 @@ static const uint32_t kNSynchEvent = 1031;
static struct SynchEvent { // this is a trivial hash table for the events
// struct is freed when refcount reaches 0
- int refcount GUARDED_BY(synch_event_mu);
+ int refcount ABSL_GUARDED_BY(synch_event_mu);
// buckets have linear, 0-terminated chains
- SynchEvent *next GUARDED_BY(synch_event_mu);
+ SynchEvent *next ABSL_GUARDED_BY(synch_event_mu);
// Constant after initialization
uintptr_t masked_addr; // object at this address is called "name"
@@ -296,8 +299,8 @@ static struct SynchEvent { // this is a trivial hash table for the events
bool log; // logging turned on
// Constant after initialization
- char name[1]; // actually longer---null-terminated std::string
-} *synch_event[kNSynchEvent] GUARDED_BY(synch_event_mu);
+ char name[1]; // actually longer---NUL-terminated std::string
+} * synch_event[kNSynchEvent] ABSL_GUARDED_BY(synch_event_mu);
// Ensure that the object at "addr" has a SynchEvent struct associated with it,
// set "bits" in the word there (waiting until lockbit is clear before doing
@@ -1144,7 +1147,7 @@ PerThreadSynch *Mutex::Wakeup(PerThreadSynch *w) {
}
static GraphId GetGraphIdLocked(Mutex *mu)
- EXCLUSIVE_LOCKS_REQUIRED(deadlock_graph_mu) {
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(deadlock_graph_mu) {
if (!deadlock_graph) { // (re)create the deadlock graph.
deadlock_graph =
new (base_internal::LowLevelAlloc::Alloc(sizeof(*deadlock_graph)))
@@ -1153,7 +1156,7 @@ static GraphId GetGraphIdLocked(Mutex *mu)
return deadlock_graph->GetId(mu);
}
-static GraphId GetGraphId(Mutex *mu) LOCKS_EXCLUDED(deadlock_graph_mu) {
+static GraphId GetGraphId(Mutex *mu) ABSL_LOCKS_EXCLUDED(deadlock_graph_mu) {
deadlock_graph_mu.Lock();
GraphId id = GetGraphIdLocked(mu);
deadlock_graph_mu.Unlock();
@@ -2721,5 +2724,5 @@ bool Condition::GuaranteedEqual(const Condition *a, const Condition *b) {
a->arg_ == b->arg_ && a->method_ == b->method_;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h
index d6890099..8c70c4ce 100644
--- a/absl/synchronization/mutex.h
+++ b/absl/synchronization/mutex.h
@@ -82,7 +82,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
class Condition;
struct SynchWaitParams;
@@ -136,7 +136,7 @@ struct SynchWaitParams;
//
// See also `MutexLock`, below, for scoped `Mutex` acquisition.
-class LOCKABLE Mutex {
+class ABSL_LOCKABLE Mutex {
public:
// Creates a `Mutex` that is not held by anyone. This constructor is
// typically used for Mutexes allocated on the heap or the stack.
@@ -165,27 +165,27 @@ class LOCKABLE Mutex {
//
// Blocks the calling thread, if necessary, until this `Mutex` is free, and
// then acquires it exclusively. (This lock is also known as a "write lock.")
- void Lock() EXCLUSIVE_LOCK_FUNCTION();
+ void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION();
// Mutex::Unlock()
//
// Releases this `Mutex` and returns it from the exclusive/write state to the
// free state. Caller must hold the `Mutex` exclusively.
- void Unlock() UNLOCK_FUNCTION();
+ void Unlock() ABSL_UNLOCK_FUNCTION();
// Mutex::TryLock()
//
// If the mutex can be acquired without blocking, does so exclusively and
// returns `true`. Otherwise, returns `false`. Returns `true` with high
// probability if the `Mutex` was free.
- bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true);
+ bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true);
// Mutex::AssertHeld()
//
// Return immediately if this thread holds the `Mutex` exclusively (in write
// mode). Otherwise, may report an error (typically by crashing with a
// diagnostic), or may return immediately.
- void AssertHeld() const ASSERT_EXCLUSIVE_LOCK();
+ void AssertHeld() const ABSL_ASSERT_EXCLUSIVE_LOCK();
// ---------------------------------------------------------------------------
// Reader-Writer Locking
@@ -226,28 +226,28 @@ class LOCKABLE Mutex {
// `ReaderLock()` will block if some other thread has an exclusive/writer lock
// on the mutex.
- void ReaderLock() SHARED_LOCK_FUNCTION();
+ void ReaderLock() ABSL_SHARED_LOCK_FUNCTION();
// Mutex::ReaderUnlock()
//
// Releases a read share of this `Mutex`. `ReaderUnlock` may return a mutex to
// the free state if this thread holds the last reader lock on the mutex. Note
// that you cannot call `ReaderUnlock()` on a mutex held in write mode.
- void ReaderUnlock() UNLOCK_FUNCTION();
+ void ReaderUnlock() ABSL_UNLOCK_FUNCTION();
// Mutex::ReaderTryLock()
//
// If the mutex can be acquired without blocking, acquires this mutex for
// shared access and returns `true`. Otherwise, returns `false`. Returns
// `true` with high probability if the `Mutex` was free or shared.
- bool ReaderTryLock() SHARED_TRYLOCK_FUNCTION(true);
+ bool ReaderTryLock() ABSL_SHARED_TRYLOCK_FUNCTION(true);
// Mutex::AssertReaderHeld()
//
// Returns immediately if this thread holds the `Mutex` in at least shared
// mode (read mode). Otherwise, may report an error (typically by
// crashing with a diagnostic), or may return immediately.
- void AssertReaderHeld() const ASSERT_SHARED_LOCK();
+ void AssertReaderHeld() const ABSL_ASSERT_SHARED_LOCK();
// Mutex::WriterLock()
// Mutex::WriterUnlock()
@@ -258,11 +258,11 @@ class LOCKABLE Mutex {
// These methods may be used (along with the complementary `Reader*()`
// methods) to distingish simple exclusive `Mutex` usage (`Lock()`,
// etc.) from reader/writer lock usage.
- void WriterLock() EXCLUSIVE_LOCK_FUNCTION() { this->Lock(); }
+ void WriterLock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { this->Lock(); }
- void WriterUnlock() UNLOCK_FUNCTION() { this->Unlock(); }
+ void WriterUnlock() ABSL_UNLOCK_FUNCTION() { this->Unlock(); }
- bool WriterTryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ bool WriterTryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
return this->TryLock();
}
@@ -316,11 +316,11 @@ class 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) EXCLUSIVE_LOCK_FUNCTION();
+ void LockWhen(const Condition &cond) ABSL_EXCLUSIVE_LOCK_FUNCTION();
- void ReaderLockWhen(const Condition &cond) SHARED_LOCK_FUNCTION();
+ void ReaderLockWhen(const Condition &cond) ABSL_SHARED_LOCK_FUNCTION();
- void WriterLockWhen(const Condition &cond) EXCLUSIVE_LOCK_FUNCTION() {
+ void WriterLockWhen(const Condition &cond) ABSL_EXCLUSIVE_LOCK_FUNCTION() {
this->LockWhen(cond);
}
@@ -362,11 +362,11 @@ class LOCKABLE Mutex {
//
// Negative timeouts are equivalent to a zero timeout.
bool LockWhenWithTimeout(const Condition &cond, absl::Duration timeout)
- EXCLUSIVE_LOCK_FUNCTION();
+ ABSL_EXCLUSIVE_LOCK_FUNCTION();
bool ReaderLockWhenWithTimeout(const Condition &cond, absl::Duration timeout)
- SHARED_LOCK_FUNCTION();
+ ABSL_SHARED_LOCK_FUNCTION();
bool WriterLockWhenWithTimeout(const Condition &cond, absl::Duration timeout)
- EXCLUSIVE_LOCK_FUNCTION() {
+ ABSL_EXCLUSIVE_LOCK_FUNCTION() {
return this->LockWhenWithTimeout(cond, timeout);
}
@@ -382,11 +382,11 @@ class LOCKABLE Mutex {
//
// Deadlines in the past are equivalent to an immediate deadline.
bool LockWhenWithDeadline(const Condition &cond, absl::Time deadline)
- EXCLUSIVE_LOCK_FUNCTION();
+ ABSL_EXCLUSIVE_LOCK_FUNCTION();
bool ReaderLockWhenWithDeadline(const Condition &cond, absl::Time deadline)
- SHARED_LOCK_FUNCTION();
+ ABSL_SHARED_LOCK_FUNCTION();
bool WriterLockWhenWithDeadline(const Condition &cond, absl::Time deadline)
- EXCLUSIVE_LOCK_FUNCTION() {
+ ABSL_EXCLUSIVE_LOCK_FUNCTION() {
return this->LockWhenWithDeadline(cond, deadline);
}
@@ -536,9 +536,9 @@ class LOCKABLE Mutex {
// private:
// Mutex lock_;
// };
-class SCOPED_LOCKABLE MutexLock {
+class ABSL_SCOPED_LOCKABLE MutexLock {
public:
- explicit MutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) {
+ explicit MutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) {
this->mu_->Lock();
}
@@ -547,7 +547,7 @@ class SCOPED_LOCKABLE MutexLock {
MutexLock& operator=(const MutexLock&) = delete;
MutexLock& operator=(MutexLock&&) = delete;
- ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); }
+ ~MutexLock() ABSL_UNLOCK_FUNCTION() { this->mu_->Unlock(); }
private:
Mutex *const mu_;
@@ -557,10 +557,9 @@ class SCOPED_LOCKABLE MutexLock {
//
// The `ReaderMutexLock` is a helper class, like `MutexLock`, which acquires and
// releases a shared lock on a `Mutex` via RAII.
-class SCOPED_LOCKABLE ReaderMutexLock {
+class ABSL_SCOPED_LOCKABLE ReaderMutexLock {
public:
- explicit ReaderMutexLock(Mutex *mu) SHARED_LOCK_FUNCTION(mu)
- : mu_(mu) {
+ explicit ReaderMutexLock(Mutex *mu) ABSL_SHARED_LOCK_FUNCTION(mu) : mu_(mu) {
mu->ReaderLock();
}
@@ -569,9 +568,7 @@ class SCOPED_LOCKABLE ReaderMutexLock {
ReaderMutexLock& operator=(const ReaderMutexLock&) = delete;
ReaderMutexLock& operator=(ReaderMutexLock&&) = delete;
- ~ReaderMutexLock() UNLOCK_FUNCTION() {
- this->mu_->ReaderUnlock();
- }
+ ~ReaderMutexLock() ABSL_UNLOCK_FUNCTION() { this->mu_->ReaderUnlock(); }
private:
Mutex *const mu_;
@@ -581,9 +578,9 @@ class SCOPED_LOCKABLE ReaderMutexLock {
//
// The `WriterMutexLock` is a helper class, like `MutexLock`, which acquires and
// releases a write (exclusive) lock on a `Mutex` via RAII.
-class SCOPED_LOCKABLE WriterMutexLock {
+class ABSL_SCOPED_LOCKABLE WriterMutexLock {
public:
- explicit WriterMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu)
+ explicit WriterMutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
: mu_(mu) {
mu->WriterLock();
}
@@ -593,9 +590,7 @@ class SCOPED_LOCKABLE WriterMutexLock {
WriterMutexLock& operator=(const WriterMutexLock&) = delete;
WriterMutexLock& operator=(WriterMutexLock&&) = delete;
- ~WriterMutexLock() UNLOCK_FUNCTION() {
- this->mu_->WriterUnlock();
- }
+ ~WriterMutexLock() ABSL_UNLOCK_FUNCTION() { this->mu_->WriterUnlock(); }
private:
Mutex *const mu_;
@@ -634,7 +629,7 @@ class SCOPED_LOCKABLE WriterMutexLock {
// Example:
//
// // assume count_ is not internal reference count
-// int count_ GUARDED_BY(mu_);
+// int count_ ABSL_GUARDED_BY(mu_);
//
// mu_.LockWhen(Condition(+[](int* count) { return *count == 0; },
// &count_));
@@ -861,13 +856,18 @@ class CondVar {
// MutexLockMaybe
//
// MutexLockMaybe is like MutexLock, but is a no-op when mu is null.
-class SCOPED_LOCKABLE MutexLockMaybe {
+class ABSL_SCOPED_LOCKABLE MutexLockMaybe {
public:
- explicit MutexLockMaybe(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu)
- : mu_(mu) { if (this->mu_ != nullptr) { this->mu_->Lock(); } }
- ~MutexLockMaybe() UNLOCK_FUNCTION() {
+ explicit MutexLockMaybe(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
+ : mu_(mu) {
+ if (this->mu_ != nullptr) {
+ this->mu_->Lock();
+ }
+ }
+ ~MutexLockMaybe() ABSL_UNLOCK_FUNCTION() {
if (this->mu_ != nullptr) { this->mu_->Unlock(); }
}
+
private:
Mutex *const mu_;
MutexLockMaybe(const MutexLockMaybe&) = delete;
@@ -880,17 +880,17 @@ class SCOPED_LOCKABLE MutexLockMaybe {
//
// ReleasableMutexLock is like MutexLock, but permits `Release()` of its
// mutex before destruction. `Release()` may be called at most once.
-class SCOPED_LOCKABLE ReleasableMutexLock {
+class ABSL_SCOPED_LOCKABLE ReleasableMutexLock {
public:
- explicit ReleasableMutexLock(Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu)
+ explicit ReleasableMutexLock(Mutex *mu) ABSL_EXCLUSIVE_LOCK_FUNCTION(mu)
: mu_(mu) {
this->mu_->Lock();
}
- ~ReleasableMutexLock() UNLOCK_FUNCTION() {
+ ~ReleasableMutexLock() ABSL_UNLOCK_FUNCTION() {
if (this->mu_ != nullptr) { this->mu_->Unlock(); }
}
- void Release() UNLOCK_FUNCTION();
+ void Release() ABSL_UNLOCK_FUNCTION();
private:
Mutex *mu_;
@@ -1001,7 +1001,7 @@ void RegisterCondVarTracer(void (*fn)(const char *msg, const void *cv));
//
// 'pc' is the program counter being symbolized, 'out' is the buffer to write
// into, and 'out_size' is the size of the buffer. This function can return
-// false if symbolizing failed, or true if a null-terminated symbol was written
+// false if symbolizing failed, or true if a NUL-terminated symbol was written
// to 'out.'
//
// This has the same memory ordering concerns as RegisterMutexProfiler() above.
@@ -1040,7 +1040,7 @@ enum class OnDeadlockCycle {
// the manner chosen here.
void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// In some build configurations we pass --detect-odr-violations to the
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index 9851ac19..afb363af 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -425,10 +425,10 @@ TEST(Mutex, CondVarWaitSignalsAwait) {
// Use a struct so the lock annotations apply.
struct {
absl::Mutex barrier_mu;
- bool barrier GUARDED_BY(barrier_mu) = false;
+ bool barrier ABSL_GUARDED_BY(barrier_mu) = false;
absl::Mutex release_mu;
- bool release GUARDED_BY(release_mu) = false;
+ bool release ABSL_GUARDED_BY(release_mu) = false;
absl::CondVar released_cv;
} state;
@@ -466,10 +466,10 @@ TEST(Mutex, CondVarWaitWithTimeoutSignalsAwait) {
// Use a struct so the lock annotations apply.
struct {
absl::Mutex barrier_mu;
- bool barrier GUARDED_BY(barrier_mu) = false;
+ bool barrier ABSL_GUARDED_BY(barrier_mu) = false;
absl::Mutex release_mu;
- bool release GUARDED_BY(release_mu) = false;
+ bool release ABSL_GUARDED_BY(release_mu) = false;
absl::CondVar released_cv;
} state;
@@ -770,7 +770,7 @@ static void GetReadLock(ReaderDecrementBugStruct *x) {
// Test for reader counter being decremented incorrectly by waiter
// with false condition.
-TEST(Mutex, MutexReaderDecrementBug) NO_THREAD_SAFETY_ANALYSIS {
+TEST(Mutex, MutexReaderDecrementBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
ReaderDecrementBugStruct x;
x.cond = false;
x.waiting_on_cond = false;
@@ -819,7 +819,7 @@ TEST(Mutex, MutexReaderDecrementBug) NO_THREAD_SAFETY_ANALYSIS {
// TSAN reports errors when locked Mutexes are destroyed.
TEST(Mutex, DISABLED_LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS {
#else
-TEST(Mutex, LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS {
+TEST(Mutex, LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
#endif
for (int i = 0; i != 10; i++) {
// Create, lock and destroy 10 locks.
@@ -1101,7 +1101,7 @@ TEST(Mutex, DeadlockDetectorBazelWarning) {
// annotation-based static thread-safety analysis is not currently
// predicate-aware and cannot tell if the two for-loops that acquire and
// release the locks have the same predicates.
-TEST(Mutex, DeadlockDetectorStessTest) NO_THREAD_SAFETY_ANALYSIS {
+TEST(Mutex, DeadlockDetectorStessTest) ABSL_NO_THREAD_SAFETY_ANALYSIS {
// Stress test: Here we create a large number of locks and use all of them.
// If a deadlock detector keeps a full graph of lock acquisition order,
// it will likely be too slow for this test to pass.
@@ -1123,7 +1123,7 @@ TEST(Mutex, DeadlockDetectorStessTest) NO_THREAD_SAFETY_ANALYSIS {
// TSAN reports errors when locked Mutexes are destroyed.
TEST(Mutex, DISABLED_DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS {
#else
-TEST(Mutex, DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS {
+TEST(Mutex, DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
#endif
// Test a scenario where a cached deadlock graph node id in the
// list of held locks is not invalidated when the corresponding
diff --git a/absl/synchronization/notification.cc b/absl/synchronization/notification.cc
index d691cfca..e91b9038 100644
--- a/absl/synchronization/notification.cc
+++ b/absl/synchronization/notification.cc
@@ -22,7 +22,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
void Notification::Notify() {
MutexLock l(&this->mutex_);
@@ -45,15 +45,6 @@ Notification::~Notification() {
MutexLock l(&this->mutex_);
}
-static inline bool HasBeenNotifiedInternal(
- const std::atomic<bool> *notified_yet) {
- return notified_yet->load(std::memory_order_acquire);
-}
-
-bool Notification::HasBeenNotified() const {
- return HasBeenNotifiedInternal(&this->notified_yet_);
-}
-
void Notification::WaitForNotification() const {
if (!HasBeenNotifiedInternal(&this->notified_yet_)) {
this->mutex_.LockWhen(Condition(&HasBeenNotifiedInternal,
@@ -83,5 +74,5 @@ bool Notification::WaitForNotificationWithDeadline(absl::Time deadline) const {
return notified;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/synchronization/notification.h b/absl/synchronization/notification.h
index 8ed7f12a..9a354ca2 100644
--- a/absl/synchronization/notification.h
+++ b/absl/synchronization/notification.h
@@ -57,7 +57,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// Notification
@@ -74,7 +74,9 @@ class Notification {
// Notification::HasBeenNotified()
//
// Returns the value of the notification's internal "notified" state.
- bool HasBeenNotified() const;
+ bool HasBeenNotified() const {
+ return HasBeenNotifiedInternal(&this->notified_yet_);
+ }
// Notification::WaitForNotification()
//
@@ -106,11 +108,16 @@ class Notification {
void Notify();
private:
+ static inline bool HasBeenNotifiedInternal(
+ const std::atomic<bool>* notified_yet) {
+ return notified_yet->load(std::memory_order_acquire);
+ }
+
mutable Mutex mutex_;
std::atomic<bool> notified_yet_; // written under mutex_
};
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_SYNCHRONIZATION_NOTIFICATION_H_
diff --git a/absl/synchronization/notification_test.cc b/absl/synchronization/notification_test.cc
index a64674ca..100ea76f 100644
--- a/absl/synchronization/notification_test.cc
+++ b/absl/synchronization/notification_test.cc
@@ -21,7 +21,7 @@
#include "absl/synchronization/mutex.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// A thread-safe class that holds a counter.
class ThreadSafeCounter {
@@ -129,5 +129,5 @@ TEST(NotificationTest, SanityTest) {
BasicTests(true, &local_notification2);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel
index 55e83a8c..9ab2adb8 100644
--- a/absl/time/BUILD.bazel
+++ b/absl/time/BUILD.bazel
@@ -14,6 +14,7 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@@ -46,6 +47,7 @@ cc_library(
deps = [
"//absl/base",
"//absl/base:core_headers",
+ "//absl/base:raw_logging_internal",
"//absl/numeric:int128",
"//absl/strings",
"//absl/time/internal/cctz:civil_time",
@@ -68,7 +70,7 @@ cc_library(
],
deps = [
":time",
- "//absl/base",
+ "//absl/base:raw_logging_internal",
"//absl/time/internal/cctz:time_zone",
"@com_google_googletest//:gtest",
],
@@ -89,9 +91,9 @@ cc_test(
deps = [
":test_util",
":time",
- "//absl/base",
"//absl/base:config",
"//absl/base:core_headers",
+ "//absl/numeric:int128",
"//absl/time/internal/cctz:time_zone",
"@com_google_googletest//:gtest_main",
],
diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt
index 59098321..853563e8 100644
--- a/absl/time/CMakeLists.txt
+++ b/absl/time/CMakeLists.txt
@@ -22,21 +22,22 @@ absl_cc_library(
"clock.h"
"time.h"
SRCS
- "civil_time.cc"
- "clock.cc"
- "duration.cc"
- "format.cc"
- "internal/get_current_time_chrono.inc"
- "internal/get_current_time_posix.inc"
- "time.cc"
+ "civil_time.cc"
+ "clock.cc"
+ "duration.cc"
+ "format.cc"
+ "internal/get_current_time_chrono.inc"
+ "internal/get_current_time_posix.inc"
+ "time.cc"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::base
+ absl::civil_time
absl::core_headers
absl::int128
+ absl::raw_logging_internal
absl::strings
- absl::civil_time
absl::time_zone
PUBLIC
)
@@ -88,7 +89,7 @@ absl_cc_library(
absl_cc_library(
NAME
- test_util
+ time_internal_test_util
HDRS
"internal/test_util.h"
SRCS
@@ -98,7 +99,7 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS}
DEPS
absl::time
- absl::base
+ absl::raw_logging_internal
absl::time_zone
gmock
TESTONLY
@@ -117,9 +118,8 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
- absl::test_util
+ absl::time_internal_test_util
absl::time
- absl::base
absl::config
absl::core_headers
absl::time_zone
diff --git a/absl/time/civil_time.cc b/absl/time/civil_time.cc
index bf7ba5ab..ada82cbc 100644
--- a/absl/time/civil_time.cc
+++ b/absl/time/civil_time.cc
@@ -21,7 +21,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
@@ -43,6 +43,58 @@ std::string FormatYearAnd(string_view fmt, CivilSecond cs) {
FormatTime(std::string(fmt), FromCivil(ncs, utc), utc));
}
+template <typename CivilT>
+bool ParseYearAnd(string_view fmt, string_view s, CivilT* c) {
+ // Civil times support a larger year range than absl::Time, so we need to
+ // parse the year separately, normalize it, then use absl::ParseTime on the
+ // normalized std::string.
+ const std::string ss = std::string(s); // TODO(absl-team): Avoid conversion.
+ const char* const np = ss.c_str();
+ char* endp;
+ errno = 0;
+ const civil_year_t y =
+ std::strtoll(np, &endp, 10); // NOLINT(runtime/deprecated_fn)
+ if (endp == np || errno == ERANGE) return false;
+ const std::string norm = StrCat(NormalizeYear(y), endp);
+
+ const TimeZone utc = UTCTimeZone();
+ Time t;
+ if (ParseTime(StrCat("%Y", fmt), norm, utc, &t, nullptr)) {
+ const auto cs = ToCivilSecond(t, utc);
+ *c = CivilT(y, cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second());
+ return true;
+ }
+
+ return false;
+}
+
+// Tries to parse the type as a CivilT1, but then assigns the result to the
+// argument of type CivilT2.
+template <typename CivilT1, typename CivilT2>
+bool ParseAs(string_view s, CivilT2* c) {
+ CivilT1 t1;
+ if (ParseCivilTime(s, &t1)) {
+ *c = CivilT2(t1);
+ return true;
+ }
+ return false;
+}
+
+template <typename CivilT>
+bool ParseLenient(string_view s, CivilT* c) {
+ // A fastpath for when the given std::string data parses exactly into the given
+ // type T (e.g., s="YYYY-MM-DD" and CivilT=CivilDay).
+ if (ParseCivilTime(s, c)) return true;
+ // Try parsing as each of the 6 types, trying the most common types first
+ // (based on csearch results).
+ if (ParseAs<CivilDay>(s, c)) return true;
+ if (ParseAs<CivilSecond>(s, c)) return true;
+ if (ParseAs<CivilHour>(s, c)) return true;
+ if (ParseAs<CivilMonth>(s, c)) return true;
+ if (ParseAs<CivilMinute>(s, c)) return true;
+ if (ParseAs<CivilYear>(s, c)) return true;
+ return false;
+}
} // namespace
std::string FormatCivilTime(CivilSecond c) {
@@ -58,6 +110,44 @@ std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); }
std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); }
std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); }
+bool ParseCivilTime(string_view s, CivilSecond* c) {
+ return ParseYearAnd("-%m-%dT%H:%M:%S", s, c);
+}
+bool ParseCivilTime(string_view s, CivilMinute* c) {
+ return ParseYearAnd("-%m-%dT%H:%M", s, c);
+}
+bool ParseCivilTime(string_view s, CivilHour* c) {
+ return ParseYearAnd("-%m-%dT%H", s, c);
+}
+bool ParseCivilTime(string_view s, CivilDay* c) {
+ return ParseYearAnd("-%m-%d", s, c);
+}
+bool ParseCivilTime(string_view s, CivilMonth* c) {
+ return ParseYearAnd("-%m", s, c);
+}
+bool ParseCivilTime(string_view s, CivilYear* c) {
+ return ParseYearAnd("", s, c);
+}
+
+bool ParseLenientCivilTime(string_view s, CivilSecond* c) {
+ return ParseLenient(s, c);
+}
+bool ParseLenientCivilTime(string_view s, CivilMinute* c) {
+ return ParseLenient(s, c);
+}
+bool ParseLenientCivilTime(string_view s, CivilHour* c) {
+ return ParseLenient(s, c);
+}
+bool ParseLenientCivilTime(string_view s, CivilDay* c) {
+ return ParseLenient(s, c);
+}
+bool ParseLenientCivilTime(string_view s, CivilMonth* c) {
+ return ParseLenient(s, c);
+}
+bool ParseLenientCivilTime(string_view s, CivilYear* c) {
+ return ParseLenient(s, c);
+}
+
namespace time_internal {
std::ostream& operator<<(std::ostream& os, CivilYear y) {
@@ -81,5 +171,5 @@ std::ostream& operator<<(std::ostream& os, CivilSecond s) {
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/civil_time.h b/absl/time/civil_time.h
index f0be303c..bb460044 100644
--- a/absl/time/civil_time.h
+++ b/absl/time/civil_time.h
@@ -76,7 +76,7 @@
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
struct second_tag : cctz::detail::second_tag {};
@@ -248,7 +248,7 @@ struct year_tag : month_tag, cctz::detail::year_tag {};
// int minute()
// int second()
//
-// Recall that fields inferior to the type's aligment will be set to their
+// Recall that fields inferior to the type's alignment will be set to their
// minimum valid value.
//
// Example:
@@ -460,6 +460,57 @@ std::string FormatCivilTime(CivilDay c);
std::string FormatCivilTime(CivilMonth c);
std::string FormatCivilTime(CivilYear c);
+// absl::ParseCivilTime()
+//
+// Parses a civil-time value from the specified `absl::string_view` into the
+// passed output parameter. Returns `true` upon successful parsing.
+//
+// The expected form of the input string is as follows:
+//
+// Type | Format
+// ---------------------------------
+// CivilSecond | YYYY-MM-DDTHH:MM:SS
+// CivilMinute | YYYY-MM-DDTHH:MM
+// CivilHour | YYYY-MM-DDTHH
+// CivilDay | YYYY-MM-DD
+// CivilMonth | YYYY-MM
+// CivilYear | YYYY
+//
+// Example:
+//
+// absl::CivilDay d;
+// bool ok = absl::ParseCivilTime("2018-01-02", &d); // OK
+//
+// Note that parsing will fail if the string's format does not match the
+// expected type exactly. `ParseLenientCivilTime()` below is more lenient.
+//
+bool ParseCivilTime(absl::string_view s, CivilSecond* c);
+bool ParseCivilTime(absl::string_view s, CivilMinute* c);
+bool ParseCivilTime(absl::string_view s, CivilHour* c);
+bool ParseCivilTime(absl::string_view s, CivilDay* c);
+bool ParseCivilTime(absl::string_view s, CivilMonth* c);
+bool ParseCivilTime(absl::string_view s, CivilYear* c);
+
+// ParseLenientCivilTime()
+//
+// Parses any of the formats accepted by `absl::ParseCivilTime()`, but is more
+// lenient if the format of the string does not exactly match the associated
+// type.
+//
+// Example:
+//
+// absl::CivilDay d;
+// bool ok = absl::ParseLenientCivilTime("1969-07-20", &d); // OK
+// ok = absl::ParseLenientCivilTime("1969-07-20T10", &d); // OK: T10 floored
+// ok = absl::ParseLenientCivilTime("1969-07", &d); // OK: day defaults to 1
+//
+bool ParseLenientCivilTime(absl::string_view s, CivilSecond* c);
+bool ParseLenientCivilTime(absl::string_view s, CivilMinute* c);
+bool ParseLenientCivilTime(absl::string_view s, CivilHour* c);
+bool ParseLenientCivilTime(absl::string_view s, CivilDay* c);
+bool ParseLenientCivilTime(absl::string_view s, CivilMonth* c);
+bool ParseLenientCivilTime(absl::string_view s, CivilYear* c);
+
namespace time_internal { // For functions found via ADL on civil-time tags.
// Streaming Operators
@@ -469,7 +520,7 @@ namespace time_internal { // For functions found via ADL on civil-time tags.
//
// Example:
//
-// absl::CivilDay d = absl::CivilDay("1969-07-20");
+// absl::CivilDay d = absl::CivilDay(1969, 7, 20);
// std::cout << "Date is: " << d << "\n";
//
std::ostream& operator<<(std::ostream& os, CivilYear y);
@@ -481,7 +532,7 @@ std::ostream& operator<<(std::ostream& os, CivilSecond s);
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_CIVIL_TIME_H_
diff --git a/absl/time/civil_time_benchmark.cc b/absl/time/civil_time_benchmark.cc
index 40869835..f04dbe20 100644
--- a/absl/time/civil_time_benchmark.cc
+++ b/absl/time/civil_time_benchmark.cc
@@ -66,6 +66,26 @@ void BM_Format(benchmark::State& state) {
}
BENCHMARK(BM_Format);
+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);
+ benchmark::DoNotOptimize(b);
+ }
+}
+BENCHMARK(BM_Parse);
+
+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);
+ benchmark::DoNotOptimize(b);
+ }
+}
+BENCHMARK(BM_RoundTripFormatParse);
+
template <typename T>
void BM_CivilTimeAbslHash(benchmark::State& state) {
const int kSize = 100000;
diff --git a/absl/time/civil_time_test.cc b/absl/time/civil_time_test.cc
index 03cd1f12..0ebd97ad 100644
--- a/absl/time/civil_time_test.cc
+++ b/absl/time/civil_time_test.cc
@@ -690,6 +690,69 @@ TEST(CivilTime, Format) {
EXPECT_EQ("1970", absl::FormatCivilTime(y));
}
+TEST(CivilTime, Parse) {
+ absl::CivilSecond ss;
+ absl::CivilMinute mm;
+ absl::CivilHour hh;
+ absl::CivilDay d;
+ absl::CivilMonth m;
+ absl::CivilYear y;
+
+ // CivilSecond OK; others fail
+ EXPECT_TRUE(absl::ParseCivilTime("2015-01-02T03:04:05", &ss));
+ EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(ss));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &mm));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &hh));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &d));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &m));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04:05", &y));
+
+ // CivilMinute OK; others fail
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &ss));
+ EXPECT_TRUE(absl::ParseCivilTime("2015-01-02T03:04", &mm));
+ EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(mm));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &hh));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &d));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &m));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03:04", &y));
+
+ // CivilHour OK; others fail
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &ss));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &mm));
+ EXPECT_TRUE(absl::ParseCivilTime("2015-01-02T03", &hh));
+ EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hh));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &d));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &m));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02T03", &y));
+
+ // CivilDay OK; others fail
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &ss));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &mm));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &hh));
+ EXPECT_TRUE(absl::ParseCivilTime("2015-01-02", &d));
+ EXPECT_EQ("2015-01-02", absl::FormatCivilTime(d));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &m));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01-02", &y));
+
+ // CivilMonth OK; others fail
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01", &ss));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01", &mm));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01", &hh));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01", &d));
+ EXPECT_TRUE(absl::ParseCivilTime("2015-01", &m));
+ EXPECT_EQ("2015-01", absl::FormatCivilTime(m));
+ EXPECT_FALSE(absl::ParseCivilTime("2015-01", &y));
+
+ // CivilYear OK; others fail
+ EXPECT_FALSE(absl::ParseCivilTime("2015", &ss));
+ EXPECT_FALSE(absl::ParseCivilTime("2015", &mm));
+ EXPECT_FALSE(absl::ParseCivilTime("2015", &hh));
+ EXPECT_FALSE(absl::ParseCivilTime("2015", &d));
+ EXPECT_FALSE(absl::ParseCivilTime("2015", &m));
+ EXPECT_TRUE(absl::ParseCivilTime("2015", &y));
+ EXPECT_EQ("2015", absl::FormatCivilTime(y));
+}
+
TEST(CivilTime, FormatAndParseLenient) {
absl::CivilSecond ss;
EXPECT_EQ("1970-01-01T00:00:00", absl::FormatCivilTime(ss));
@@ -708,6 +771,101 @@ TEST(CivilTime, FormatAndParseLenient) {
absl::CivilYear y;
EXPECT_EQ("1970", absl::FormatCivilTime(y));
+
+ EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &ss));
+ EXPECT_EQ("2015-01-02T03:04:05", absl::FormatCivilTime(ss));
+
+ EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &mm));
+ EXPECT_EQ("2015-01-02T03:04", absl::FormatCivilTime(mm));
+
+ EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &hh));
+ EXPECT_EQ("2015-01-02T03", absl::FormatCivilTime(hh));
+
+ EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &d));
+ EXPECT_EQ("2015-01-02", absl::FormatCivilTime(d));
+
+ EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &m));
+ EXPECT_EQ("2015-01", absl::FormatCivilTime(m));
+
+ EXPECT_TRUE(absl::ParseLenientCivilTime("2015-01-02T03:04:05", &y));
+ EXPECT_EQ("2015", absl::FormatCivilTime(y));
+}
+
+TEST(CivilTime, ParseEdgeCases) {
+ absl::CivilSecond ss;
+ EXPECT_TRUE(
+ absl::ParseLenientCivilTime("9223372036854775807-12-31T23:59:59", &ss));
+ EXPECT_EQ("9223372036854775807-12-31T23:59:59", absl::FormatCivilTime(ss));
+ EXPECT_TRUE(
+ absl::ParseLenientCivilTime("-9223372036854775808-01-01T00:00:00", &ss));
+ EXPECT_EQ("-9223372036854775808-01-01T00:00:00", absl::FormatCivilTime(ss));
+
+ absl::CivilMinute mm;
+ EXPECT_TRUE(
+ absl::ParseLenientCivilTime("9223372036854775807-12-31T23:59", &mm));
+ EXPECT_EQ("9223372036854775807-12-31T23:59", absl::FormatCivilTime(mm));
+ EXPECT_TRUE(
+ absl::ParseLenientCivilTime("-9223372036854775808-01-01T00:00", &mm));
+ EXPECT_EQ("-9223372036854775808-01-01T00:00", absl::FormatCivilTime(mm));
+
+ absl::CivilHour hh;
+ EXPECT_TRUE(
+ absl::ParseLenientCivilTime("9223372036854775807-12-31T23", &hh));
+ EXPECT_EQ("9223372036854775807-12-31T23", absl::FormatCivilTime(hh));
+ EXPECT_TRUE(
+ absl::ParseLenientCivilTime("-9223372036854775808-01-01T00", &hh));
+ EXPECT_EQ("-9223372036854775808-01-01T00", absl::FormatCivilTime(hh));
+
+ absl::CivilDay d;
+ EXPECT_TRUE(absl::ParseLenientCivilTime("9223372036854775807-12-31", &d));
+ EXPECT_EQ("9223372036854775807-12-31", absl::FormatCivilTime(d));
+ EXPECT_TRUE(absl::ParseLenientCivilTime("-9223372036854775808-01-01", &d));
+ EXPECT_EQ("-9223372036854775808-01-01", absl::FormatCivilTime(d));
+
+ absl::CivilMonth m;
+ EXPECT_TRUE(absl::ParseLenientCivilTime("9223372036854775807-12", &m));
+ EXPECT_EQ("9223372036854775807-12", absl::FormatCivilTime(m));
+ EXPECT_TRUE(absl::ParseLenientCivilTime("-9223372036854775808-01", &m));
+ EXPECT_EQ("-9223372036854775808-01", absl::FormatCivilTime(m));
+
+ absl::CivilYear y;
+ EXPECT_TRUE(absl::ParseLenientCivilTime("9223372036854775807", &y));
+ EXPECT_EQ("9223372036854775807", absl::FormatCivilTime(y));
+ EXPECT_TRUE(absl::ParseLenientCivilTime("-9223372036854775808", &y));
+ EXPECT_EQ("-9223372036854775808", absl::FormatCivilTime(y));
+
+ // Tests some valid, but interesting, cases
+ EXPECT_TRUE(absl::ParseLenientCivilTime("0", &ss)) << ss;
+ EXPECT_EQ(absl::CivilYear(0), ss);
+ EXPECT_TRUE(absl::ParseLenientCivilTime("0-1", &ss)) << ss;
+ EXPECT_EQ(absl::CivilMonth(0, 1), ss);
+ EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015 ", &ss)) << ss;
+ EXPECT_EQ(absl::CivilYear(2015), ss);
+ EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015-6 ", &ss)) << ss;
+ EXPECT_EQ(absl::CivilMonth(2015, 6), ss);
+ EXPECT_TRUE(absl::ParseLenientCivilTime("2015-6-7", &ss)) << ss;
+ EXPECT_EQ(absl::CivilDay(2015, 6, 7), ss);
+ EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015-6-7 ", &ss)) << ss;
+ EXPECT_EQ(absl::CivilDay(2015, 6, 7), ss);
+ EXPECT_TRUE(absl::ParseLenientCivilTime("2015-06-07T10:11:12 ", &ss)) << ss;
+ EXPECT_EQ(absl::CivilSecond(2015, 6, 7, 10, 11, 12), ss);
+ EXPECT_TRUE(absl::ParseLenientCivilTime(" 2015-06-07T10:11:12 ", &ss)) << ss;
+ EXPECT_EQ(absl::CivilSecond(2015, 6, 7, 10, 11, 12), ss);
+ EXPECT_TRUE(absl::ParseLenientCivilTime("-01-01", &ss)) << ss;
+ EXPECT_EQ(absl::CivilMonth(-1, 1), ss);
+
+ // Tests some invalid cases
+ EXPECT_FALSE(absl::ParseLenientCivilTime("01-01-2015", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("2015-", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("0xff-01", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-30T04:05:06", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-03T04:05:96", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("X2015-02-03T04:05:06", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-03T04:05:003", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("2015 -02-03T04:05:06", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("2015-02-03-04:05:06", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("2015:02:03T04-05-06", &ss)) << ss;
+ EXPECT_FALSE(absl::ParseLenientCivilTime("9223372036854775808", &y)) << y;
}
TEST(CivilTime, OutputStream) {
diff --git a/absl/time/clock.cc b/absl/time/clock.cc
index 48dc4450..3b895c38 100644
--- a/absl/time/clock.cc
+++ b/absl/time/clock.cc
@@ -34,7 +34,7 @@
#include "absl/base/thread_annotations.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
Time Now() {
// TODO(bww): Get a timespec instead so we don't have to divide.
int64_t n = absl::GetCurrentTimeNanos();
@@ -44,7 +44,7 @@ Time Now() {
}
return time_internal::FromUnixDuration(absl::Nanoseconds(n));
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// Decide if we should use the fast GetCurrentTimeNanos() algorithm
@@ -73,11 +73,11 @@ Time Now() {
#if !ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
int64_t GetCurrentTimeNanos() {
return GET_CURRENT_TIME_NANOS_FROM_SYSTEM();
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#else // Use the cyclecounter-based implementation below.
@@ -95,7 +95,7 @@ static int64_t stats_slow_paths;
static int64_t stats_fast_slow_paths;
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
// This is a friend wrapper around UnscaledCycleClock::Now()
// (needed to access UnscaledCycleClock).
@@ -390,7 +390,7 @@ static uint64_t UpdateLastSample(
// TODO(absl-team): Remove this attribute when our compiler is smart enough
// to do the right thing.
ABSL_ATTRIBUTE_NOINLINE
-static int64_t GetCurrentTimeNanosSlowPath() LOCKS_EXCLUDED(lock) {
+static int64_t GetCurrentTimeNanosSlowPath() ABSL_LOCKS_EXCLUDED(lock) {
// Serialize access to slow-path. Fast-path readers are not blocked yet, and
// code below must not modify last_sample until the seqlock is acquired.
lock.Lock();
@@ -435,7 +435,7 @@ static int64_t GetCurrentTimeNanosSlowPath() LOCKS_EXCLUDED(lock) {
static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns,
uint64_t delta_cycles,
const struct TimeSample *sample)
- EXCLUSIVE_LOCKS_REQUIRED(lock) {
+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock) {
uint64_t estimated_base_ns = now_ns;
uint64_t lock_value = SeqAcquire(&seq); // acquire seqlock to block readers
@@ -454,7 +454,7 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns,
last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed);
stats_initializations++;
} else if (sample->raw_ns + 500 * 1000 * 1000 < now_ns &&
- sample->base_cycles + 100 < now_cycles) {
+ sample->base_cycles + 50 < now_cycles) {
// Enough time has passed to compute the cycle time.
if (sample->nsscaled_per_cycle != 0) { // Have a cycle time estimate.
// Compute time from counter reading, but avoiding overflow
@@ -520,12 +520,12 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns,
return estimated_base_ns;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
// Returns the maximum duration that SleepOnce() can sleep for.
@@ -553,7 +553,7 @@ void SleepOnce(absl::Duration to_sleep) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
extern "C" {
diff --git a/absl/time/clock.h b/absl/time/clock.h
index a3b9ffe9..27764a92 100644
--- a/absl/time/clock.h
+++ b/absl/time/clock.h
@@ -26,7 +26,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Now()
//
@@ -50,7 +50,7 @@ int64_t GetCurrentTimeNanos();
// * Returns immediately when passed a nonpositive duration.
void SleepFor(absl::Duration duration);
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
// -----------------------------------------------------------------------------
diff --git a/absl/time/duration.cc b/absl/time/duration.cc
index 6a51baff..b1af8406 100644
--- a/absl/time/duration.cc
+++ b/absl/time/duration.cc
@@ -71,7 +71,7 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
@@ -198,11 +198,11 @@ inline int64_t DecodeTwosComp(uint64_t v) { return absl::bit_cast<int64_t>(v); }
// double as overflow cases.
inline bool SafeAddRepHi(double a_hi, double b_hi, Duration* d) {
double c = a_hi + b_hi;
- if (c >= kint64max) {
+ if (c >= static_cast<double>(kint64max)) {
*d = InfiniteDuration();
return false;
}
- if (c <= kint64min) {
+ if (c <= static_cast<double>(kint64min)) {
*d = -InfiniteDuration();
return false;
}
@@ -907,11 +907,16 @@ bool ParseDuration(const std::string& dur_string, Duration* d) {
return true;
}
+bool AbslParseFlag(absl::string_view text, Duration* dst, std::string*) {
+ return ParseDuration(std::string(text), dst);
+}
+
+std::string AbslUnparseFlag(Duration d) { return FormatDuration(d); }
bool ParseFlag(const std::string& text, Duration* dst, std::string* ) {
return ParseDuration(text, dst);
}
std::string UnparseFlag(Duration d) { return FormatDuration(d); }
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc
index 5dce9ac8..4d85a2c4 100644
--- a/absl/time/duration_test.cc
+++ b/absl/time/duration_test.cc
@@ -762,11 +762,6 @@ TEST(Duration, DivisionByZero) {
const double dbl_inf = std::numeric_limits<double>::infinity();
const double dbl_denorm = std::numeric_limits<double>::denorm_min();
- // IEEE 754 behavior
- double z = 0.0, two = 2.0;
- EXPECT_TRUE(std::isinf(two / z));
- EXPECT_TRUE(std::isnan(z / z)); // We'll return inf
-
// Operator/(Duration, double)
EXPECT_EQ(inf, zero / 0.0);
EXPECT_EQ(-inf, zero / -0.0);
@@ -1050,7 +1045,7 @@ TEST(Duration, Multiplication) {
EXPECT_EQ(absl::Seconds(666666666) + absl::Nanoseconds(666666667) +
absl::Nanoseconds(1) / 2,
sigfigs / 3);
- sigfigs = absl::Seconds(7000000000LL);
+ sigfigs = absl::Seconds(int64_t{7000000000});
EXPECT_EQ(absl::Seconds(2333333333) + absl::Nanoseconds(333333333) +
absl::Nanoseconds(1) / 4,
sigfigs / 3);
diff --git a/absl/time/format.cc b/absl/time/format.cc
index 6eb83d7a..ee088f33 100644
--- a/absl/time/format.cc
+++ b/absl/time/format.cc
@@ -22,13 +22,16 @@
namespace cctz = absl::time_internal::cctz;
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
-extern const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
-extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
+ABSL_DLL extern const char RFC3339_full[] =
+ "%Y-%m-%dT%H:%M:%E*S%Ez";
+ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
-extern const char RFC1123_full[] = "%a, %d %b %E4Y %H:%M:%S %z";
-extern const char RFC1123_no_wday[] = "%d %b %E4Y %H:%M:%S %z";
+ABSL_DLL extern const char RFC1123_full[] =
+ "%a, %d %b %E4Y %H:%M:%S %z";
+ABSL_DLL extern const char RFC1123_no_wday[] =
+ "%d %b %E4Y %H:%M:%S %z";
namespace {
@@ -130,6 +133,14 @@ bool ParseTime(const std::string& format, const std::string& input,
}
// Functions required to support absl::Time flags.
+bool AbslParseFlag(absl::string_view text, absl::Time* t, std::string* error) {
+ return absl::ParseTime(RFC3339_full, std::string(text), absl::UTCTimeZone(),
+ t, error);
+}
+
+std::string AbslUnparseFlag(absl::Time t) {
+ return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone());
+}
bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) {
return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error);
}
@@ -138,5 +149,5 @@ std::string UnparseFlag(absl::Time t) {
return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone());
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel
index b05c2347..7a53c815 100644
--- a/absl/time/internal/cctz/BUILD.bazel
+++ b/absl/time/internal/cctz/BUILD.bazel
@@ -12,10 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
+
package(features = ["-parse_headers"])
licenses(["notice"]) # Apache License
+filegroup(
+ name = "zoneinfo",
+ srcs = glob(["testdata/zoneinfo/**"]),
+)
+
config_setting(
name = "osx",
constraint_values = [
@@ -33,16 +40,6 @@ config_setting(
### libraries
cc_library(
- name = "includes",
- textual_hdrs = [
- "include/cctz/civil_time.h",
- "include/cctz/civil_time_detail.h",
- "include/cctz/time_zone.h",
- ],
- visibility = ["//absl/time:__pkg__"],
-)
-
-cc_library(
name = "civil_time",
srcs = ["src/civil_time_detail.cc"],
hdrs = [
@@ -50,6 +47,7 @@ cc_library(
],
textual_hdrs = ["include/cctz/civil_time_detail.h"],
visibility = ["//visibility:public"],
+ deps = ["//absl/base:config"],
)
cc_library(
@@ -86,7 +84,10 @@ cc_library(
"//conditions:default": [],
}),
visibility = ["//visibility:public"],
- deps = [":civil_time"],
+ deps = [
+ ":civil_time",
+ "//absl/base:config",
+ ],
)
### tests
@@ -97,6 +98,7 @@ cc_test(
srcs = ["src/civil_time_test.cc"],
deps = [
":civil_time",
+ "//absl/base:config",
"@com_google_googletest//:gtest_main",
],
)
@@ -114,6 +116,7 @@ cc_test(
deps = [
":civil_time",
":time_zone",
+ "//absl/base:config",
"@com_google_googletest//:gtest_main",
],
)
@@ -132,6 +135,7 @@ cc_test(
deps = [
":civil_time",
":time_zone",
+ "//absl/base:config",
"@com_google_googletest//:gtest_main",
],
)
@@ -152,6 +156,7 @@ cc_test(
deps = [
":civil_time",
":time_zone",
+ "//absl/base:config",
"@com_github_google_benchmark//:benchmark_main",
],
)
@@ -159,8 +164,3 @@ cc_test(
### examples
### binaries
-
-filegroup(
- name = "zoneinfo",
- srcs = glob(["testdata/zoneinfo/**"]),
-)
diff --git a/absl/time/internal/cctz/include/cctz/civil_time.h b/absl/time/internal/cctz/include/cctz/civil_time.h
index df25db07..d47ff86f 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time.h
@@ -15,10 +15,11 @@
#ifndef ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
#define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -151,7 +152,7 @@ namespace cctz {
//
// All civil-time types have accessors for all six of the civil-time fields:
// year, month, day, hour, minute, and second. Recall that fields inferior to
-// the type's aligment will be set to their minimum valid value.
+// the type's alignment will be set to their minimum valid value.
//
// civil_day d(2015, 6, 28);
// // d.year() == 2015
@@ -325,7 +326,7 @@ using detail::get_yearday;
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
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 53e087a2..4cde96f1 100644
--- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h
+++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h
@@ -20,6 +20,8 @@
#include <ostream>
#include <type_traits>
+#include "absl/base/config.h"
+
// Disable constexpr support unless we are in C++14 mode.
#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910)
#define CONSTEXPR_D constexpr // data
@@ -32,7 +34,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -54,8 +56,8 @@ using second_t = std::int_fast8_t; // [0:59]
// Normalized civil-time fields: Y-M-D HH:MM:SS.
struct fields {
- CONSTEXPR_M fields(year_t year, month_t month, day_t day,
- hour_t hour, minute_t minute, second_t second)
+ CONSTEXPR_M fields(year_t year, month_t month, day_t day, hour_t hour,
+ minute_t minute, second_t second)
: y(year), m(month), d(day), hh(hour), mm(minute), ss(second) {}
std::int_least64_t y;
std::int_least8_t m;
@@ -102,8 +104,8 @@ CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept {
return k_days_per_month[m] + (m == 2 && is_leap_year(y));
}
-CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd,
- hour_t hh, minute_t mm, second_t ss) noexcept {
+CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh,
+ minute_t mm, second_t ss) noexcept {
y += (cd / 146097) * 400;
cd %= 146097;
if (cd < 0) {
@@ -153,8 +155,8 @@ CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd,
}
return fields(y, m, static_cast<day_t>(d), hh, mm, ss);
}
-CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd,
- hour_t hh, minute_t mm, second_t ss) noexcept {
+CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh,
+ minute_t mm, second_t ss) noexcept {
if (m != 12) {
y += m / 12;
m %= 12;
@@ -165,8 +167,8 @@ CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd,
}
return n_day(y, static_cast<month_t>(m), d, cd, hh, mm, ss);
}
-CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd,
- diff_t hh, minute_t mm, second_t ss) noexcept {
+CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, diff_t hh,
+ minute_t mm, second_t ss) noexcept {
cd += hh / 24;
hh %= 24;
if (hh < 0) {
@@ -265,8 +267,8 @@ CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept {
// yet the difference between two such extreme values may actually be
// small, so we take a little care to avoid overflow when possible by
// exploiting the 146097-day cycle.
-CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1,
- year_t y2, month_t m2, day_t d2) noexcept {
+CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, year_t y2,
+ month_t m2, day_t d2) noexcept {
const diff_t a_c4_off = y1 % 400;
const diff_t b_c4_off = y2 % 400;
diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off);
@@ -306,9 +308,7 @@ CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept {
////////////////////////////////////////////////////////////////////////
// Aligns the (normalized) fields struct to the indicated field.
-CONSTEXPR_F fields align(second_tag, fields f) noexcept {
- return f;
-}
+CONSTEXPR_F fields align(second_tag, fields f) noexcept { return f; }
CONSTEXPR_F fields align(minute_tag, fields f) noexcept {
return fields{f.y, f.m, f.d, f.hh, f.mm, 0};
}
@@ -387,11 +387,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 civil_time(max)() {
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 civil_time(min)() {
const auto min_year = (std::numeric_limits<std::int_least64_t>::min)();
return civil_time(min_year, 1, 1, 0, 0, 0);
}
@@ -417,17 +417,13 @@ class civil_time {
}
return *this;
}
- CONSTEXPR_M civil_time& operator++() noexcept {
- return *this += 1;
- }
+ CONSTEXPR_M civil_time& operator++() noexcept { return *this += 1; }
CONSTEXPR_M civil_time operator++(int) noexcept {
const civil_time a = *this;
++*this;
return a;
}
- CONSTEXPR_M civil_time& operator--() noexcept {
- return *this -= 1;
- }
+ CONSTEXPR_M civil_time& operator--() noexcept { return *this -= 1; }
CONSTEXPR_M civil_time operator--(int) noexcept {
const civil_time a = *this;
--*this;
@@ -484,17 +480,17 @@ using civil_second = civil_time<second_tag>;
template <typename T1, typename T2>
CONSTEXPR_F bool operator<(const civil_time<T1>& lhs,
const civil_time<T2>& rhs) noexcept {
- return (lhs.year() < rhs.year() ||
- (lhs.year() == rhs.year() &&
- (lhs.month() < rhs.month() ||
- (lhs.month() == rhs.month() &&
- (lhs.day() < rhs.day() ||
- (lhs.day() == rhs.day() &&
- (lhs.hour() < rhs.hour() ||
- (lhs.hour() == rhs.hour() &&
- (lhs.minute() < rhs.minute() ||
- (lhs.minute() == rhs.minute() &&
- (lhs.second() < rhs.second())))))))))));
+ return (
+ lhs.year() < rhs.year() ||
+ (lhs.year() == rhs.year() &&
+ (lhs.month() < rhs.month() ||
+ (lhs.month() == rhs.month() &&
+ (lhs.day() < rhs.day() || (lhs.day() == rhs.day() &&
+ (lhs.hour() < rhs.hour() ||
+ (lhs.hour() == rhs.hour() &&
+ (lhs.minute() < rhs.minute() ||
+ (lhs.minute() == rhs.minute() &&
+ (lhs.second() < rhs.second())))))))))));
}
template <typename T1, typename T2>
CONSTEXPR_F bool operator<=(const civil_time<T1>& lhs,
@@ -616,7 +612,7 @@ std::ostream& operator<<(std::ostream& os, weekday wd);
} // namespace detail
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#undef CONSTEXPR_M
diff --git a/absl/time/internal/cctz/include/cctz/time_zone.h b/absl/time/internal/cctz/include/cctz/time_zone.h
index f9769c0c..d05147a1 100644
--- a/absl/time/internal/cctz/include/cctz/time_zone.h
+++ b/absl/time/internal/cctz/include/cctz/time_zone.h
@@ -25,10 +25,11 @@
#include <string>
#include <utility>
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -40,8 +41,8 @@ using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead.
namespace detail {
template <typename D>
-inline std::pair<time_point<seconds>, D>
-split_seconds(const time_point<D>& tp) {
+inline std::pair<time_point<seconds>, D> split_seconds(
+ const time_point<D>& tp) {
auto sec = std::chrono::time_point_cast<seconds>(tp);
auto sub = tp - sec;
if (sub.count() < 0) {
@@ -50,8 +51,8 @@ split_seconds(const time_point<D>& tp) {
}
return {sec, std::chrono::duration_cast<D>(sub)};
}
-inline std::pair<time_point<seconds>, seconds>
-split_seconds(const time_point<seconds>& tp) {
+inline std::pair<time_point<seconds>, seconds> split_seconds(
+ const time_point<seconds>& tp) {
return {tp, seconds::zero()};
}
} // namespace detail
@@ -195,15 +196,13 @@ class time_zone {
bool next_transition(const time_point<seconds>& tp,
civil_transition* trans) const;
template <typename D>
- bool next_transition(const time_point<D>& tp,
- civil_transition* trans) const {
+ bool next_transition(const time_point<D>& tp, civil_transition* trans) const {
return next_transition(detail::split_seconds(tp).first, trans);
}
bool prev_transition(const time_point<seconds>& tp,
civil_transition* trans) const;
template <typename D>
- bool prev_transition(const time_point<D>& tp,
- civil_transition* trans) const {
+ bool prev_transition(const time_point<D>& tp, civil_transition* trans) const {
return prev_transition(detail::split_seconds(tp).first, trans);
}
@@ -221,9 +220,7 @@ class time_zone {
friend bool operator==(time_zone lhs, time_zone rhs) {
return &lhs.effective_impl() == &rhs.effective_impl();
}
- friend bool operator!=(time_zone lhs, time_zone rhs) {
- return !(lhs == rhs);
- }
+ friend bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); }
template <typename H>
friend H AbslHashValue(H h, time_zone tz) {
@@ -381,7 +378,7 @@ inline bool parse(const std::string& fmt, const std::string& input,
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
diff --git a/absl/time/internal/cctz/include/cctz/zone_info_source.h b/absl/time/internal/cctz/include/cctz/zone_info_source.h
index 7372c5de..912b44ba 100644
--- a/absl/time/internal/cctz/include/cctz/zone_info_source.h
+++ b/absl/time/internal/cctz/include/cctz/zone_info_source.h
@@ -20,8 +20,10 @@
#include <memory>
#include <string>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -31,7 +33,7 @@ class ZoneInfoSource {
virtual ~ZoneInfoSource();
virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread()
- virtual int Skip(std::size_t offset) = 0; // like fseek()
+ virtual int Skip(std::size_t offset) = 0; // like fseek()
// Until the zoneinfo data supports versioning information, we provide
// a way for a ZoneInfoSource to indicate it out-of-band. The default
@@ -41,11 +43,11 @@ class ZoneInfoSource {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz_extension {
@@ -55,8 +57,8 @@ namespace cctz_extension {
using ZoneInfoSourceFactory =
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> (*)(
const std::string&,
- const std::function<std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>(
- const std::string&)>&);
+ const std::function<std::unique_ptr<
+ absl::time_internal::cctz::ZoneInfoSource>(const std::string&)>&);
// The user can control the mapping of zone names to zoneinfo data by
// providing a definition for cctz_extension::zone_info_source_factory.
@@ -94,7 +96,7 @@ extern ZoneInfoSourceFactory zone_info_source_factory;
} // namespace cctz_extension
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc
index a40f504e..d30a644e 100644
--- a/absl/time/internal/cctz/src/cctz_benchmark.cc
+++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -105,601 +105,599 @@ const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
// A list of known time-zone names.
// TODO: Refactor with src/time_zone_lookup_test.cc.
-const char* const kTimeZoneNames[] = {
- "Africa/Abidjan",
- "Africa/Accra",
- "Africa/Addis_Ababa",
- "Africa/Algiers",
- "Africa/Asmara",
- "Africa/Asmera",
- "Africa/Bamako",
- "Africa/Bangui",
- "Africa/Banjul",
- "Africa/Bissau",
- "Africa/Blantyre",
- "Africa/Brazzaville",
- "Africa/Bujumbura",
- "Africa/Cairo",
- "Africa/Casablanca",
- "Africa/Ceuta",
- "Africa/Conakry",
- "Africa/Dakar",
- "Africa/Dar_es_Salaam",
- "Africa/Djibouti",
- "Africa/Douala",
- "Africa/El_Aaiun",
- "Africa/Freetown",
- "Africa/Gaborone",
- "Africa/Harare",
- "Africa/Johannesburg",
- "Africa/Juba",
- "Africa/Kampala",
- "Africa/Khartoum",
- "Africa/Kigali",
- "Africa/Kinshasa",
- "Africa/Lagos",
- "Africa/Libreville",
- "Africa/Lome",
- "Africa/Luanda",
- "Africa/Lubumbashi",
- "Africa/Lusaka",
- "Africa/Malabo",
- "Africa/Maputo",
- "Africa/Maseru",
- "Africa/Mbabane",
- "Africa/Mogadishu",
- "Africa/Monrovia",
- "Africa/Nairobi",
- "Africa/Ndjamena",
- "Africa/Niamey",
- "Africa/Nouakchott",
- "Africa/Ouagadougou",
- "Africa/Porto-Novo",
- "Africa/Sao_Tome",
- "Africa/Timbuktu",
- "Africa/Tripoli",
- "Africa/Tunis",
- "Africa/Windhoek",
- "America/Adak",
- "America/Anchorage",
- "America/Anguilla",
- "America/Antigua",
- "America/Araguaina",
- "America/Argentina/Buenos_Aires",
- "America/Argentina/Catamarca",
- "America/Argentina/ComodRivadavia",
- "America/Argentina/Cordoba",
- "America/Argentina/Jujuy",
- "America/Argentina/La_Rioja",
- "America/Argentina/Mendoza",
- "America/Argentina/Rio_Gallegos",
- "America/Argentina/Salta",
- "America/Argentina/San_Juan",
- "America/Argentina/San_Luis",
- "America/Argentina/Tucuman",
- "America/Argentina/Ushuaia",
- "America/Aruba",
- "America/Asuncion",
- "America/Atikokan",
- "America/Atka",
- "America/Bahia",
- "America/Bahia_Banderas",
- "America/Barbados",
- "America/Belem",
- "America/Belize",
- "America/Blanc-Sablon",
- "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/Coral_Harbour",
- "America/Cordoba",
- "America/Costa_Rica",
- "America/Creston",
- "America/Cuiaba",
- "America/Curacao",
- "America/Danmarkshavn",
- "America/Dawson",
- "America/Dawson_Creek",
- "America/Denver",
- "America/Detroit",
- "America/Dominica",
- "America/Edmonton",
- "America/Eirunepe",
- "America/El_Salvador",
- "America/Ensenada",
- "America/Fort_Nelson",
- "America/Fort_Wayne",
- "America/Fortaleza",
- "America/Glace_Bay",
- "America/Godthab",
- "America/Goose_Bay",
- "America/Grand_Turk",
- "America/Grenada",
- "America/Guadeloupe",
- "America/Guatemala",
- "America/Guayaquil",
- "America/Guyana",
- "America/Halifax",
- "America/Havana",
- "America/Hermosillo",
- "America/Indiana/Indianapolis",
- "America/Indiana/Knox",
- "America/Indiana/Marengo",
- "America/Indiana/Petersburg",
- "America/Indiana/Tell_City",
- "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",
- "America/Manaus",
- "America/Marigot",
- "America/Martinique",
- "America/Matamoros",
- "America/Mazatlan",
- "America/Mendoza",
- "America/Menominee",
- "America/Merida",
- "America/Metlakatla",
- "America/Mexico_City",
- "America/Miquelon",
- "America/Moncton",
- "America/Monterrey",
- "America/Montevideo",
- "America/Montreal",
- "America/Montserrat",
- "America/Nassau",
- "America/New_York",
- "America/Nipigon",
- "America/Nome",
- "America/Noronha",
- "America/North_Dakota/Beulah",
- "America/North_Dakota/Center",
- "America/North_Dakota/New_Salem",
- "America/Ojinaga",
- "America/Panama",
- "America/Pangnirtung",
- "America/Paramaribo",
- "America/Phoenix",
- "America/Port-au-Prince",
- "America/Port_of_Spain",
- "America/Porto_Acre",
- "America/Porto_Velho",
- "America/Puerto_Rico",
- "America/Punta_Arenas",
- "America/Rainy_River",
- "America/Rankin_Inlet",
- "America/Recife",
- "America/Regina",
- "America/Resolute",
- "America/Rio_Branco",
- "America/Rosario",
- "America/Santa_Isabel",
- "America/Santarem",
- "America/Santiago",
- "America/Santo_Domingo",
- "America/Sao_Paulo",
- "America/Scoresbysund",
- "America/Shiprock",
- "America/Sitka",
- "America/St_Barthelemy",
- "America/St_Johns",
- "America/St_Kitts",
- "America/St_Lucia",
- "America/St_Thomas",
- "America/St_Vincent",
- "America/Swift_Current",
- "America/Tegucigalpa",
- "America/Thule",
- "America/Thunder_Bay",
- "America/Tijuana",
- "America/Toronto",
- "America/Tortola",
- "America/Vancouver",
- "America/Virgin",
- "America/Whitehorse",
- "America/Winnipeg",
- "America/Yakutat",
- "America/Yellowknife",
- "Antarctica/Casey",
- "Antarctica/Davis",
- "Antarctica/DumontDUrville",
- "Antarctica/Macquarie",
- "Antarctica/Mawson",
- "Antarctica/McMurdo",
- "Antarctica/Palmer",
- "Antarctica/Rothera",
- "Antarctica/South_Pole",
- "Antarctica/Syowa",
- "Antarctica/Troll",
- "Antarctica/Vostok",
- "Arctic/Longyearbyen",
- "Asia/Aden",
- "Asia/Almaty",
- "Asia/Amman",
- "Asia/Anadyr",
- "Asia/Aqtau",
- "Asia/Aqtobe",
- "Asia/Ashgabat",
- "Asia/Ashkhabad",
- "Asia/Atyrau",
- "Asia/Baghdad",
- "Asia/Bahrain",
- "Asia/Baku",
- "Asia/Bangkok",
- "Asia/Barnaul",
- "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",
- "Asia/Dubai",
- "Asia/Dushanbe",
- "Asia/Famagusta",
- "Asia/Gaza",
- "Asia/Harbin",
- "Asia/Hebron",
- "Asia/Ho_Chi_Minh",
- "Asia/Hong_Kong",
- "Asia/Hovd",
- "Asia/Irkutsk",
- "Asia/Istanbul",
- "Asia/Jakarta",
- "Asia/Jayapura",
- "Asia/Jerusalem",
- "Asia/Kabul",
- "Asia/Kamchatka",
- "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",
- "Asia/Manila",
- "Asia/Muscat",
- "Asia/Nicosia",
- "Asia/Novokuznetsk",
- "Asia/Novosibirsk",
- "Asia/Omsk",
- "Asia/Oral",
- "Asia/Phnom_Penh",
- "Asia/Pontianak",
- "Asia/Pyongyang",
- "Asia/Qatar",
- "Asia/Qostanay",
- "Asia/Qyzylorda",
- "Asia/Rangoon",
- "Asia/Riyadh",
- "Asia/Saigon",
- "Asia/Sakhalin",
- "Asia/Samarkand",
- "Asia/Seoul",
- "Asia/Shanghai",
- "Asia/Singapore",
- "Asia/Srednekolymsk",
- "Asia/Taipei",
- "Asia/Tashkent",
- "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",
- "Asia/Vladivostok",
- "Asia/Yakutsk",
- "Asia/Yangon",
- "Asia/Yekaterinburg",
- "Asia/Yerevan",
- "Atlantic/Azores",
- "Atlantic/Bermuda",
- "Atlantic/Canary",
- "Atlantic/Cape_Verde",
- "Atlantic/Faeroe",
- "Atlantic/Faroe",
- "Atlantic/Jan_Mayen",
- "Atlantic/Madeira",
- "Atlantic/Reykjavik",
- "Atlantic/South_Georgia",
- "Atlantic/St_Helena",
- "Atlantic/Stanley",
- "Australia/ACT",
- "Australia/Adelaide",
- "Australia/Brisbane",
- "Australia/Broken_Hill",
- "Australia/Canberra",
- "Australia/Currie",
- "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",
- "Etc/GMT+10",
- "Etc/GMT+11",
- "Etc/GMT+12",
- "Etc/GMT+2",
- "Etc/GMT+3",
- "Etc/GMT+4",
- "Etc/GMT+5",
- "Etc/GMT+6",
- "Etc/GMT+7",
- "Etc/GMT+8",
- "Etc/GMT+9",
- "Etc/GMT-0",
- "Etc/GMT-1",
- "Etc/GMT-10",
- "Etc/GMT-11",
- "Etc/GMT-12",
- "Etc/GMT-13",
- "Etc/GMT-14",
- "Etc/GMT-2",
- "Etc/GMT-3",
- "Etc/GMT-4",
- "Etc/GMT-5",
- "Etc/GMT-6",
- "Etc/GMT-7",
- "Etc/GMT-8",
- "Etc/GMT-9",
- "Etc/GMT0",
- "Etc/Greenwich",
- "Etc/UCT",
- "Etc/UTC",
- "Etc/Universal",
- "Etc/Zulu",
- "Europe/Amsterdam",
- "Europe/Andorra",
- "Europe/Astrakhan",
- "Europe/Athens",
- "Europe/Belfast",
- "Europe/Belgrade",
- "Europe/Berlin",
- "Europe/Bratislava",
- "Europe/Brussels",
- "Europe/Bucharest",
- "Europe/Budapest",
- "Europe/Busingen",
- "Europe/Chisinau",
- "Europe/Copenhagen",
- "Europe/Dublin",
- "Europe/Gibraltar",
- "Europe/Guernsey",
- "Europe/Helsinki",
- "Europe/Isle_of_Man",
- "Europe/Istanbul",
- "Europe/Jersey",
- "Europe/Kaliningrad",
- "Europe/Kiev",
- "Europe/Kirov",
- "Europe/Lisbon",
- "Europe/Ljubljana",
- "Europe/London",
- "Europe/Luxembourg",
- "Europe/Madrid",
- "Europe/Malta",
- "Europe/Mariehamn",
- "Europe/Minsk",
- "Europe/Monaco",
- "Europe/Moscow",
- "Europe/Nicosia",
- "Europe/Oslo",
- "Europe/Paris",
- "Europe/Podgorica",
- "Europe/Prague",
- "Europe/Riga",
- "Europe/Rome",
- "Europe/Samara",
- "Europe/San_Marino",
- "Europe/Sarajevo",
- "Europe/Saratov",
- "Europe/Simferopol",
- "Europe/Skopje",
- "Europe/Sofia",
- "Europe/Stockholm",
- "Europe/Tallinn",
- "Europe/Tirane",
- "Europe/Tiraspol",
- "Europe/Ulyanovsk",
- "Europe/Uzhgorod",
- "Europe/Vaduz",
- "Europe/Vatican",
- "Europe/Vienna",
- "Europe/Vilnius",
- "Europe/Volgograd",
- "Europe/Warsaw",
- "Europe/Zagreb",
- "Europe/Zaporozhye",
- "Europe/Zurich",
- "GB",
- "GB-Eire",
- "GMT",
- "GMT+0",
- "GMT-0",
- "GMT0",
- "Greenwich",
- "HST",
- "Hongkong",
- "Iceland",
- "Indian/Antananarivo",
- "Indian/Chagos",
- "Indian/Christmas",
- "Indian/Cocos",
- "Indian/Comoro",
- "Indian/Kerguelen",
- "Indian/Mahe",
- "Indian/Maldives",
- "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",
- "Pacific/Chatham",
- "Pacific/Chuuk",
- "Pacific/Easter",
- "Pacific/Efate",
- "Pacific/Enderbury",
- "Pacific/Fakaofo",
- "Pacific/Fiji",
- "Pacific/Funafuti",
- "Pacific/Galapagos",
- "Pacific/Gambier",
- "Pacific/Guadalcanal",
- "Pacific/Guam",
- "Pacific/Honolulu",
- "Pacific/Johnston",
- "Pacific/Kiritimati",
- "Pacific/Kosrae",
- "Pacific/Kwajalein",
- "Pacific/Majuro",
- "Pacific/Marquesas",
- "Pacific/Midway",
- "Pacific/Nauru",
- "Pacific/Niue",
- "Pacific/Norfolk",
- "Pacific/Noumea",
- "Pacific/Pago_Pago",
- "Pacific/Palau",
- "Pacific/Pitcairn",
- "Pacific/Pohnpei",
- "Pacific/Ponape",
- "Pacific/Port_Moresby",
- "Pacific/Rarotonga",
- "Pacific/Saipan",
- "Pacific/Samoa",
- "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
-};
+const char* const kTimeZoneNames[] = {"Africa/Abidjan",
+ "Africa/Accra",
+ "Africa/Addis_Ababa",
+ "Africa/Algiers",
+ "Africa/Asmara",
+ "Africa/Asmera",
+ "Africa/Bamako",
+ "Africa/Bangui",
+ "Africa/Banjul",
+ "Africa/Bissau",
+ "Africa/Blantyre",
+ "Africa/Brazzaville",
+ "Africa/Bujumbura",
+ "Africa/Cairo",
+ "Africa/Casablanca",
+ "Africa/Ceuta",
+ "Africa/Conakry",
+ "Africa/Dakar",
+ "Africa/Dar_es_Salaam",
+ "Africa/Djibouti",
+ "Africa/Douala",
+ "Africa/El_Aaiun",
+ "Africa/Freetown",
+ "Africa/Gaborone",
+ "Africa/Harare",
+ "Africa/Johannesburg",
+ "Africa/Juba",
+ "Africa/Kampala",
+ "Africa/Khartoum",
+ "Africa/Kigali",
+ "Africa/Kinshasa",
+ "Africa/Lagos",
+ "Africa/Libreville",
+ "Africa/Lome",
+ "Africa/Luanda",
+ "Africa/Lubumbashi",
+ "Africa/Lusaka",
+ "Africa/Malabo",
+ "Africa/Maputo",
+ "Africa/Maseru",
+ "Africa/Mbabane",
+ "Africa/Mogadishu",
+ "Africa/Monrovia",
+ "Africa/Nairobi",
+ "Africa/Ndjamena",
+ "Africa/Niamey",
+ "Africa/Nouakchott",
+ "Africa/Ouagadougou",
+ "Africa/Porto-Novo",
+ "Africa/Sao_Tome",
+ "Africa/Timbuktu",
+ "Africa/Tripoli",
+ "Africa/Tunis",
+ "Africa/Windhoek",
+ "America/Adak",
+ "America/Anchorage",
+ "America/Anguilla",
+ "America/Antigua",
+ "America/Araguaina",
+ "America/Argentina/Buenos_Aires",
+ "America/Argentina/Catamarca",
+ "America/Argentina/ComodRivadavia",
+ "America/Argentina/Cordoba",
+ "America/Argentina/Jujuy",
+ "America/Argentina/La_Rioja",
+ "America/Argentina/Mendoza",
+ "America/Argentina/Rio_Gallegos",
+ "America/Argentina/Salta",
+ "America/Argentina/San_Juan",
+ "America/Argentina/San_Luis",
+ "America/Argentina/Tucuman",
+ "America/Argentina/Ushuaia",
+ "America/Aruba",
+ "America/Asuncion",
+ "America/Atikokan",
+ "America/Atka",
+ "America/Bahia",
+ "America/Bahia_Banderas",
+ "America/Barbados",
+ "America/Belem",
+ "America/Belize",
+ "America/Blanc-Sablon",
+ "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/Coral_Harbour",
+ "America/Cordoba",
+ "America/Costa_Rica",
+ "America/Creston",
+ "America/Cuiaba",
+ "America/Curacao",
+ "America/Danmarkshavn",
+ "America/Dawson",
+ "America/Dawson_Creek",
+ "America/Denver",
+ "America/Detroit",
+ "America/Dominica",
+ "America/Edmonton",
+ "America/Eirunepe",
+ "America/El_Salvador",
+ "America/Ensenada",
+ "America/Fort_Nelson",
+ "America/Fort_Wayne",
+ "America/Fortaleza",
+ "America/Glace_Bay",
+ "America/Godthab",
+ "America/Goose_Bay",
+ "America/Grand_Turk",
+ "America/Grenada",
+ "America/Guadeloupe",
+ "America/Guatemala",
+ "America/Guayaquil",
+ "America/Guyana",
+ "America/Halifax",
+ "America/Havana",
+ "America/Hermosillo",
+ "America/Indiana/Indianapolis",
+ "America/Indiana/Knox",
+ "America/Indiana/Marengo",
+ "America/Indiana/Petersburg",
+ "America/Indiana/Tell_City",
+ "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",
+ "America/Manaus",
+ "America/Marigot",
+ "America/Martinique",
+ "America/Matamoros",
+ "America/Mazatlan",
+ "America/Mendoza",
+ "America/Menominee",
+ "America/Merida",
+ "America/Metlakatla",
+ "America/Mexico_City",
+ "America/Miquelon",
+ "America/Moncton",
+ "America/Monterrey",
+ "America/Montevideo",
+ "America/Montreal",
+ "America/Montserrat",
+ "America/Nassau",
+ "America/New_York",
+ "America/Nipigon",
+ "America/Nome",
+ "America/Noronha",
+ "America/North_Dakota/Beulah",
+ "America/North_Dakota/Center",
+ "America/North_Dakota/New_Salem",
+ "America/Ojinaga",
+ "America/Panama",
+ "America/Pangnirtung",
+ "America/Paramaribo",
+ "America/Phoenix",
+ "America/Port-au-Prince",
+ "America/Port_of_Spain",
+ "America/Porto_Acre",
+ "America/Porto_Velho",
+ "America/Puerto_Rico",
+ "America/Punta_Arenas",
+ "America/Rainy_River",
+ "America/Rankin_Inlet",
+ "America/Recife",
+ "America/Regina",
+ "America/Resolute",
+ "America/Rio_Branco",
+ "America/Rosario",
+ "America/Santa_Isabel",
+ "America/Santarem",
+ "America/Santiago",
+ "America/Santo_Domingo",
+ "America/Sao_Paulo",
+ "America/Scoresbysund",
+ "America/Shiprock",
+ "America/Sitka",
+ "America/St_Barthelemy",
+ "America/St_Johns",
+ "America/St_Kitts",
+ "America/St_Lucia",
+ "America/St_Thomas",
+ "America/St_Vincent",
+ "America/Swift_Current",
+ "America/Tegucigalpa",
+ "America/Thule",
+ "America/Thunder_Bay",
+ "America/Tijuana",
+ "America/Toronto",
+ "America/Tortola",
+ "America/Vancouver",
+ "America/Virgin",
+ "America/Whitehorse",
+ "America/Winnipeg",
+ "America/Yakutat",
+ "America/Yellowknife",
+ "Antarctica/Casey",
+ "Antarctica/Davis",
+ "Antarctica/DumontDUrville",
+ "Antarctica/Macquarie",
+ "Antarctica/Mawson",
+ "Antarctica/McMurdo",
+ "Antarctica/Palmer",
+ "Antarctica/Rothera",
+ "Antarctica/South_Pole",
+ "Antarctica/Syowa",
+ "Antarctica/Troll",
+ "Antarctica/Vostok",
+ "Arctic/Longyearbyen",
+ "Asia/Aden",
+ "Asia/Almaty",
+ "Asia/Amman",
+ "Asia/Anadyr",
+ "Asia/Aqtau",
+ "Asia/Aqtobe",
+ "Asia/Ashgabat",
+ "Asia/Ashkhabad",
+ "Asia/Atyrau",
+ "Asia/Baghdad",
+ "Asia/Bahrain",
+ "Asia/Baku",
+ "Asia/Bangkok",
+ "Asia/Barnaul",
+ "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",
+ "Asia/Dubai",
+ "Asia/Dushanbe",
+ "Asia/Famagusta",
+ "Asia/Gaza",
+ "Asia/Harbin",
+ "Asia/Hebron",
+ "Asia/Ho_Chi_Minh",
+ "Asia/Hong_Kong",
+ "Asia/Hovd",
+ "Asia/Irkutsk",
+ "Asia/Istanbul",
+ "Asia/Jakarta",
+ "Asia/Jayapura",
+ "Asia/Jerusalem",
+ "Asia/Kabul",
+ "Asia/Kamchatka",
+ "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",
+ "Asia/Manila",
+ "Asia/Muscat",
+ "Asia/Nicosia",
+ "Asia/Novokuznetsk",
+ "Asia/Novosibirsk",
+ "Asia/Omsk",
+ "Asia/Oral",
+ "Asia/Phnom_Penh",
+ "Asia/Pontianak",
+ "Asia/Pyongyang",
+ "Asia/Qatar",
+ "Asia/Qostanay",
+ "Asia/Qyzylorda",
+ "Asia/Rangoon",
+ "Asia/Riyadh",
+ "Asia/Saigon",
+ "Asia/Sakhalin",
+ "Asia/Samarkand",
+ "Asia/Seoul",
+ "Asia/Shanghai",
+ "Asia/Singapore",
+ "Asia/Srednekolymsk",
+ "Asia/Taipei",
+ "Asia/Tashkent",
+ "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",
+ "Asia/Vladivostok",
+ "Asia/Yakutsk",
+ "Asia/Yangon",
+ "Asia/Yekaterinburg",
+ "Asia/Yerevan",
+ "Atlantic/Azores",
+ "Atlantic/Bermuda",
+ "Atlantic/Canary",
+ "Atlantic/Cape_Verde",
+ "Atlantic/Faeroe",
+ "Atlantic/Faroe",
+ "Atlantic/Jan_Mayen",
+ "Atlantic/Madeira",
+ "Atlantic/Reykjavik",
+ "Atlantic/South_Georgia",
+ "Atlantic/St_Helena",
+ "Atlantic/Stanley",
+ "Australia/ACT",
+ "Australia/Adelaide",
+ "Australia/Brisbane",
+ "Australia/Broken_Hill",
+ "Australia/Canberra",
+ "Australia/Currie",
+ "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",
+ "Etc/GMT+10",
+ "Etc/GMT+11",
+ "Etc/GMT+12",
+ "Etc/GMT+2",
+ "Etc/GMT+3",
+ "Etc/GMT+4",
+ "Etc/GMT+5",
+ "Etc/GMT+6",
+ "Etc/GMT+7",
+ "Etc/GMT+8",
+ "Etc/GMT+9",
+ "Etc/GMT-0",
+ "Etc/GMT-1",
+ "Etc/GMT-10",
+ "Etc/GMT-11",
+ "Etc/GMT-12",
+ "Etc/GMT-13",
+ "Etc/GMT-14",
+ "Etc/GMT-2",
+ "Etc/GMT-3",
+ "Etc/GMT-4",
+ "Etc/GMT-5",
+ "Etc/GMT-6",
+ "Etc/GMT-7",
+ "Etc/GMT-8",
+ "Etc/GMT-9",
+ "Etc/GMT0",
+ "Etc/Greenwich",
+ "Etc/UCT",
+ "Etc/UTC",
+ "Etc/Universal",
+ "Etc/Zulu",
+ "Europe/Amsterdam",
+ "Europe/Andorra",
+ "Europe/Astrakhan",
+ "Europe/Athens",
+ "Europe/Belfast",
+ "Europe/Belgrade",
+ "Europe/Berlin",
+ "Europe/Bratislava",
+ "Europe/Brussels",
+ "Europe/Bucharest",
+ "Europe/Budapest",
+ "Europe/Busingen",
+ "Europe/Chisinau",
+ "Europe/Copenhagen",
+ "Europe/Dublin",
+ "Europe/Gibraltar",
+ "Europe/Guernsey",
+ "Europe/Helsinki",
+ "Europe/Isle_of_Man",
+ "Europe/Istanbul",
+ "Europe/Jersey",
+ "Europe/Kaliningrad",
+ "Europe/Kiev",
+ "Europe/Kirov",
+ "Europe/Lisbon",
+ "Europe/Ljubljana",
+ "Europe/London",
+ "Europe/Luxembourg",
+ "Europe/Madrid",
+ "Europe/Malta",
+ "Europe/Mariehamn",
+ "Europe/Minsk",
+ "Europe/Monaco",
+ "Europe/Moscow",
+ "Europe/Nicosia",
+ "Europe/Oslo",
+ "Europe/Paris",
+ "Europe/Podgorica",
+ "Europe/Prague",
+ "Europe/Riga",
+ "Europe/Rome",
+ "Europe/Samara",
+ "Europe/San_Marino",
+ "Europe/Sarajevo",
+ "Europe/Saratov",
+ "Europe/Simferopol",
+ "Europe/Skopje",
+ "Europe/Sofia",
+ "Europe/Stockholm",
+ "Europe/Tallinn",
+ "Europe/Tirane",
+ "Europe/Tiraspol",
+ "Europe/Ulyanovsk",
+ "Europe/Uzhgorod",
+ "Europe/Vaduz",
+ "Europe/Vatican",
+ "Europe/Vienna",
+ "Europe/Vilnius",
+ "Europe/Volgograd",
+ "Europe/Warsaw",
+ "Europe/Zagreb",
+ "Europe/Zaporozhye",
+ "Europe/Zurich",
+ "GB",
+ "GB-Eire",
+ "GMT",
+ "GMT+0",
+ "GMT-0",
+ "GMT0",
+ "Greenwich",
+ "HST",
+ "Hongkong",
+ "Iceland",
+ "Indian/Antananarivo",
+ "Indian/Chagos",
+ "Indian/Christmas",
+ "Indian/Cocos",
+ "Indian/Comoro",
+ "Indian/Kerguelen",
+ "Indian/Mahe",
+ "Indian/Maldives",
+ "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",
+ "Pacific/Chatham",
+ "Pacific/Chuuk",
+ "Pacific/Easter",
+ "Pacific/Efate",
+ "Pacific/Enderbury",
+ "Pacific/Fakaofo",
+ "Pacific/Fiji",
+ "Pacific/Funafuti",
+ "Pacific/Galapagos",
+ "Pacific/Gambier",
+ "Pacific/Guadalcanal",
+ "Pacific/Guam",
+ "Pacific/Honolulu",
+ "Pacific/Johnston",
+ "Pacific/Kiritimati",
+ "Pacific/Kosrae",
+ "Pacific/Kwajalein",
+ "Pacific/Majuro",
+ "Pacific/Marquesas",
+ "Pacific/Midway",
+ "Pacific/Nauru",
+ "Pacific/Niue",
+ "Pacific/Norfolk",
+ "Pacific/Noumea",
+ "Pacific/Pago_Pago",
+ "Pacific/Palau",
+ "Pacific/Pitcairn",
+ "Pacific/Pohnpei",
+ "Pacific/Ponape",
+ "Pacific/Port_Moresby",
+ "Pacific/Rarotonga",
+ "Pacific/Saipan",
+ "Pacific/Samoa",
+ "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() {
std::vector<std::string> names;
diff --git a/absl/time/internal/cctz/src/civil_time_detail.cc b/absl/time/internal/cctz/src/civil_time_detail.cc
index 4df15d55..0b07e397 100644
--- a/absl/time/internal/cctz/src/civil_time_detail.cc
+++ b/absl/time/internal/cctz/src/civil_time_detail.cc
@@ -18,8 +18,10 @@
#include <ostream>
#include <sstream>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
namespace detail {
@@ -88,5 +90,5 @@ std::ostream& operator<<(std::ostream& os, weekday wd) {
} // namespace detail
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/civil_time_test.cc b/absl/time/internal/cctz/src/civil_time_test.cc
index 7d9a1834..be894d70 100644
--- a/absl/time/internal/cctz/src/civil_time_test.cc
+++ b/absl/time/internal/cctz/src/civil_time_test.cc
@@ -21,9 +21,10 @@
#include <type_traits>
#include "gtest/gtest.h"
+#include "absl/base/config.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -1015,19 +1016,13 @@ TEST(CivilTime, LeapYears) {
int day;
} leap_day; // The date of the day after Feb 28.
} kLeapYearTable[]{
- {1900, 365, {3, 1}},
- {1999, 365, {3, 1}},
+ {1900, 365, {3, 1}}, {1999, 365, {3, 1}},
{2000, 366, {2, 29}}, // leap year
- {2001, 365, {3, 1}},
- {2002, 365, {3, 1}},
- {2003, 365, {3, 1}},
- {2004, 366, {2, 29}}, // leap year
- {2005, 365, {3, 1}},
- {2006, 365, {3, 1}},
- {2007, 365, {3, 1}},
- {2008, 366, {2, 29}}, // leap year
- {2009, 365, {3, 1}},
- {2100, 365, {3, 1}},
+ {2001, 365, {3, 1}}, {2002, 365, {3, 1}},
+ {2003, 365, {3, 1}}, {2004, 366, {2, 29}}, // leap year
+ {2005, 365, {3, 1}}, {2006, 365, {3, 1}},
+ {2007, 365, {3, 1}}, {2008, 366, {2, 29}}, // leap year
+ {2009, 365, {3, 1}}, {2100, 365, {3, 1}},
};
for (const auto& e : kLeapYearTable) {
@@ -1057,5 +1052,5 @@ TEST(CivilTime, FirstThursdayInMonth) {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc
index aa5af020..303c0244 100644
--- a/absl/time/internal/cctz/src/time_zone_fixed.cc
+++ b/absl/time/internal/cctz/src/time_zone_fixed.cc
@@ -20,8 +20,10 @@
#include <cstring>
#include <string>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -60,11 +62,9 @@ bool FixedOffsetFromName(const std::string& name, seconds* offset) {
const char* const ep = kFixedZonePrefix + prefix_len;
if (name.size() != prefix_len + 9) // <prefix>+99:99:99
return false;
- if (!std::equal(kFixedZonePrefix, ep, name.begin()))
- return false;
+ if (!std::equal(kFixedZonePrefix, ep, name.begin())) return false;
const char* np = name.data() + prefix_len;
- if (np[0] != '+' && np[0] != '-')
- return false;
+ if (np[0] != '+' && np[0] != '-') return false;
if (np[3] != ':' || np[6] != ':') // see note below about large offsets
return false;
@@ -89,29 +89,29 @@ std::string FixedOffsetToName(const seconds& offset) {
// offsets and to (somewhat) limit the total number of zones.
return "UTC";
}
- int seconds = static_cast<int>(offset.count());
- const char sign = (seconds < 0 ? '-' : '+');
- int minutes = seconds / 60;
- seconds %= 60;
+ int offset_seconds = static_cast<int>(offset.count());
+ const char sign = (offset_seconds < 0 ? '-' : '+');
+ int offset_minutes = offset_seconds / 60;
+ offset_seconds %= 60;
if (sign == '-') {
- if (seconds > 0) {
- seconds -= 60;
- minutes += 1;
+ if (offset_seconds > 0) {
+ offset_seconds -= 60;
+ offset_minutes += 1;
}
- seconds = -seconds;
- minutes = -minutes;
+ offset_seconds = -offset_seconds;
+ offset_minutes = -offset_minutes;
}
- int hours = minutes / 60;
- minutes %= 60;
+ int offset_hours = offset_minutes / 60;
+ offset_minutes %= 60;
const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
char buf[prefix_len + sizeof("-24:00:00")];
char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf);
*ep++ = sign;
- ep = Format02d(ep, hours);
+ ep = Format02d(ep, offset_hours);
*ep++ = ':';
- ep = Format02d(ep, minutes);
+ ep = Format02d(ep, offset_minutes);
*ep++ = ':';
- ep = Format02d(ep, seconds);
+ ep = Format02d(ep, offset_seconds);
*ep++ = '\0';
assert(ep == buf + sizeof(buf));
return buf;
@@ -136,5 +136,5 @@ std::string FixedOffsetToAbbr(const seconds& offset) {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_fixed.h b/absl/time/internal/cctz/src/time_zone_fixed.h
index 15ea3bdf..e74a0bbd 100644
--- a/absl/time/internal/cctz/src/time_zone_fixed.h
+++ b/absl/time/internal/cctz/src/time_zone_fixed.h
@@ -17,10 +17,11 @@
#include <string>
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -45,7 +46,7 @@ std::string FixedOffsetToAbbr(const seconds& offset);
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc
index ee2ffac0..950b23a1 100644
--- a/absl/time/internal/cctz/src/time_zone_format.cc
+++ b/absl/time/internal/cctz/src/time_zone_format.cc
@@ -13,17 +13,18 @@
// limitations under the License.
#if !defined(HAS_STRPTIME)
-# if !defined(_MSC_VER) && !defined(__MINGW32__)
-# define HAS_STRPTIME 1 // assume everyone has strptime() except windows
-# endif
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#define HAS_STRPTIME 1 // assume everyone has strptime() except windows
+#endif
#endif
#if defined(HAS_STRPTIME) && HAS_STRPTIME
-# if !defined(_XOPEN_SOURCE)
-# define _XOPEN_SOURCE // Definedness suffices for strptime.
-# endif
+#if !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE // Definedness suffices for strptime.
+#endif
#endif
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
// Include time.h directly since, by C++ standards, ctime doesn't have to
@@ -48,7 +49,7 @@
#include "time_zone_if.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
namespace detail {
@@ -503,8 +504,9 @@ std::string format(const std::string& format, const time_point<seconds>& tp,
bp = ep;
if (n > 0) {
if (n > kDigits10_64) n = kDigits10_64;
- bp = Format64(bp, n, (n > 15) ? fs.count() * kExp10[n - 15]
- : fs.count() / kExp10[15 - n]);
+ bp = Format64(bp, n,
+ (n > 15) ? fs.count() * kExp10[n - 15]
+ : fs.count() / kExp10[15 - n]);
if (*np == 'S') *--bp = '.';
}
if (*np == 'S') bp = Format02d(bp, al.cs.second());
@@ -721,10 +723,9 @@ bool parse(const std::string& format, const std::string& input,
data = ParseZone(data, &zone);
continue;
case 's':
- data = ParseInt(data, 0,
- std::numeric_limits<std::int_fast64_t>::min(),
- std::numeric_limits<std::int_fast64_t>::max(),
- &percent_s);
+ data =
+ ParseInt(data, 0, std::numeric_limits<std::int_fast64_t>::min(),
+ std::numeric_limits<std::int_fast64_t>::max(), &percent_s);
if (data != nullptr) saw_percent_s = true;
continue;
case ':':
@@ -917,5 +918,5 @@ bool parse(const std::string& format, const std::string& input,
} // namespace detail
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc
index 49737445..caebcc4d 100644
--- a/absl/time/internal/cctz/src/time_zone_format_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_format_test.cc
@@ -12,21 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
#include <chrono>
#include <iomanip>
#include <sstream>
#include <string>
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/time/internal/cctz/include/cctz/civil_time.h"
+#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace chrono = std::chrono;
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -49,10 +49,10 @@ namespace {
} while (0)
const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez";
-const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
+const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
-const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
+const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
// A helper that tests the given format specifier by itself, and with leading
// and trailing characters. For example: TestFormatSpecifier(tp, "%a", "Thu").
@@ -89,8 +89,11 @@ TEST(Format, TimePointResolution) {
format(kFmt, chrono::time_point_cast<chrono::milliseconds>(t0), utc));
EXPECT_EQ("03:04:05",
format(kFmt, chrono::time_point_cast<chrono::seconds>(t0), utc));
- EXPECT_EQ("03:04:05",
- format(kFmt, chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), utc));
+ EXPECT_EQ(
+ "03:04:05",
+ format(kFmt,
+ chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0),
+ utc));
EXPECT_EQ("03:04:00",
format(kFmt, chrono::time_point_cast<chrono::minutes>(t0), utc));
EXPECT_EQ("03:00:00",
@@ -111,12 +114,10 @@ TEST(Format, TimePointExtendedResolution) {
EXPECT_EQ(
"12:34:56.012345678901234",
detail::format(kFmt, tp, detail::femtoseconds(12345678901234), utc));
- EXPECT_EQ(
- "12:34:56.001234567890123",
- detail::format(kFmt, tp, detail::femtoseconds(1234567890123), utc));
- EXPECT_EQ(
- "12:34:56.000123456789012",
- detail::format(kFmt, tp, detail::femtoseconds(123456789012), utc));
+ EXPECT_EQ("12:34:56.001234567890123",
+ detail::format(kFmt, tp, detail::femtoseconds(1234567890123), utc));
+ EXPECT_EQ("12:34:56.000123456789012",
+ detail::format(kFmt, tp, detail::femtoseconds(123456789012), utc));
EXPECT_EQ("12:34:56.000000000000123",
detail::format(kFmt, tp, detail::femtoseconds(123), utc));
@@ -1251,9 +1252,9 @@ TEST(Parse, ExtendedSubecondsScan) {
const auto expected = chrono::system_clock::from_time_t(0) +
chrono::nanoseconds(micros * 1000 + ns);
for (int ps = 0; ps < 1000; ps += 250) {
- std::ostringstream oss;
+ std::ostringstream ps_oss;
oss << std::setfill('0') << std::setw(3) << ps;
- const std::string input = nanos + oss.str() + "999";
+ const std::string input = nanos + ps_oss.str() + "999";
EXPECT_TRUE(parse("%E*f", input, tz, &tp));
EXPECT_EQ(expected + chrono::nanoseconds(ps) / 1000, tp) << input;
}
@@ -1417,8 +1418,8 @@ TEST(Parse, MaxRange) {
parse(RFC3339_sec, "-292277022657-01-27T08:29:51+01:00", utc, &tp));
// tests max/min civil-second overflow
- EXPECT_FALSE(parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01",
- utc, &tp));
+ EXPECT_FALSE(
+ parse(RFC3339_sec, "9223372036854775807-12-31T23:59:59-00:01", utc, &tp));
EXPECT_FALSE(parse(RFC3339_sec, "-9223372036854775808-01-01T00:00:00+00:01",
utc, &tp));
@@ -1475,7 +1476,8 @@ TEST(FormatParse, RoundTrip) {
TEST(FormatParse, RoundTripDistantFuture) {
const time_zone utc = utc_time_zone();
- const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::max();
+ const time_point<absl::time_internal::cctz::seconds> in =
+ time_point<absl::time_internal::cctz::seconds>::max();
const std::string s = format(RFC3339_full, in, utc);
time_point<absl::time_internal::cctz::seconds> out;
EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
@@ -1484,7 +1486,8 @@ TEST(FormatParse, RoundTripDistantFuture) {
TEST(FormatParse, RoundTripDistantPast) {
const time_zone utc = utc_time_zone();
- const time_point<absl::time_internal::cctz::seconds> in = time_point<absl::time_internal::cctz::seconds>::min();
+ const time_point<absl::time_internal::cctz::seconds> in =
+ time_point<absl::time_internal::cctz::seconds>::min();
const std::string s = format(RFC3339_full, in, utc);
time_point<absl::time_internal::cctz::seconds> out;
EXPECT_TRUE(parse(RFC3339_full, s, utc, &out)) << s;
@@ -1493,5 +1496,5 @@ TEST(FormatParse, RoundTripDistantPast) {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_if.cc b/absl/time/internal/cctz/src/time_zone_if.cc
index 75d23da5..0319b2f9 100644
--- a/absl/time/internal/cctz/src/time_zone_if.cc
+++ b/absl/time/internal/cctz/src/time_zone_if.cc
@@ -13,11 +13,13 @@
// limitations under the License.
#include "time_zone_if.h"
+
+#include "absl/base/config.h"
#include "time_zone_info.h"
#include "time_zone_libc.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -39,5 +41,5 @@ TimeZoneIf::~TimeZoneIf() {}
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h
index 0081d990..32c0891c 100644
--- a/absl/time/internal/cctz/src/time_zone_if.h
+++ b/absl/time/internal/cctz/src/time_zone_if.h
@@ -20,11 +20,12 @@
#include <memory>
#include <string>
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -39,8 +40,7 @@ class TimeZoneIf {
virtual time_zone::absolute_lookup BreakTime(
const time_point<seconds>& tp) const = 0;
- virtual time_zone::civil_lookup MakeTime(
- const civil_second& cs) const = 0;
+ virtual time_zone::civil_lookup MakeTime(const civil_second& cs) const = 0;
virtual bool NextTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const = 0;
@@ -59,16 +59,18 @@ class TimeZoneIf {
// Unix clock are second aligned, but not that they share an epoch.
inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
return (tp - std::chrono::time_point_cast<seconds>(
- std::chrono::system_clock::from_time_t(0))).count();
+ std::chrono::system_clock::from_time_t(0)))
+ .count();
}
inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) {
return std::chrono::time_point_cast<seconds>(
- std::chrono::system_clock::from_time_t(0)) + seconds(t);
+ std::chrono::system_clock::from_time_t(0)) +
+ seconds(t);
}
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc
index 60911f0b..030ae0e1 100644
--- a/absl/time/internal/cctz/src/time_zone_impl.cc
+++ b/absl/time/internal/cctz/src/time_zone_impl.cc
@@ -14,15 +14,17 @@
#include "time_zone_impl.h"
+#include <deque>
#include <mutex>
#include <string>
#include <unordered_map>
#include <utility>
+#include "absl/base/config.h"
#include "time_zone_fixed.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -34,13 +36,16 @@ using TimeZoneImplByName =
TimeZoneImplByName* time_zone_map = nullptr;
// Mutual exclusion for time_zone_map.
-std::mutex time_zone_mutex;
+std::mutex& TimeZoneMutex() {
+ // This mutex is intentionally "leaked" to avoid the static deinitialization
+ // order fiasco (std::mutex's destructor is not trivial on many platforms).
+ static std::mutex* time_zone_mutex = new std::mutex;
+ return *time_zone_mutex;
+}
} // namespace
-time_zone time_zone::Impl::UTC() {
- return time_zone(UTCImpl());
-}
+time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); }
bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
const time_zone::Impl* const utc_impl = UTCImpl();
@@ -55,7 +60,7 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
// Then check, under a shared lock, whether the time zone has already
// been loaded. This is the common path. TODO: Move to shared_mutex.
{
- std::lock_guard<std::mutex> lock(time_zone_mutex);
+ std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map != nullptr) {
TimeZoneImplByName::const_iterator itr = time_zone_map->find(name);
if (itr != time_zone_map->end()) {
@@ -66,7 +71,7 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
}
// Now check again, under an exclusive lock.
- std::lock_guard<std::mutex> lock(time_zone_mutex);
+ std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName;
const Impl*& impl = (*time_zone_map)[name];
if (impl == nullptr) {
@@ -85,10 +90,16 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
}
void time_zone::Impl::ClearTimeZoneMapTestOnly() {
- std::lock_guard<std::mutex> lock(time_zone_mutex);
+ std::lock_guard<std::mutex> lock(TimeZoneMutex());
if (time_zone_map != nullptr) {
- // Existing time_zone::Impl* entries are in the wild, so we simply
- // leak them. Future requests will result in reloading the data.
+ // Existing time_zone::Impl* entries are in the wild, so we can't delete
+ // them. Instead, we move them to a private container, where they are
+ // logically unreachable but not "leaked". Future requests will result
+ // in reloading the data.
+ static auto* cleared = new std::deque<const time_zone::Impl*>;
+ for (const auto& element : *time_zone_map) {
+ cleared->push_back(element.second);
+ }
time_zone_map->clear();
}
}
@@ -106,5 +117,5 @@ const time_zone::Impl* time_zone::Impl::UTCImpl() {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_impl.h b/absl/time/internal/cctz/src/time_zone_impl.h
index 33d3f6bd..69806c10 100644
--- a/absl/time/internal/cctz/src/time_zone_impl.h
+++ b/absl/time/internal/cctz/src/time_zone_impl.h
@@ -18,13 +18,14 @@
#include <memory>
#include <string>
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#include "time_zone_if.h"
#include "time_zone_info.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -86,7 +87,7 @@ class time_zone::Impl {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc
index b45d82fa..f1697cdf 100644
--- a/absl/time/internal/cctz/src/time_zone_info.cc
+++ b/absl/time/internal/cctz/src/time_zone_info.cc
@@ -45,12 +45,13 @@
#include <sstream>
#include <string>
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "time_zone_fixed.h"
#include "time_zone_posix.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -66,8 +67,8 @@ const std::int_least32_t kDaysPerYear[2] = {365, 366};
// The day offsets of the beginning of each (1-based) month in non-leap and
// leap years respectively (e.g., 335 days before December in a leap year).
const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = {
- {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
- {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
+ {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
+ {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
};
// We reject leap-second encoded zoneinfo and so assume 60-second minutes.
@@ -78,8 +79,8 @@ const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay;
// Like kDaysPerYear[] but scaled up by a factor of kSecsPerDay.
const std::int_least32_t kSecsPerYear[2] = {
- 365 * kSecsPerDay,
- 366 * kSecsPerDay,
+ 365 * kSecsPerDay,
+ 366 * kSecsPerDay,
};
// Single-byte, unsigned numeric values are encoded directly.
@@ -173,8 +174,8 @@ inline time_zone::civil_lookup MakeRepeated(const Transition& tr,
}
inline civil_second YearShift(const civil_second& cs, year_t shift) {
- return civil_second(cs.year() + shift, cs.month(), cs.day(),
- cs.hour(), cs.minute(), cs.second());
+ return civil_second(cs.year() + shift, cs.month(), cs.day(), cs.hour(),
+ cs.minute(), cs.second());
}
} // namespace
@@ -217,7 +218,7 @@ bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
default_transition_type_ = 0;
abbreviations_ = FixedOffsetToAbbr(offset);
abbreviations_.append(1, '\0'); // add NUL
- future_spec_.clear(); // never needed for a fixed-offset zone
+ future_spec_.clear(); // never needed for a fixed-offset zone
extended_ = false;
tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
@@ -394,31 +395,24 @@ void TimeZoneInfo::ExtendTransitions(const std::string& name,
bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
// Read and validate the header.
tzhead tzh;
- if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh))
- return false;
+ if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
return false;
Header hdr;
- if (!hdr.Build(tzh))
- return false;
+ if (!hdr.Build(tzh)) return false;
std::size_t time_len = 4;
if (tzh.tzh_version[0] != '\0') {
// Skip the 4-byte data.
- if (zip->Skip(hdr.DataLength(time_len)) != 0)
- return false;
+ if (zip->Skip(hdr.DataLength(time_len)) != 0) return false;
// Read and validate the header for the 8-byte data.
- if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh))
- return false;
+ if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
return false;
- if (tzh.tzh_version[0] == '\0')
- return false;
- if (!hdr.Build(tzh))
- return false;
+ if (tzh.tzh_version[0] == '\0') return false;
+ if (!hdr.Build(tzh)) return false;
time_len = 8;
}
- if (hdr.typecnt == 0)
- return false;
+ if (hdr.typecnt == 0) return false;
if (hdr.leapcnt != 0) {
// This code assumes 60-second minutes so we do not want
// the leap-second encoded zoneinfo. We could reverse the
@@ -426,16 +420,13 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
// so currently we simply reject such data.
return false;
}
- if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt)
- return false;
- if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt)
- return false;
+ if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false;
+ if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false;
// Read the data into a local buffer.
std::size_t len = hdr.DataLength(time_len);
std::vector<char> tbuf(len);
- if (zip->Read(tbuf.data(), len) != len)
- return false;
+ if (zip->Read(tbuf.data(), len) != len) return false;
const char* bp = tbuf.data();
// Decode and validate the transitions.
@@ -453,10 +444,8 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
bool seen_type_0 = false;
for (std::size_t i = 0; i != hdr.timecnt; ++i) {
transitions_[i].type_index = Decode8(bp++);
- if (transitions_[i].type_index >= hdr.typecnt)
- return false;
- if (transitions_[i].type_index == 0)
- seen_type_0 = true;
+ if (transitions_[i].type_index >= hdr.typecnt) return false;
+ if (transitions_[i].type_index == 0) seen_type_0 = true;
}
// Decode and validate the transition types.
@@ -470,8 +459,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
bp += 4;
transition_types_[i].is_dst = (Decode8(bp++) != 0);
transition_types_[i].abbr_index = Decode8(bp++);
- if (transition_types_[i].abbr_index >= hdr.charcnt)
- return false;
+ if (transition_types_[i].abbr_index >= hdr.charcnt) return false;
}
// Determine the before-first-transition type.
@@ -480,13 +468,10 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
std::uint_fast8_t index = 0;
if (transition_types_[0].is_dst) {
index = transitions_[0].type_index;
- while (index != 0 && transition_types_[index].is_dst)
- --index;
+ while (index != 0 && transition_types_[index].is_dst) --index;
}
- while (index != hdr.typecnt && transition_types_[index].is_dst)
- ++index;
- if (index != hdr.typecnt)
- default_transition_type_ = index;
+ while (index != hdr.typecnt && transition_types_[index].is_dst) ++index;
+ if (index != hdr.typecnt) default_transition_type_ = index;
}
// Copy all the abbreviations.
@@ -510,11 +495,9 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
unsigned char ch; // all non-EOF results are positive
return (azip->Read(&ch, 1) == 1) ? ch : EOF;
};
- if (get_char(zip) != '\n')
- return false;
+ if (get_char(zip) != '\n') return false;
for (int c = get_char(zip); c != '\n'; c = get_char(zip)) {
- if (c == EOF)
- return false;
+ if (c == EOF) return false;
future_spec_.push_back(static_cast<char>(c));
}
}
@@ -625,18 +608,18 @@ class FileZoneInfoSource : public ZoneInfoSource {
: fp_(fp, fclose), len_(len) {}
private:
- std::unique_ptr<FILE, int(*)(FILE*)> fp_;
+ std::unique_ptr<FILE, int (*)(FILE*)> fp_;
std::size_t len_;
};
std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
const std::string& name) {
// Use of the "file:" prefix is intended for testing purposes only.
- if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5));
+ const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
// Map the time-zone name to a path name.
std::string path;
- if (name.empty() || name[0] != '/') {
+ if (pos == name.size() || name[pos] != '/') {
const char* tzdir = "/usr/share/zoneinfo";
char* tzdir_env = nullptr;
#if defined(_MSC_VER)
@@ -651,16 +634,16 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
free(tzdir_env);
#endif
}
- path += name;
+ path.append(name, pos, std::string::npos);
// Open the zoneinfo file.
FILE* fp = FOpen(path.c_str(), "rb");
if (fp == nullptr) return nullptr;
std::size_t length = 0;
if (fseek(fp, 0, SEEK_END) == 0) {
- long pos = ftell(fp);
- if (pos >= 0) {
- length = static_cast<std::size_t>(pos);
+ long offset = ftell(fp);
+ if (offset >= 0) {
+ length = static_cast<std::size_t>(offset);
}
rewind(fp);
}
@@ -681,7 +664,7 @@ class AndroidZoneInfoSource : public FileZoneInfoSource {
std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
const std::string& name) {
// Use of the "file:" prefix is intended for testing purposes only.
- if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5));
+ 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",
@@ -710,7 +693,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
const std::int_fast32_t length = Decode32(ebuf + 44);
if (start < 0 || length < 0) break;
ebuf[40] = '\0'; // ensure zone name is NUL terminated
- if (strcmp(name.c_str(), ebuf) == 0) {
+ if (strcmp(name.c_str() + pos, ebuf) == 0) {
if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
fp.release(), static_cast<std::size_t>(length), vers));
@@ -749,13 +732,13 @@ time_zone::absolute_lookup TimeZoneInfo::LocalTime(
// A civil time in "+offset" looks like (time+offset) in UTC.
// Note: We perform two additions in the civil_second domain to
// sidestep the chance of overflow in (unix_time + tt.utc_offset).
- return {(civil_second() + unix_time) + tt.utc_offset,
- tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]};
+ return {(civil_second() + unix_time) + tt.utc_offset, tt.utc_offset,
+ tt.is_dst, &abbreviations_[tt.abbr_index]};
}
// BreakTime() translation for a particular transition.
-time_zone::absolute_lookup TimeZoneInfo::LocalTime(
- std::int_fast64_t unix_time, const Transition& tr) const {
+time_zone::absolute_lookup TimeZoneInfo::LocalTime(std::int_fast64_t unix_time,
+ const Transition& tr) const {
const TransitionType& tt = transition_types_[tr.type_index];
// Note: (unix_time - tr.unix_time) will never overflow as we
// have ensured that there is always a "nearby" transition.
@@ -898,9 +881,7 @@ time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
}
-std::string TimeZoneInfo::Version() const {
- return version_;
-}
+std::string TimeZoneInfo::Version() const { return version_; }
std::string TimeZoneInfo::Description() const {
std::ostringstream oss;
@@ -922,8 +903,8 @@ bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
}
std::int_fast64_t unix_time = ToUnixSeconds(tp);
const Transition target = {unix_time, 0, civil_second(), civil_second()};
- const Transition* tr = std::upper_bound(begin, end, target,
- Transition::ByUnixTime());
+ const Transition* tr =
+ std::upper_bound(begin, end, target, Transition::ByUnixTime());
for (; tr != end; ++tr) { // skip no-op transitions
std::uint_fast8_t prev_type_index =
(tr == begin) ? default_transition_type_ : tr[-1].type_index;
@@ -957,8 +938,8 @@ bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
unix_time += 1; // ceils
}
const Transition target = {unix_time, 0, civil_second(), civil_second()};
- const Transition* tr = std::lower_bound(begin, end, target,
- Transition::ByUnixTime());
+ const Transition* tr =
+ std::lower_bound(begin, end, target, Transition::ByUnixTime());
for (; tr != begin; --tr) { // skip no-op transitions
std::uint_fast8_t prev_type_index =
(tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index;
@@ -973,5 +954,5 @@ bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_info.h b/absl/time/internal/cctz/src/time_zone_info.h
index c724fa8f..2a10c06c 100644
--- a/absl/time/internal/cctz/src/time_zone_info.h
+++ b/absl/time/internal/cctz/src/time_zone_info.h
@@ -21,6 +21,7 @@
#include <string>
#include <vector>
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
@@ -28,7 +29,7 @@
#include "tzfile.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -73,8 +74,7 @@ class TimeZoneInfo : public TimeZoneIf {
// TimeZoneIf implementations.
time_zone::absolute_lookup BreakTime(
const time_point<seconds>& tp) const override;
- time_zone::civil_lookup MakeTime(
- const civil_second& cs) const override;
+ time_zone::civil_lookup MakeTime(const civil_second& cs) const override;
bool NextTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const override;
bool PrevTransition(const time_point<seconds>& tp,
@@ -83,7 +83,7 @@ class TimeZoneInfo : public TimeZoneIf {
std::string Description() const override;
private:
- struct Header { // counts of:
+ struct Header { // counts of:
std::size_t timecnt; // transition times
std::size_t typecnt; // transition types
std::size_t charcnt; // zone abbreviation characters
@@ -115,7 +115,7 @@ class TimeZoneInfo : public TimeZoneIf {
std::vector<Transition> transitions_; // ordered by unix_time and civil_sec
std::vector<TransitionType> transition_types_; // distinct transition types
- std::uint_fast8_t default_transition_type_; // for before first transition
+ std::uint_fast8_t default_transition_type_; // for before first transition
std::string abbreviations_; // all the NUL-terminated abbreviations
std::string version_; // the tzdata version if available
@@ -132,7 +132,7 @@ class TimeZoneInfo : public TimeZoneIf {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc
index cfe7d55c..47cf84c6 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.cc
+++ b/absl/time/internal/cctz/src/time_zone_libc.cc
@@ -23,11 +23,12 @@
#include <limits>
#include <utility>
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -85,9 +86,7 @@ auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
}
#endif // tm_gmtoff
#if defined(tm_zone)
-auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) {
- return tm.tm_zone;
-}
+auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; }
#elif defined(__tm_zone)
auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
return tm.__tm_zone;
@@ -104,19 +103,19 @@ auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
#endif // tm_zone
#endif
-inline std::tm* gm_time(const std::time_t *timep, std::tm *result) {
+inline std::tm* gm_time(const std::time_t* timep, std::tm* result) {
#if defined(_WIN32) || defined(_WIN64)
- return gmtime_s(result, timep) ? nullptr : result;
+ return gmtime_s(result, timep) ? nullptr : result;
#else
- return gmtime_r(timep, result);
+ return gmtime_r(timep, result);
#endif
}
-inline std::tm* local_time(const std::time_t *timep, std::tm *result) {
+inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
#if defined(_WIN32) || defined(_WIN64)
- return localtime_s(result, timep) ? nullptr : result;
+ return localtime_s(result, timep) ? nullptr : result;
#else
- return localtime_r(timep, result);
+ return localtime_r(timep, result);
#endif
}
@@ -209,8 +208,8 @@ time_zone::absolute_lookup TimeZoneLibC::BreakTime(
}
const year_t year = tmp->tm_year + year_t{1900};
- al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday,
- tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour,
+ tmp->tm_min, tmp->tm_sec);
al.offset = static_cast<int>(tm_gmtoff(*tmp));
al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz
al.is_dst = tmp->tm_isdst > 0;
@@ -305,5 +304,5 @@ std::string TimeZoneLibC::Description() const {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_libc.h b/absl/time/internal/cctz/src/time_zone_libc.h
index 4b1ccb06..1da9039a 100644
--- a/absl/time/internal/cctz/src/time_zone_libc.h
+++ b/absl/time/internal/cctz/src/time_zone_libc.h
@@ -17,10 +17,11 @@
#include <string>
+#include "absl/base/config.h"
#include "time_zone_if.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -34,8 +35,7 @@ class TimeZoneLibC : public TimeZoneIf {
// TimeZoneIf implementations.
time_zone::absolute_lookup BreakTime(
const time_point<seconds>& tp) const override;
- time_zone::civil_lookup MakeTime(
- const civil_second& cs) const override;
+ time_zone::civil_lookup MakeTime(const civil_second& cs) const override;
bool NextTransition(const time_point<seconds>& tp,
time_zone::civil_transition* trans) const override;
bool PrevTransition(const time_point<seconds>& tp,
@@ -49,7 +49,7 @@ class TimeZoneLibC : public TimeZoneIf {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc
index 772a9375..efdea64b 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "absl/base/config.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
#if defined(__ANDROID__)
@@ -23,6 +24,7 @@
#if defined(__APPLE__)
#include <CoreFoundation/CFTimeZone.h>
+
#include <vector>
#endif
@@ -34,7 +36,7 @@
#include "time_zone_impl.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -67,9 +69,7 @@ int __system_property_get(const char* name, char* value) {
} // namespace
#endif
-std::string time_zone::name() const {
- return effective_impl().Name();
-}
+std::string time_zone::name() const { return effective_impl().Name(); }
time_zone::absolute_lookup time_zone::lookup(
const time_point<seconds>& tp) const {
@@ -90,9 +90,7 @@ bool time_zone::prev_transition(const time_point<seconds>& tp,
return effective_impl().PrevTransition(tp, trans);
}
-std::string time_zone::version() const {
- return effective_impl().Version();
-}
+std::string time_zone::version() const { return effective_impl().Version(); }
std::string time_zone::description() const {
return effective_impl().Description();
@@ -185,5 +183,5 @@ time_zone local_time_zone() {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
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 1bef7708..99137a08 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "absl/time/internal/cctz/include/cctz/time_zone.h"
-
#include <chrono>
#include <cstddef>
#include <cstdlib>
@@ -23,614 +21,614 @@
#include <thread>
#include <vector>
-#include "absl/time/internal/cctz/include/cctz/civil_time.h"
#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/time/internal/cctz/include/cctz/civil_time.h"
+#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace chrono = std::chrono;
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
namespace {
// A list of known time-zone names.
-const char* const kTimeZoneNames[] = {
- "Africa/Abidjan",
- "Africa/Accra",
- "Africa/Addis_Ababa",
- "Africa/Algiers",
- "Africa/Asmara",
- "Africa/Asmera",
- "Africa/Bamako",
- "Africa/Bangui",
- "Africa/Banjul",
- "Africa/Bissau",
- "Africa/Blantyre",
- "Africa/Brazzaville",
- "Africa/Bujumbura",
- "Africa/Cairo",
- "Africa/Casablanca",
- "Africa/Ceuta",
- "Africa/Conakry",
- "Africa/Dakar",
- "Africa/Dar_es_Salaam",
- "Africa/Djibouti",
- "Africa/Douala",
- "Africa/El_Aaiun",
- "Africa/Freetown",
- "Africa/Gaborone",
- "Africa/Harare",
- "Africa/Johannesburg",
- "Africa/Juba",
- "Africa/Kampala",
- "Africa/Khartoum",
- "Africa/Kigali",
- "Africa/Kinshasa",
- "Africa/Lagos",
- "Africa/Libreville",
- "Africa/Lome",
- "Africa/Luanda",
- "Africa/Lubumbashi",
- "Africa/Lusaka",
- "Africa/Malabo",
- "Africa/Maputo",
- "Africa/Maseru",
- "Africa/Mbabane",
- "Africa/Mogadishu",
- "Africa/Monrovia",
- "Africa/Nairobi",
- "Africa/Ndjamena",
- "Africa/Niamey",
- "Africa/Nouakchott",
- "Africa/Ouagadougou",
- "Africa/Porto-Novo",
- "Africa/Sao_Tome",
- "Africa/Timbuktu",
- "Africa/Tripoli",
- "Africa/Tunis",
- "Africa/Windhoek",
- "America/Adak",
- "America/Anchorage",
- "America/Anguilla",
- "America/Antigua",
- "America/Araguaina",
- "America/Argentina/Buenos_Aires",
- "America/Argentina/Catamarca",
- "America/Argentina/ComodRivadavia",
- "America/Argentina/Cordoba",
- "America/Argentina/Jujuy",
- "America/Argentina/La_Rioja",
- "America/Argentina/Mendoza",
- "America/Argentina/Rio_Gallegos",
- "America/Argentina/Salta",
- "America/Argentina/San_Juan",
- "America/Argentina/San_Luis",
- "America/Argentina/Tucuman",
- "America/Argentina/Ushuaia",
- "America/Aruba",
- "America/Asuncion",
- "America/Atikokan",
- "America/Atka",
- "America/Bahia",
- "America/Bahia_Banderas",
- "America/Barbados",
- "America/Belem",
- "America/Belize",
- "America/Blanc-Sablon",
- "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/Coral_Harbour",
- "America/Cordoba",
- "America/Costa_Rica",
- "America/Creston",
- "America/Cuiaba",
- "America/Curacao",
- "America/Danmarkshavn",
- "America/Dawson",
- "America/Dawson_Creek",
- "America/Denver",
- "America/Detroit",
- "America/Dominica",
- "America/Edmonton",
- "America/Eirunepe",
- "America/El_Salvador",
- "America/Ensenada",
- "America/Fort_Nelson",
- "America/Fort_Wayne",
- "America/Fortaleza",
- "America/Glace_Bay",
- "America/Godthab",
- "America/Goose_Bay",
- "America/Grand_Turk",
- "America/Grenada",
- "America/Guadeloupe",
- "America/Guatemala",
- "America/Guayaquil",
- "America/Guyana",
- "America/Halifax",
- "America/Havana",
- "America/Hermosillo",
- "America/Indiana/Indianapolis",
- "America/Indiana/Knox",
- "America/Indiana/Marengo",
- "America/Indiana/Petersburg",
- "America/Indiana/Tell_City",
- "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",
- "America/Manaus",
- "America/Marigot",
- "America/Martinique",
- "America/Matamoros",
- "America/Mazatlan",
- "America/Mendoza",
- "America/Menominee",
- "America/Merida",
- "America/Metlakatla",
- "America/Mexico_City",
- "America/Miquelon",
- "America/Moncton",
- "America/Monterrey",
- "America/Montevideo",
- "America/Montreal",
- "America/Montserrat",
- "America/Nassau",
- "America/New_York",
- "America/Nipigon",
- "America/Nome",
- "America/Noronha",
- "America/North_Dakota/Beulah",
- "America/North_Dakota/Center",
- "America/North_Dakota/New_Salem",
- "America/Ojinaga",
- "America/Panama",
- "America/Pangnirtung",
- "America/Paramaribo",
- "America/Phoenix",
- "America/Port-au-Prince",
- "America/Port_of_Spain",
- "America/Porto_Acre",
- "America/Porto_Velho",
- "America/Puerto_Rico",
- "America/Punta_Arenas",
- "America/Rainy_River",
- "America/Rankin_Inlet",
- "America/Recife",
- "America/Regina",
- "America/Resolute",
- "America/Rio_Branco",
- "America/Rosario",
- "America/Santa_Isabel",
- "America/Santarem",
- "America/Santiago",
- "America/Santo_Domingo",
- "America/Sao_Paulo",
- "America/Scoresbysund",
- "America/Shiprock",
- "America/Sitka",
- "America/St_Barthelemy",
- "America/St_Johns",
- "America/St_Kitts",
- "America/St_Lucia",
- "America/St_Thomas",
- "America/St_Vincent",
- "America/Swift_Current",
- "America/Tegucigalpa",
- "America/Thule",
- "America/Thunder_Bay",
- "America/Tijuana",
- "America/Toronto",
- "America/Tortola",
- "America/Vancouver",
- "America/Virgin",
- "America/Whitehorse",
- "America/Winnipeg",
- "America/Yakutat",
- "America/Yellowknife",
- "Antarctica/Casey",
- "Antarctica/Davis",
- "Antarctica/DumontDUrville",
- "Antarctica/Macquarie",
- "Antarctica/Mawson",
- "Antarctica/McMurdo",
- "Antarctica/Palmer",
- "Antarctica/Rothera",
- "Antarctica/South_Pole",
- "Antarctica/Syowa",
- "Antarctica/Troll",
- "Antarctica/Vostok",
- "Arctic/Longyearbyen",
- "Asia/Aden",
- "Asia/Almaty",
- "Asia/Amman",
- "Asia/Anadyr",
- "Asia/Aqtau",
- "Asia/Aqtobe",
- "Asia/Ashgabat",
- "Asia/Ashkhabad",
- "Asia/Atyrau",
- "Asia/Baghdad",
- "Asia/Bahrain",
- "Asia/Baku",
- "Asia/Bangkok",
- "Asia/Barnaul",
- "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",
- "Asia/Dubai",
- "Asia/Dushanbe",
- "Asia/Famagusta",
- "Asia/Gaza",
- "Asia/Harbin",
- "Asia/Hebron",
- "Asia/Ho_Chi_Minh",
- "Asia/Hong_Kong",
- "Asia/Hovd",
- "Asia/Irkutsk",
- "Asia/Istanbul",
- "Asia/Jakarta",
- "Asia/Jayapura",
- "Asia/Jerusalem",
- "Asia/Kabul",
- "Asia/Kamchatka",
- "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",
- "Asia/Manila",
- "Asia/Muscat",
- "Asia/Nicosia",
- "Asia/Novokuznetsk",
- "Asia/Novosibirsk",
- "Asia/Omsk",
- "Asia/Oral",
- "Asia/Phnom_Penh",
- "Asia/Pontianak",
- "Asia/Pyongyang",
- "Asia/Qatar",
- "Asia/Qostanay",
- "Asia/Qyzylorda",
- "Asia/Rangoon",
- "Asia/Riyadh",
- "Asia/Saigon",
- "Asia/Sakhalin",
- "Asia/Samarkand",
- "Asia/Seoul",
- "Asia/Shanghai",
- "Asia/Singapore",
- "Asia/Srednekolymsk",
- "Asia/Taipei",
- "Asia/Tashkent",
- "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",
- "Asia/Vladivostok",
- "Asia/Yakutsk",
- "Asia/Yangon",
- "Asia/Yekaterinburg",
- "Asia/Yerevan",
- "Atlantic/Azores",
- "Atlantic/Bermuda",
- "Atlantic/Canary",
- "Atlantic/Cape_Verde",
- "Atlantic/Faeroe",
- "Atlantic/Faroe",
- "Atlantic/Jan_Mayen",
- "Atlantic/Madeira",
- "Atlantic/Reykjavik",
- "Atlantic/South_Georgia",
- "Atlantic/St_Helena",
- "Atlantic/Stanley",
- "Australia/ACT",
- "Australia/Adelaide",
- "Australia/Brisbane",
- "Australia/Broken_Hill",
- "Australia/Canberra",
- "Australia/Currie",
- "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",
- "Etc/GMT+10",
- "Etc/GMT+11",
- "Etc/GMT+12",
- "Etc/GMT+2",
- "Etc/GMT+3",
- "Etc/GMT+4",
- "Etc/GMT+5",
- "Etc/GMT+6",
- "Etc/GMT+7",
- "Etc/GMT+8",
- "Etc/GMT+9",
- "Etc/GMT-0",
- "Etc/GMT-1",
- "Etc/GMT-10",
- "Etc/GMT-11",
- "Etc/GMT-12",
- "Etc/GMT-13",
- "Etc/GMT-14",
- "Etc/GMT-2",
- "Etc/GMT-3",
- "Etc/GMT-4",
- "Etc/GMT-5",
- "Etc/GMT-6",
- "Etc/GMT-7",
- "Etc/GMT-8",
- "Etc/GMT-9",
- "Etc/GMT0",
- "Etc/Greenwich",
- "Etc/UCT",
- "Etc/UTC",
- "Etc/Universal",
- "Etc/Zulu",
- "Europe/Amsterdam",
- "Europe/Andorra",
- "Europe/Astrakhan",
- "Europe/Athens",
- "Europe/Belfast",
- "Europe/Belgrade",
- "Europe/Berlin",
- "Europe/Bratislava",
- "Europe/Brussels",
- "Europe/Bucharest",
- "Europe/Budapest",
- "Europe/Busingen",
- "Europe/Chisinau",
- "Europe/Copenhagen",
- "Europe/Dublin",
- "Europe/Gibraltar",
- "Europe/Guernsey",
- "Europe/Helsinki",
- "Europe/Isle_of_Man",
- "Europe/Istanbul",
- "Europe/Jersey",
- "Europe/Kaliningrad",
- "Europe/Kiev",
- "Europe/Kirov",
- "Europe/Lisbon",
- "Europe/Ljubljana",
- "Europe/London",
- "Europe/Luxembourg",
- "Europe/Madrid",
- "Europe/Malta",
- "Europe/Mariehamn",
- "Europe/Minsk",
- "Europe/Monaco",
- "Europe/Moscow",
- "Europe/Nicosia",
- "Europe/Oslo",
- "Europe/Paris",
- "Europe/Podgorica",
- "Europe/Prague",
- "Europe/Riga",
- "Europe/Rome",
- "Europe/Samara",
- "Europe/San_Marino",
- "Europe/Sarajevo",
- "Europe/Saratov",
- "Europe/Simferopol",
- "Europe/Skopje",
- "Europe/Sofia",
- "Europe/Stockholm",
- "Europe/Tallinn",
- "Europe/Tirane",
- "Europe/Tiraspol",
- "Europe/Ulyanovsk",
- "Europe/Uzhgorod",
- "Europe/Vaduz",
- "Europe/Vatican",
- "Europe/Vienna",
- "Europe/Vilnius",
- "Europe/Volgograd",
- "Europe/Warsaw",
- "Europe/Zagreb",
- "Europe/Zaporozhye",
- "Europe/Zurich",
- "GB",
- "GB-Eire",
- "GMT",
- "GMT+0",
- "GMT-0",
- "GMT0",
- "Greenwich",
- "HST",
- "Hongkong",
- "Iceland",
- "Indian/Antananarivo",
- "Indian/Chagos",
- "Indian/Christmas",
- "Indian/Cocos",
- "Indian/Comoro",
- "Indian/Kerguelen",
- "Indian/Mahe",
- "Indian/Maldives",
- "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",
- "Pacific/Chatham",
- "Pacific/Chuuk",
- "Pacific/Easter",
- "Pacific/Efate",
- "Pacific/Enderbury",
- "Pacific/Fakaofo",
- "Pacific/Fiji",
- "Pacific/Funafuti",
- "Pacific/Galapagos",
- "Pacific/Gambier",
- "Pacific/Guadalcanal",
- "Pacific/Guam",
- "Pacific/Honolulu",
- "Pacific/Johnston",
- "Pacific/Kiritimati",
- "Pacific/Kosrae",
- "Pacific/Kwajalein",
- "Pacific/Majuro",
- "Pacific/Marquesas",
- "Pacific/Midway",
- "Pacific/Nauru",
- "Pacific/Niue",
- "Pacific/Norfolk",
- "Pacific/Noumea",
- "Pacific/Pago_Pago",
- "Pacific/Palau",
- "Pacific/Pitcairn",
- "Pacific/Pohnpei",
- "Pacific/Ponape",
- "Pacific/Port_Moresby",
- "Pacific/Rarotonga",
- "Pacific/Saipan",
- "Pacific/Samoa",
- "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
-};
+const char* const kTimeZoneNames[] = {"Africa/Abidjan",
+ "Africa/Accra",
+ "Africa/Addis_Ababa",
+ "Africa/Algiers",
+ "Africa/Asmara",
+ "Africa/Asmera",
+ "Africa/Bamako",
+ "Africa/Bangui",
+ "Africa/Banjul",
+ "Africa/Bissau",
+ "Africa/Blantyre",
+ "Africa/Brazzaville",
+ "Africa/Bujumbura",
+ "Africa/Cairo",
+ "Africa/Casablanca",
+ "Africa/Ceuta",
+ "Africa/Conakry",
+ "Africa/Dakar",
+ "Africa/Dar_es_Salaam",
+ "Africa/Djibouti",
+ "Africa/Douala",
+ "Africa/El_Aaiun",
+ "Africa/Freetown",
+ "Africa/Gaborone",
+ "Africa/Harare",
+ "Africa/Johannesburg",
+ "Africa/Juba",
+ "Africa/Kampala",
+ "Africa/Khartoum",
+ "Africa/Kigali",
+ "Africa/Kinshasa",
+ "Africa/Lagos",
+ "Africa/Libreville",
+ "Africa/Lome",
+ "Africa/Luanda",
+ "Africa/Lubumbashi",
+ "Africa/Lusaka",
+ "Africa/Malabo",
+ "Africa/Maputo",
+ "Africa/Maseru",
+ "Africa/Mbabane",
+ "Africa/Mogadishu",
+ "Africa/Monrovia",
+ "Africa/Nairobi",
+ "Africa/Ndjamena",
+ "Africa/Niamey",
+ "Africa/Nouakchott",
+ "Africa/Ouagadougou",
+ "Africa/Porto-Novo",
+ "Africa/Sao_Tome",
+ "Africa/Timbuktu",
+ "Africa/Tripoli",
+ "Africa/Tunis",
+ "Africa/Windhoek",
+ "America/Adak",
+ "America/Anchorage",
+ "America/Anguilla",
+ "America/Antigua",
+ "America/Araguaina",
+ "America/Argentina/Buenos_Aires",
+ "America/Argentina/Catamarca",
+ "America/Argentina/ComodRivadavia",
+ "America/Argentina/Cordoba",
+ "America/Argentina/Jujuy",
+ "America/Argentina/La_Rioja",
+ "America/Argentina/Mendoza",
+ "America/Argentina/Rio_Gallegos",
+ "America/Argentina/Salta",
+ "America/Argentina/San_Juan",
+ "America/Argentina/San_Luis",
+ "America/Argentina/Tucuman",
+ "America/Argentina/Ushuaia",
+ "America/Aruba",
+ "America/Asuncion",
+ "America/Atikokan",
+ "America/Atka",
+ "America/Bahia",
+ "America/Bahia_Banderas",
+ "America/Barbados",
+ "America/Belem",
+ "America/Belize",
+ "America/Blanc-Sablon",
+ "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/Coral_Harbour",
+ "America/Cordoba",
+ "America/Costa_Rica",
+ "America/Creston",
+ "America/Cuiaba",
+ "America/Curacao",
+ "America/Danmarkshavn",
+ "America/Dawson",
+ "America/Dawson_Creek",
+ "America/Denver",
+ "America/Detroit",
+ "America/Dominica",
+ "America/Edmonton",
+ "America/Eirunepe",
+ "America/El_Salvador",
+ "America/Ensenada",
+ "America/Fort_Nelson",
+ "America/Fort_Wayne",
+ "America/Fortaleza",
+ "America/Glace_Bay",
+ "America/Godthab",
+ "America/Goose_Bay",
+ "America/Grand_Turk",
+ "America/Grenada",
+ "America/Guadeloupe",
+ "America/Guatemala",
+ "America/Guayaquil",
+ "America/Guyana",
+ "America/Halifax",
+ "America/Havana",
+ "America/Hermosillo",
+ "America/Indiana/Indianapolis",
+ "America/Indiana/Knox",
+ "America/Indiana/Marengo",
+ "America/Indiana/Petersburg",
+ "America/Indiana/Tell_City",
+ "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",
+ "America/Manaus",
+ "America/Marigot",
+ "America/Martinique",
+ "America/Matamoros",
+ "America/Mazatlan",
+ "America/Mendoza",
+ "America/Menominee",
+ "America/Merida",
+ "America/Metlakatla",
+ "America/Mexico_City",
+ "America/Miquelon",
+ "America/Moncton",
+ "America/Monterrey",
+ "America/Montevideo",
+ "America/Montreal",
+ "America/Montserrat",
+ "America/Nassau",
+ "America/New_York",
+ "America/Nipigon",
+ "America/Nome",
+ "America/Noronha",
+ "America/North_Dakota/Beulah",
+ "America/North_Dakota/Center",
+ "America/North_Dakota/New_Salem",
+ "America/Ojinaga",
+ "America/Panama",
+ "America/Pangnirtung",
+ "America/Paramaribo",
+ "America/Phoenix",
+ "America/Port-au-Prince",
+ "America/Port_of_Spain",
+ "America/Porto_Acre",
+ "America/Porto_Velho",
+ "America/Puerto_Rico",
+ "America/Punta_Arenas",
+ "America/Rainy_River",
+ "America/Rankin_Inlet",
+ "America/Recife",
+ "America/Regina",
+ "America/Resolute",
+ "America/Rio_Branco",
+ "America/Rosario",
+ "America/Santa_Isabel",
+ "America/Santarem",
+ "America/Santiago",
+ "America/Santo_Domingo",
+ "America/Sao_Paulo",
+ "America/Scoresbysund",
+ "America/Shiprock",
+ "America/Sitka",
+ "America/St_Barthelemy",
+ "America/St_Johns",
+ "America/St_Kitts",
+ "America/St_Lucia",
+ "America/St_Thomas",
+ "America/St_Vincent",
+ "America/Swift_Current",
+ "America/Tegucigalpa",
+ "America/Thule",
+ "America/Thunder_Bay",
+ "America/Tijuana",
+ "America/Toronto",
+ "America/Tortola",
+ "America/Vancouver",
+ "America/Virgin",
+ "America/Whitehorse",
+ "America/Winnipeg",
+ "America/Yakutat",
+ "America/Yellowknife",
+ "Antarctica/Casey",
+ "Antarctica/Davis",
+ "Antarctica/DumontDUrville",
+ "Antarctica/Macquarie",
+ "Antarctica/Mawson",
+ "Antarctica/McMurdo",
+ "Antarctica/Palmer",
+ "Antarctica/Rothera",
+ "Antarctica/South_Pole",
+ "Antarctica/Syowa",
+ "Antarctica/Troll",
+ "Antarctica/Vostok",
+ "Arctic/Longyearbyen",
+ "Asia/Aden",
+ "Asia/Almaty",
+ "Asia/Amman",
+ "Asia/Anadyr",
+ "Asia/Aqtau",
+ "Asia/Aqtobe",
+ "Asia/Ashgabat",
+ "Asia/Ashkhabad",
+ "Asia/Atyrau",
+ "Asia/Baghdad",
+ "Asia/Bahrain",
+ "Asia/Baku",
+ "Asia/Bangkok",
+ "Asia/Barnaul",
+ "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",
+ "Asia/Dubai",
+ "Asia/Dushanbe",
+ "Asia/Famagusta",
+ "Asia/Gaza",
+ "Asia/Harbin",
+ "Asia/Hebron",
+ "Asia/Ho_Chi_Minh",
+ "Asia/Hong_Kong",
+ "Asia/Hovd",
+ "Asia/Irkutsk",
+ "Asia/Istanbul",
+ "Asia/Jakarta",
+ "Asia/Jayapura",
+ "Asia/Jerusalem",
+ "Asia/Kabul",
+ "Asia/Kamchatka",
+ "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",
+ "Asia/Manila",
+ "Asia/Muscat",
+ "Asia/Nicosia",
+ "Asia/Novokuznetsk",
+ "Asia/Novosibirsk",
+ "Asia/Omsk",
+ "Asia/Oral",
+ "Asia/Phnom_Penh",
+ "Asia/Pontianak",
+ "Asia/Pyongyang",
+ "Asia/Qatar",
+ "Asia/Qostanay",
+ "Asia/Qyzylorda",
+ "Asia/Rangoon",
+ "Asia/Riyadh",
+ "Asia/Saigon",
+ "Asia/Sakhalin",
+ "Asia/Samarkand",
+ "Asia/Seoul",
+ "Asia/Shanghai",
+ "Asia/Singapore",
+ "Asia/Srednekolymsk",
+ "Asia/Taipei",
+ "Asia/Tashkent",
+ "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",
+ "Asia/Vladivostok",
+ "Asia/Yakutsk",
+ "Asia/Yangon",
+ "Asia/Yekaterinburg",
+ "Asia/Yerevan",
+ "Atlantic/Azores",
+ "Atlantic/Bermuda",
+ "Atlantic/Canary",
+ "Atlantic/Cape_Verde",
+ "Atlantic/Faeroe",
+ "Atlantic/Faroe",
+ "Atlantic/Jan_Mayen",
+ "Atlantic/Madeira",
+ "Atlantic/Reykjavik",
+ "Atlantic/South_Georgia",
+ "Atlantic/St_Helena",
+ "Atlantic/Stanley",
+ "Australia/ACT",
+ "Australia/Adelaide",
+ "Australia/Brisbane",
+ "Australia/Broken_Hill",
+ "Australia/Canberra",
+ "Australia/Currie",
+ "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",
+ "Etc/GMT+10",
+ "Etc/GMT+11",
+ "Etc/GMT+12",
+ "Etc/GMT+2",
+ "Etc/GMT+3",
+ "Etc/GMT+4",
+ "Etc/GMT+5",
+ "Etc/GMT+6",
+ "Etc/GMT+7",
+ "Etc/GMT+8",
+ "Etc/GMT+9",
+ "Etc/GMT-0",
+ "Etc/GMT-1",
+ "Etc/GMT-10",
+ "Etc/GMT-11",
+ "Etc/GMT-12",
+ "Etc/GMT-13",
+ "Etc/GMT-14",
+ "Etc/GMT-2",
+ "Etc/GMT-3",
+ "Etc/GMT-4",
+ "Etc/GMT-5",
+ "Etc/GMT-6",
+ "Etc/GMT-7",
+ "Etc/GMT-8",
+ "Etc/GMT-9",
+ "Etc/GMT0",
+ "Etc/Greenwich",
+ "Etc/UCT",
+ "Etc/UTC",
+ "Etc/Universal",
+ "Etc/Zulu",
+ "Europe/Amsterdam",
+ "Europe/Andorra",
+ "Europe/Astrakhan",
+ "Europe/Athens",
+ "Europe/Belfast",
+ "Europe/Belgrade",
+ "Europe/Berlin",
+ "Europe/Bratislava",
+ "Europe/Brussels",
+ "Europe/Bucharest",
+ "Europe/Budapest",
+ "Europe/Busingen",
+ "Europe/Chisinau",
+ "Europe/Copenhagen",
+ "Europe/Dublin",
+ "Europe/Gibraltar",
+ "Europe/Guernsey",
+ "Europe/Helsinki",
+ "Europe/Isle_of_Man",
+ "Europe/Istanbul",
+ "Europe/Jersey",
+ "Europe/Kaliningrad",
+ "Europe/Kiev",
+ "Europe/Kirov",
+ "Europe/Lisbon",
+ "Europe/Ljubljana",
+ "Europe/London",
+ "Europe/Luxembourg",
+ "Europe/Madrid",
+ "Europe/Malta",
+ "Europe/Mariehamn",
+ "Europe/Minsk",
+ "Europe/Monaco",
+ "Europe/Moscow",
+ "Europe/Nicosia",
+ "Europe/Oslo",
+ "Europe/Paris",
+ "Europe/Podgorica",
+ "Europe/Prague",
+ "Europe/Riga",
+ "Europe/Rome",
+ "Europe/Samara",
+ "Europe/San_Marino",
+ "Europe/Sarajevo",
+ "Europe/Saratov",
+ "Europe/Simferopol",
+ "Europe/Skopje",
+ "Europe/Sofia",
+ "Europe/Stockholm",
+ "Europe/Tallinn",
+ "Europe/Tirane",
+ "Europe/Tiraspol",
+ "Europe/Ulyanovsk",
+ "Europe/Uzhgorod",
+ "Europe/Vaduz",
+ "Europe/Vatican",
+ "Europe/Vienna",
+ "Europe/Vilnius",
+ "Europe/Volgograd",
+ "Europe/Warsaw",
+ "Europe/Zagreb",
+ "Europe/Zaporozhye",
+ "Europe/Zurich",
+ "GB",
+ "GB-Eire",
+ "GMT",
+ "GMT+0",
+ "GMT-0",
+ "GMT0",
+ "Greenwich",
+ "HST",
+ "Hongkong",
+ "Iceland",
+ "Indian/Antananarivo",
+ "Indian/Chagos",
+ "Indian/Christmas",
+ "Indian/Cocos",
+ "Indian/Comoro",
+ "Indian/Kerguelen",
+ "Indian/Mahe",
+ "Indian/Maldives",
+ "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",
+ "Pacific/Chatham",
+ "Pacific/Chuuk",
+ "Pacific/Easter",
+ "Pacific/Efate",
+ "Pacific/Enderbury",
+ "Pacific/Fakaofo",
+ "Pacific/Fiji",
+ "Pacific/Funafuti",
+ "Pacific/Galapagos",
+ "Pacific/Gambier",
+ "Pacific/Guadalcanal",
+ "Pacific/Guam",
+ "Pacific/Honolulu",
+ "Pacific/Johnston",
+ "Pacific/Kiritimati",
+ "Pacific/Kosrae",
+ "Pacific/Kwajalein",
+ "Pacific/Majuro",
+ "Pacific/Marquesas",
+ "Pacific/Midway",
+ "Pacific/Nauru",
+ "Pacific/Niue",
+ "Pacific/Norfolk",
+ "Pacific/Noumea",
+ "Pacific/Pago_Pago",
+ "Pacific/Palau",
+ "Pacific/Pitcairn",
+ "Pacific/Pohnpei",
+ "Pacific/Ponape",
+ "Pacific/Port_Moresby",
+ "Pacific/Rarotonga",
+ "Pacific/Saipan",
+ "Pacific/Samoa",
+ "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).
time_zone LoadZone(const std::string& name) {
@@ -725,7 +723,8 @@ TEST(TimeZone, NamedTimeZones) {
EXPECT_EQ("America/New_York", nyc.name());
const time_zone syd = LoadZone("Australia/Sydney");
EXPECT_EQ("Australia/Sydney", syd.name());
- const time_zone fixed0 = fixed_time_zone(absl::time_internal::cctz::seconds::zero());
+ const time_zone fixed0 =
+ fixed_time_zone(absl::time_internal::cctz::seconds::zero());
EXPECT_EQ("UTC", fixed0.name());
const time_zone fixed_pos = fixed_time_zone(
chrono::hours(3) + chrono::minutes(25) + chrono::seconds(45));
@@ -768,7 +767,8 @@ TEST(TimeZone, Equality) {
EXPECT_EQ(implicit_utc, explicit_utc);
EXPECT_EQ(implicit_utc.name(), explicit_utc.name());
- const time_zone fixed_zero = fixed_time_zone(absl::time_internal::cctz::seconds::zero());
+ const time_zone fixed_zero =
+ fixed_time_zone(absl::time_internal::cctz::seconds::zero());
EXPECT_EQ(fixed_zero, LoadZone(fixed_zero.name()));
EXPECT_EQ(fixed_zero, explicit_utc);
@@ -807,8 +807,8 @@ TEST(TimeZone, Equality) {
TEST(StdChronoTimePoint, TimeTAlignment) {
// Ensures that the Unix epoch and the system clock epoch are an integral
// number of seconds apart. This simplifies conversions to/from time_t.
- auto diff = chrono::system_clock::time_point() -
- chrono::system_clock::from_time_t(0);
+ auto diff =
+ chrono::system_clock::time_point() - chrono::system_clock::from_time_t(0);
EXPECT_EQ(chrono::system_clock::time_point::duration::zero(),
diff % chrono::seconds(1));
}
@@ -817,20 +817,20 @@ TEST(BreakTime, TimePointResolution) {
const time_zone utc = utc_time_zone();
const auto t0 = chrono::system_clock::from_time_t(0);
- ExpectTime(chrono::time_point_cast<chrono::nanoseconds>(t0), utc,
- 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(chrono::time_point_cast<chrono::microseconds>(t0), utc,
- 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(chrono::time_point_cast<chrono::milliseconds>(t0), utc,
- 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(chrono::time_point_cast<chrono::seconds>(t0), utc,
- 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0), utc,
- 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(chrono::time_point_cast<chrono::minutes>(t0), utc,
- 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
- ExpectTime(chrono::time_point_cast<chrono::hours>(t0), utc,
- 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
+ ExpectTime(chrono::time_point_cast<chrono::nanoseconds>(t0), utc, 1970, 1, 1,
+ 0, 0, 0, 0, false, "UTC");
+ ExpectTime(chrono::time_point_cast<chrono::microseconds>(t0), utc, 1970, 1, 1,
+ 0, 0, 0, 0, false, "UTC");
+ ExpectTime(chrono::time_point_cast<chrono::milliseconds>(t0), utc, 1970, 1, 1,
+ 0, 0, 0, 0, false, "UTC");
+ ExpectTime(chrono::time_point_cast<chrono::seconds>(t0), utc, 1970, 1, 1, 0,
+ 0, 0, 0, false, "UTC");
+ ExpectTime(chrono::time_point_cast<absl::time_internal::cctz::seconds>(t0),
+ utc, 1970, 1, 1, 0, 0, 0, 0, false, "UTC");
+ ExpectTime(chrono::time_point_cast<chrono::minutes>(t0), utc, 1970, 1, 1, 0,
+ 0, 0, 0, false, "UTC");
+ ExpectTime(chrono::time_point_cast<chrono::hours>(t0), utc, 1970, 1, 1, 0, 0,
+ 0, 0, false, "UTC");
}
TEST(BreakTime, LocalTimeInUTC) {
@@ -912,9 +912,8 @@ TEST(MakeTime, TimePointResolution) {
chrono::time_point_cast<chrono::minutes>(
convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
EXPECT_EQ("04:00", format("%M:%E*S", tp_m, utc));
- const time_point<chrono::hours> tp_h =
- chrono::time_point_cast<chrono::hours>(
- convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
+ const time_point<chrono::hours> tp_h = chrono::time_point_cast<chrono::hours>(
+ convert(civil_second(2015, 1, 2, 3, 4, 5), utc));
EXPECT_EQ("00:00", format("%M:%E*S", tp_h, utc));
}
@@ -933,7 +932,7 @@ TEST(MakeTime, Normalization) {
// NOTE: Run this with -ftrapv to detect overflow problems.
TEST(MakeTime, SysSecondsLimits) {
- const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez";
+ const char RFC3339[] = "%Y-%m-%dT%H:%M:%S%Ez";
const time_zone utc = utc_time_zone();
const time_zone east = fixed_time_zone(chrono::hours(14));
const time_zone west = fixed_time_zone(-chrono::hours(14));
@@ -1029,17 +1028,17 @@ TEST(MakeTime, LocalTimeLibC) {
ASSERT_EQ(0, setenv("TZ", *np, 1)); // change what "localtime" means
const auto zi = local_time_zone();
const auto lc = LoadZone("libc:localtime");
- time_zone::civil_transition trans;
+ time_zone::civil_transition transition;
for (auto tp = zi.lookup(civil_second()).trans;
- zi.next_transition(tp, &trans);
- tp = zi.lookup(trans.to).trans) {
- const auto fcl = zi.lookup(trans.from);
- const auto tcl = zi.lookup(trans.to);
+ zi.next_transition(tp, &transition);
+ tp = zi.lookup(transition.to).trans) {
+ const auto fcl = zi.lookup(transition.from);
+ const auto tcl = zi.lookup(transition.to);
civil_second cs; // compare cs in zi and lc
if (fcl.kind == time_zone::civil_lookup::UNIQUE) {
if (tcl.kind == time_zone::civil_lookup::UNIQUE) {
// Both unique; must be an is_dst or abbr change.
- ASSERT_EQ(trans.from, trans.to);
+ ASSERT_EQ(transition.from, transition.to);
const auto trans = fcl.trans;
const auto tal = zi.lookup(trans);
const auto tprev = trans - absl::time_internal::cctz::seconds(1);
@@ -1050,11 +1049,11 @@ TEST(MakeTime, LocalTimeLibC) {
continue;
}
ASSERT_EQ(time_zone::civil_lookup::REPEATED, tcl.kind);
- cs = trans.to;
+ cs = transition.to;
} else {
ASSERT_EQ(time_zone::civil_lookup::UNIQUE, tcl.kind);
ASSERT_EQ(time_zone::civil_lookup::SKIPPED, fcl.kind);
- cs = trans.from;
+ cs = transition.from;
}
if (cs.year() > 2037) break; // limit test time (and to 32-bit time_t)
const auto cl_zi = zi.lookup(cs);
@@ -1434,5 +1433,5 @@ TEST(TimeZoneEdgeCase, UTC5DigitYear) {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_posix.cc b/absl/time/internal/cctz/src/time_zone_posix.cc
index d1f8ecbe..5cdd09e8 100644
--- a/absl/time/internal/cctz/src/time_zone_posix.cc
+++ b/absl/time/internal/cctz/src/time_zone_posix.cc
@@ -19,8 +19,10 @@
#include <limits>
#include <string>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -153,5 +155,5 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) {
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/src/time_zone_posix.h b/absl/time/internal/cctz/src/time_zone_posix.h
index 07e3427a..0cf29055 100644
--- a/absl/time/internal/cctz/src/time_zone_posix.h
+++ b/absl/time/internal/cctz/src/time_zone_posix.h
@@ -55,8 +55,10 @@
#include <cstdint>
#include <string>
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -124,7 +126,7 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res);
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h
index 51b1f1f3..1ed55e0f 100644
--- a/absl/time/internal/cctz/src/tzfile.h
+++ b/absl/time/internal/cctz/src/tzfile.h
@@ -22,36 +22,35 @@
*/
#ifndef TZDIR
-#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
-#endif /* !defined TZDIR */
+#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
+#endif /* !defined TZDIR */
#ifndef TZDEFAULT
-#define TZDEFAULT "/etc/localtime"
+#define TZDEFAULT "/etc/localtime"
#endif /* !defined TZDEFAULT */
#ifndef TZDEFRULES
-#define TZDEFRULES "posixrules"
+#define TZDEFRULES "posixrules"
#endif /* !defined TZDEFRULES */
-
/* See Internet RFC 8536 for more details about the following format. */
/*
** Each file begins with. . .
*/
-#define TZ_MAGIC "TZif"
+#define TZ_MAGIC "TZif"
struct tzhead {
- char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
- char tzh_reserved[15]; /* reserved; must be zero */
- char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
- char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
- char tzh_leapcnt[4]; /* coded number of leap seconds */
- char tzh_timecnt[4]; /* coded number of transition times */
- char tzh_typecnt[4]; /* coded number of local time types */
- char tzh_charcnt[4]; /* coded number of abbr. chars */
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
+ char tzh_reserved[15]; /* reserved; must be zero */
+ char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
};
/*
@@ -103,21 +102,21 @@ struct tzhead {
*/
#ifndef TZ_MAX_TIMES
-#define TZ_MAX_TIMES 2000
+#define TZ_MAX_TIMES 2000
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
-#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
-#endif /* !defined TZ_MAX_TYPES */
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
-#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
- /* (limited by what unsigned chars can hold) */
-#endif /* !defined TZ_MAX_CHARS */
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS
-#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
-#endif /* !defined TZ_MAX_LEAPS */
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
#endif /* !defined TZFILE_H */
diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc
index b64950cb..98ea1612 100644
--- a/absl/time/internal/cctz/src/zone_info_source.cc
+++ b/absl/time/internal/cctz/src/zone_info_source.cc
@@ -14,8 +14,10 @@
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
+#include "absl/base/config.h"
+
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz {
@@ -25,11 +27,11 @@ std::string ZoneInfoSource::Version() const { return std::string(); }
} // namespace cctz
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz_extension {
@@ -39,8 +41,9 @@ namespace {
// defers to the fallback factory.
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory(
const std::string& name,
- const std::function<std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>(
- const std::string& name)>& fallback_factory) {
+ const std::function<
+ std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>(
+ const std::string& name)>& fallback_factory) {
return fallback_factory(name);
}
@@ -52,21 +55,52 @@ std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory(
#if !defined(__has_attribute)
#define __has_attribute(x) 0
#endif
-#if __has_attribute(weak) || defined(__GNUC__)
-ZoneInfoSourceFactory zone_info_source_factory
- __attribute__((weak)) = DefaultFactory;
-#elif defined(_MSC_VER) && !defined(_LIBCPP_VERSION)
+// MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the
+// Windows linker cannot handle that. Nor does the MinGW compiler know how to
+// pass "#pragma comment(linker, ...)" to the Windows linker.
+#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__)
+ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) =
+ DefaultFactory;
+#elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION)
extern ZoneInfoSourceFactory zone_info_source_factory;
extern ZoneInfoSourceFactory default_factory;
ZoneInfoSourceFactory default_factory = DefaultFactory;
#if defined(_M_IX86)
-#pragma comment( \
- linker, \
- "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA=?default_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA")
+#pragma comment( \
+ linker, \
+ "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
+ "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
+ "@@ZA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
+ "@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
+ "@@ZA")
#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64)
-#pragma comment( \
- linker, \
- "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA=?default_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA")
+#pragma comment( \
+ linker, \
+ "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
+ "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
+ "@@ZEA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
+ "@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
+ "@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
+ "@@ZEA")
#else
#error Unsupported MSVC platform
#endif // _M_<PLATFORM>
@@ -77,5 +111,5 @@ ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory;
} // namespace cctz_extension
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version
index 8d4a43ba..db18f831 100644
--- a/absl/time/internal/cctz/testdata/version
+++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@
-2019b
+2019c
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit b/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit
index 5e022605..e104faa4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Detroit
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton b/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton
index 3fa05798..cd78a6f8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Edmonton
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City
index 4ce95c15..7bbb653c 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Indiana/Tell_City
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville b/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville
index f4c4cf96..177836e4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Kentucky/Louisville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville b/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville
index f4c4cf96..177836e4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Louisville
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver b/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver
index 0f9f8328..bb60cbce 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver
+++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Vancouver
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong
index 378a37f9..23d0375f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul
index 10d4b21b..508446bb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul
index 73182cf6..96199e73 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain
index 3fa05798..cd78a6f8 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Mountain
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific
index 0f9f8328..bb60cbce 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Pacific
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels
index 9613c981..40d7124e 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul
index 10d4b21b..508446bb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad
index f774ffdb..cc99beab 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna
index 696f359d..3582bb15 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Hongkong b/absl/time/internal/cctz/testdata/zoneinfo/Hongkong
index 378a37f9..23d0375f 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Hongkong
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Hongkong
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
index 6bf667d4..d39bf536 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk
index 1f6d610e..53c1aad4 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/ROK b/absl/time/internal/cctz/testdata/zoneinfo/ROK
index 73182cf6..96199e73 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/ROK
+++ b/absl/time/internal/cctz/testdata/zoneinfo/ROK
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Turkey b/absl/time/internal/cctz/testdata/zoneinfo/Turkey
index 10d4b21b..508446bb 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/Turkey
+++ b/absl/time/internal/cctz/testdata/zoneinfo/Turkey
Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan b/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan
index 5e022605..e104faa4 100644
--- a/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan
+++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Michigan
Binary files differ
diff --git a/absl/time/internal/get_current_time_chrono.inc b/absl/time/internal/get_current_time_chrono.inc
index d294b3a6..5eeb6406 100644
--- a/absl/time/internal/get_current_time_chrono.inc
+++ b/absl/time/internal/get_current_time_chrono.inc
@@ -16,7 +16,7 @@
#include <cstdint>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
static int64_t GetCurrentTimeNanosFromSystem() {
@@ -27,5 +27,5 @@ static int64_t GetCurrentTimeNanosFromSystem() {
}
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/get_current_time_posix.inc b/absl/time/internal/get_current_time_posix.inc
index 0ce2a9a9..42072000 100644
--- a/absl/time/internal/get_current_time_posix.inc
+++ b/absl/time/internal/get_current_time_posix.inc
@@ -7,7 +7,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
static int64_t GetCurrentTimeNanosFromSystem() {
@@ -20,5 +20,5 @@ static int64_t GetCurrentTimeNanosFromSystem() {
}
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc
index 2bc449fe..9bffe121 100644
--- a/absl/time/internal/test_util.cc
+++ b/absl/time/internal/test_util.cc
@@ -24,7 +24,7 @@
namespace cctz = absl::time_internal::cctz;
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
TimeZone LoadTimeZone(const std::string& name) {
@@ -34,11 +34,11 @@ TimeZone LoadTimeZone(const std::string& name) {
}
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
namespace cctz_extension {
namespace {
@@ -119,9 +119,12 @@ std::unique_ptr<cctz::ZoneInfoSource> TestFactory(
} // namespace
+#if !defined(__MINGW32__)
+// MinGW does not support the weak symbol extension mechanism.
ZoneInfoSourceFactory zone_info_source_factory = TestFactory;
+#endif
} // namespace cctz_extension
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/internal/test_util.h b/absl/time/internal/test_util.h
index 1a320cab..5c4bf1f6 100644
--- a/absl/time/internal/test_util.h
+++ b/absl/time/internal/test_util.h
@@ -20,14 +20,14 @@
#include "absl/time/time.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace time_internal {
// Loads the named timezone, but dies on any failure.
absl::TimeZone LoadTimeZone(const std::string& name);
} // namespace time_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_INTERNAL_TEST_UTIL_H_
diff --git a/absl/time/time.cc b/absl/time/time.cc
index 2c7da3ad..6bb36cb3 100644
--- a/absl/time/time.cc
+++ b/absl/time/time.cc
@@ -47,7 +47,7 @@
namespace cctz = absl::time_internal::cctz;
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
@@ -431,9 +431,17 @@ absl::TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour,
}
absl::Time FromTM(const struct tm& tm, absl::TimeZone tz) {
- const CivilSecond cs(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
- const auto ti = tz.At(cs);
+ civil_year_t tm_year = tm.tm_year;
+ // Avoids years that are too extreme for CivilSecond to normalize.
+ if (tm_year > 300000000000ll) return InfiniteFuture();
+ if (tm_year < -300000000000ll) return InfinitePast();
+ int tm_mon = tm.tm_mon;
+ if (tm_mon == std::numeric_limits<int>::max()) {
+ tm_mon -= 12;
+ tm_year += 1;
+ }
+ const auto ti = tz.At(CivilSecond(tm_year + 1900, tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec));
return tm.tm_isdst == 0 ? ti.post : ti.pre;
}
@@ -487,5 +495,5 @@ struct tm ToTM(absl::Time t, absl::TimeZone tz) {
return tm;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/time/time.h b/absl/time/time.h
index 1f6500e8..33a4a630 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -83,12 +83,13 @@ struct timeval;
#include <type_traits>
#include <utility>
+#include "absl/base/macros.h"
#include "absl/strings/string_view.h"
#include "absl/time/civil_time.h"
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
class Duration; // Defined below
class Time; // Defined below
@@ -381,11 +382,11 @@ constexpr Duration InfiniteDuration();
// of the unit indicated by the factory function's name. The number must be
// representable as int64_t.
//
-// Note: no "Days()" factory function exists because "a day" is ambiguous.
+// NOTE: no "Days()" factory function exists because "a day" is ambiguous.
// Civil days are not always 24 hours long, and a 24-hour duration often does
// not correspond with a civil day. If a 24-hour duration is needed, use
-// `absl::Hours(24)`. (If you actually want a civil day, use absl::CivilDay
-// from civil_time.h.)
+// `absl::Hours(24)`. If you actually want a civil day, use absl::CivilDay
+// from civil_time.h.
//
// Example:
//
@@ -422,7 +423,9 @@ Duration Milliseconds(T n) {
template <typename T, time_internal::EnableIfFloat<T> = 0>
Duration Seconds(T n) {
if (n >= 0) { // Note: `NaN >= 0` is false.
- if (n >= (std::numeric_limits<int64_t>::max)()) return InfiniteDuration();
+ if (n >= static_cast<T>((std::numeric_limits<int64_t>::max)())) {
+ return InfiniteDuration();
+ }
return time_internal::MakePosDoubleDuration(n);
} else {
if (std::isnan(n))
@@ -546,7 +549,11 @@ bool ParseDuration(const std::string& dur_string, Duration* d);
// Support for flag values of type Duration. Duration flags must be specified
// in a format that is valid input for absl::ParseDuration().
+bool AbslParseFlag(absl::string_view text, Duration* dst, std::string* error);
+std::string AbslUnparseFlag(Duration d);
+ABSL_DEPRECATED("Use AbslParseFlag() instead.")
bool ParseFlag(const std::string& text, Duration* dst, std::string* error);
+ABSL_DEPRECATED("Use AbslUnparseFlag() instead.")
std::string UnparseFlag(Duration d);
// Time
@@ -816,7 +823,11 @@ std::chrono::system_clock::time_point ToChronoTime(Time);
// Additionally, if you'd like to specify a time as a count of
// seconds/milliseconds/etc from the Unix epoch, use an absl::Duration flag
// and add that duration to absl::UnixEpoch() to get an absl::Time.
+bool AbslParseFlag(absl::string_view text, Time* t, std::string* error);
+std::string AbslUnparseFlag(Time t);
+ABSL_DEPRECATED("Use AbslParseFlag() instead.")
bool ParseFlag(const std::string& text, Time* t, std::string* error);
+ABSL_DEPRECATED("Use AbslUnparseFlag() instead.")
std::string UnparseFlag(Time t);
// TimeZone
@@ -1104,7 +1115,7 @@ inline Time FromCivil(CivilSecond ct, TimeZone tz) {
// An `absl::TimeConversion` represents the conversion of year, month, day,
// hour, minute, and second values (i.e., a civil time), in a particular
// `absl::TimeZone`, to a time instant (an absolute time), as returned by
-// `absl::ConvertDateTime()`. Lecacy version of `absl::TimeZone::TimeInfo`.
+// `absl::ConvertDateTime()`. Legacy version of `absl::TimeZone::TimeInfo`.
//
// Deprecated. Use `absl::TimeZone::TimeInfo`.
struct
@@ -1192,15 +1203,18 @@ struct tm ToTM(Time t, TimeZone tz);
// time with UTC offset. Also note the use of "%Y": RFC3339 mandates that
// years have exactly four digits, but we allow them to take their natural
// width.
-extern const char RFC3339_full[]; // %Y-%m-%dT%H:%M:%E*S%Ez
-extern const char RFC3339_sec[]; // %Y-%m-%dT%H:%M:%S%Ez
+ABSL_DLL extern const char
+ RFC3339_full[]; // %Y-%m-%dT%H:%M:%E*S%Ez
+ABSL_DLL extern const char RFC3339_sec[]; // %Y-%m-%dT%H:%M:%S%Ez
// RFC1123_full
// RFC1123_no_wday
//
// FormatTime()/ParseTime() format specifiers for RFC1123 date/time strings.
-extern const char RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z
-extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z
+ABSL_DLL extern const char
+ RFC1123_full[]; // %a, %d %b %E4Y %H:%M:%S %z
+ABSL_DLL extern const char
+ RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z
// FormatTime()
//
@@ -1402,8 +1416,7 @@ constexpr Duration FromInt64(int64_t v, std::ratio<3600>) {
// IsValidRep64<T>(0) is true if the expression `int64_t{std::declval<T>()}` is
// valid. That is, if a T can be assigned to an int64_t without narrowing.
template <typename T>
-constexpr auto IsValidRep64(int)
- -> decltype(int64_t{std::declval<T>()}, bool()) {
+constexpr auto IsValidRep64(int) -> decltype(int64_t{std::declval<T>()} == 0) {
return true;
}
template <typename T>
@@ -1565,7 +1578,7 @@ constexpr Time FromTimeT(time_t t) {
return time_internal::FromUnixDuration(Seconds(t));
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TIME_TIME_H_
diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc
index 37af39d9..6f89672c 100644
--- a/absl/time/time_test.cc
+++ b/absl/time/time_test.cc
@@ -27,6 +27,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/numeric/int128.h"
#include "absl/time/clock.h"
#include "absl/time/internal/test_util.h"
@@ -575,6 +576,50 @@ TEST(Time, ToChronoTime) {
absl::ToChronoTime(absl::UnixEpoch() - tick));
}
+// Check that absl::int128 works as a std::chrono::duration representation.
+TEST(Time, Chrono128) {
+ // Define a std::chrono::time_point type whose time[sic]_since_epoch() is
+ // a signed 128-bit count of attoseconds. This has a range and resolution
+ // (currently) beyond those of absl::Time, and undoubtedly also beyond those
+ // of std::chrono::system_clock::time_point.
+ //
+ // Note: The to/from-chrono support should probably be updated to handle
+ // such wide representations.
+ using Timestamp =
+ std::chrono::time_point<std::chrono::system_clock,
+ std::chrono::duration<absl::int128, std::atto>>;
+
+ // Expect that we can round-trip the std::chrono::system_clock::time_point
+ // extremes through both absl::Time and Timestamp, and that Timestamp can
+ // handle the (current) absl::Time extremes.
+ //
+ // Note: We should use std::chrono::floor() instead of time_point_cast(),
+ // but floor() is only available since c++17.
+ for (const auto tp : {std::chrono::system_clock::time_point::min(),
+ std::chrono::system_clock::time_point::max()}) {
+ EXPECT_EQ(tp, absl::ToChronoTime(absl::FromChrono(tp)));
+ EXPECT_EQ(tp, std::chrono::time_point_cast<
+ std::chrono::system_clock::time_point::duration>(
+ std::chrono::time_point_cast<Timestamp::duration>(tp)));
+ }
+ Timestamp::duration::rep v = std::numeric_limits<int64_t>::min();
+ v *= Timestamp::duration::period::den;
+ auto ts = Timestamp(Timestamp::duration(v));
+ ts += std::chrono::duration<int64_t, std::atto>(0);
+ EXPECT_EQ(std::numeric_limits<int64_t>::min(),
+ ts.time_since_epoch().count() / Timestamp::duration::period::den);
+ EXPECT_EQ(0,
+ ts.time_since_epoch().count() % Timestamp::duration::period::den);
+ v = std::numeric_limits<int64_t>::max();
+ v *= Timestamp::duration::period::den;
+ ts = Timestamp(Timestamp::duration(v));
+ ts += std::chrono::duration<int64_t, std::atto>(999999999750000000);
+ EXPECT_EQ(std::numeric_limits<int64_t>::max(),
+ ts.time_since_epoch().count() / Timestamp::duration::period::den);
+ EXPECT_EQ(999999999750000000,
+ ts.time_since_epoch().count() % Timestamp::duration::period::den);
+}
+
TEST(Time, TimeZoneAt) {
const absl::TimeZone nyc =
absl::time_internal::LoadTimeZone("America/New_York");
@@ -795,6 +840,30 @@ TEST(Time, FromTM) {
tm.tm_isdst = 1;
t = FromTM(tm, nyc);
EXPECT_EQ("2014-03-09T03:30:42-04:00", absl::FormatTime(t, nyc)); // DST
+
+ // Adjusts tm to refer to a time with a year larger than 2147483647.
+ tm.tm_year = 2147483647 - 1900 + 1;
+ tm.tm_mon = 6 - 1;
+ tm.tm_mday = 28;
+ tm.tm_hour = 1;
+ tm.tm_min = 2;
+ tm.tm_sec = 3;
+ tm.tm_isdst = -1;
+ t = FromTM(tm, absl::UTCTimeZone());
+ EXPECT_EQ("2147483648-06-28T01:02:03+00:00",
+ absl::FormatTime(t, absl::UTCTimeZone()));
+
+ // Adjusts tm to refer to a time with a very large month.
+ tm.tm_year = 2019 - 1900;
+ tm.tm_mon = 2147483647;
+ tm.tm_mday = 28;
+ tm.tm_hour = 1;
+ tm.tm_min = 2;
+ tm.tm_sec = 3;
+ tm.tm_isdst = -1;
+ t = FromTM(tm, absl::UTCTimeZone());
+ EXPECT_EQ("178958989-08-28T01:02:03+00:00",
+ absl::FormatTime(t, absl::UTCTimeZone()));
}
TEST(Time, TMRoundTrip) {
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel
index 66ecb044..f2ea9f39 100644
--- a/absl/types/BUILD.bazel
+++ b/absl/types/BUILD.bazel
@@ -14,12 +14,11 @@
# limitations under the License.
#
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
"ABSL_DEFAULT_LINKOPTS",
- "ABSL_EXCEPTIONS_FLAG",
- "ABSL_EXCEPTIONS_FLAG_LINKOPTS",
"ABSL_TEST_COPTS",
)
@@ -58,12 +57,12 @@ cc_library(
"bad_any_cast.cc",
"bad_any_cast.h",
],
- copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"],
deps = [
- "//absl/base",
"//absl/base:config",
+ "//absl/base:raw_logging_internal",
],
)
@@ -73,31 +72,13 @@ cc_test(
srcs = [
"any_test.cc",
],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
- deps = [
- ":any",
- "//absl/base",
- "//absl/base:config",
- "//absl/base:exception_testing",
- "//absl/container:test_instance_tracker",
- "@com_google_googletest//:gtest_main",
- ],
-)
-
-cc_test(
- name = "any_test_noexceptions",
- size = "small",
- srcs = [
- "any_test.cc",
- ],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":any",
- "//absl/base",
"//absl/base:config",
"//absl/base:exception_testing",
+ "//absl/base:raw_logging_internal",
"//absl/container:test_instance_tracker",
"@com_google_googletest//:gtest_main",
],
@@ -106,10 +87,11 @@ cc_test(
cc_test(
name = "any_exception_safety_test",
srcs = ["any_exception_safety_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":any",
+ "//absl/base:config",
"//absl/base:exception_safety_testing",
"@com_google_googletest//:gtest_main",
],
@@ -137,25 +119,6 @@ cc_test(
name = "span_test",
size = "small",
srcs = ["span_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
- deps = [
- ":span",
- "//absl/base:config",
- "//absl/base:core_headers",
- "//absl/base:exception_testing",
- "//absl/container:fixed_array",
- "//absl/container:inlined_vector",
- "//absl/hash:hash_testing",
- "//absl/strings",
- "@com_google_googletest//:gtest_main",
- ],
-)
-
-cc_test(
- name = "span_test_noexceptions",
- size = "small",
- srcs = ["span_test.cc"],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
@@ -192,11 +155,11 @@ cc_library(
name = "bad_optional_access",
srcs = ["bad_optional_access.cc"],
hdrs = ["bad_optional_access.h"],
- copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- "//absl/base",
"//absl/base:config",
+ "//absl/base:raw_logging_internal",
],
)
@@ -204,11 +167,11 @@ cc_library(
name = "bad_variant_access",
srcs = ["bad_variant_access.cc"],
hdrs = ["bad_variant_access.h"],
- copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_DEFAULT_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
- "//absl/base",
"//absl/base:config",
+ "//absl/base:raw_logging_internal",
],
)
@@ -218,12 +181,12 @@ cc_test(
srcs = [
"optional_test.cc",
],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":optional",
- "//absl/base",
"//absl/base:config",
+ "//absl/base:raw_logging_internal",
"//absl/meta:type_traits",
"//absl/strings",
"@com_google_googletest//:gtest_main",
@@ -235,16 +198,51 @@ cc_test(
srcs = [
"optional_exception_safety_test.cc",
],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":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",
+ ],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ "//absl/debugging:demangle_internal",
+ "//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",
+ ],
+)
+
+cc_library(
name = "variant",
srcs = ["internal/variant.h"],
hdrs = ["variant.h"],
@@ -264,8 +262,8 @@ cc_test(
name = "variant_test",
size = "small",
srcs = ["variant_test.cc"],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":variant",
"//absl/base:config",
@@ -298,8 +296,8 @@ cc_test(
srcs = [
"variant_exception_safety_test.cc",
],
- copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
- linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS,
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":variant",
"//absl/base:config",
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt
index 4ce685da..c7c88250 100644
--- a/absl/types/CMakeLists.txt
+++ b/absl/types/CMakeLists.txt
@@ -50,12 +50,9 @@ absl_cc_library(
"bad_any_cast.cc"
COPTS
${ABSL_DEFAULT_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
- absl::base
absl::config
+ absl::raw_logging_internal
)
absl_cc_test(
@@ -65,14 +62,11 @@ absl_cc_test(
"any_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::any
- absl::base
absl::config
absl::exception_testing
+ absl::raw_logging_internal
absl::test_instance_tracker
gmock_main
)
@@ -86,9 +80,9 @@ absl_cc_test(
${ABSL_TEST_COPTS}
DEPS
absl::any
- absl::base
absl::config
absl::exception_testing
+ absl::raw_logging_internal
absl::test_instance_tracker
gmock_main
)
@@ -100,11 +94,9 @@ absl_cc_test(
"any_exception_safety_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::any
+ absl::config
absl::exception_safety_testing
gmock_main
)
@@ -133,9 +125,6 @@ absl_cc_test(
"span_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::span
absl::base
@@ -198,12 +187,9 @@ absl_cc_library(
"bad_optional_access.cc"
COPTS
${ABSL_DEFAULT_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
- absl::base
absl::config
+ absl::raw_logging_internal
PUBLIC
)
@@ -216,12 +202,9 @@ absl_cc_library(
"bad_variant_access.cc"
COPTS
${ABSL_DEFAULT_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
- absl::base
absl::config
+ absl::raw_logging_internal
PUBLIC
)
@@ -232,15 +215,12 @@ absl_cc_test(
"optional_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::optional
- absl::base
absl::config
- absl::type_traits
+ absl::raw_logging_internal
absl::strings
+ absl::type_traits
gmock_main
)
@@ -251,12 +231,56 @@ absl_cc_test(
"optional_exception_safety_test.cc"
COPTS
${ABSL_TEST_COPTS}
+ DEPS
+ absl::optional
+ absl::config
+ absl::exception_safety_testing
+ gmock_main
+)
+
+absl_cc_library(
+ NAME
+ conformance_testing
+ HDRS
+ "internal/conformance_aliases.h"
+ "internal/conformance_archetype.h"
+ "internal/conformance_profile.h"
+ COPTS
+ ${ABSL_DEFAULT_COPTS}
+ DEPS
+ absl::debugging
+ absl::type_traits
+ absl::strings
+ absl::utility
+ gmock_main
+ PUBLIC
+)
+
+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::optional
- absl::exception_safety_testing
+ absl::conformance_testing
+ absl::type_traits
+ 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
gmock_main
)
@@ -286,9 +310,6 @@ absl_cc_test(
"variant_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::variant
absl::config
@@ -335,9 +356,6 @@ absl_cc_test(
"variant_exception_safety_test.cc"
COPTS
${ABSL_TEST_COPTS}
- ${ABSL_EXCEPTIONS_FLAG}
- LINKOPTS
- ${ABSL_EXCEPTIONS_FLAG_LINKOPTS}
DEPS
absl::variant
absl::config
diff --git a/absl/types/any.h b/absl/types/any.h
index 7cae9dd0..16bda79c 100644
--- a/absl/types/any.h
+++ b/absl/types/any.h
@@ -56,20 +56,20 @@
#include "absl/base/config.h"
#include "absl/utility/utility.h"
-#ifdef ABSL_HAVE_STD_ANY
+#ifdef ABSL_USES_STD_ANY
#include <any> // IWYU pragma: export
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
using std::any;
using std::any_cast;
using std::bad_any_cast;
using std::make_any;
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#else // ABSL_HAVE_STD_ANY
+#else // ABSL_USES_STD_ANY
#include <algorithm>
#include <cstddef>
@@ -93,7 +93,7 @@ using std::make_any;
#endif // !defined(__GNUC__) || defined(__GXX_RTTI)
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace any_internal {
@@ -185,7 +185,7 @@ ValueType* any_cast(any* operand) noexcept;
// object, when containing a value, must contain a value type; storing a
// reference type is neither desired nor supported.
//
-// An `absl::any` can only store a type that is copy-constructable; move-only
+// An `absl::any` can only store a type that is copy-constructible; move-only
// types are not allowed within an `any` object.
//
// Example:
@@ -193,7 +193,7 @@ ValueType* any_cast(any* operand) noexcept;
// auto a = absl::any(65); // Literal, copyable
// auto b = absl::any(std::vector<int>()); // Default-initialized, copyable
// std::unique_ptr<Foo> my_foo;
-// auto c = absl::any(std::move(my_foo)); // Error, not copy-constructable
+// auto c = absl::any(std::move(my_foo)); // Error, not copy-constructible
//
// Note that `absl::any` makes use of decayed types (`absl::decay_t` in this
// context) to remove const-volatile qualifiers (known as "cv qualifiers"),
@@ -518,26 +518,30 @@ ValueType any_cast(any&& operand) {
// Description at the declaration site (top of file).
template <typename T>
const T* any_cast(const any* operand) noexcept {
- return operand && operand->GetObjTypeId() == any::IdForType<T>()
+ using U =
+ typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+ return operand && operand->GetObjTypeId() == any::IdForType<U>()
? std::addressof(
- static_cast<const any::Obj<T>*>(operand->obj_.get())->value)
+ static_cast<const any::Obj<U>*>(operand->obj_.get())->value)
: nullptr;
}
// Description at the declaration site (top of file).
template <typename T>
T* any_cast(any* operand) noexcept {
- return operand && operand->GetObjTypeId() == any::IdForType<T>()
+ using U =
+ typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+ return operand && operand->GetObjTypeId() == any::IdForType<U>()
? std::addressof(
- static_cast<any::Obj<T>*>(operand->obj_.get())->value)
+ static_cast<any::Obj<U>*>(operand->obj_.get())->value)
: nullptr;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#undef ABSL_ANY_DETAIL_HAS_RTTI
-#endif // ABSL_HAVE_STD_ANY
+#endif // ABSL_USES_STD_ANY
#endif // ABSL_TYPES_ANY_H_
diff --git a/absl/types/any_exception_safety_test.cc b/absl/types/any_exception_safety_test.cc
index 5d7d8a5c..31c11401 100644
--- a/absl/types/any_exception_safety_test.cc
+++ b/absl/types/any_exception_safety_test.cc
@@ -14,6 +14,12 @@
#include "absl/types/any.h"
+#include "absl/base/config.h"
+
+// This test is a no-op when absl::any is an alias for std::any and when
+// exceptions are not enabled.
+#if !defined(ABSL_USES_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS)
+
#include <typeinfo>
#include <vector>
@@ -136,8 +142,6 @@ TEST(AnyExceptionSafety, Assignment) {
EXPECT_TRUE(strong_empty_any_tester.Test(move));
}
-// libstdc++ std::any fails this test
-#if !defined(ABSL_HAVE_STD_ANY)
TEST(AnyExceptionSafety, Emplace) {
auto initial_val =
absl::any{absl::in_place_type_t<Thrower>(), 1, testing::nothrow_ctor};
@@ -163,6 +167,7 @@ TEST(AnyExceptionSafety, Emplace) {
EXPECT_TRUE(empty_tester.Test(emp_thrower));
EXPECT_TRUE(empty_tester.Test(emp_throwervec));
}
-#endif // ABSL_HAVE_STD_ANY
} // namespace
+
+#endif // #if !defined(ABSL_USES_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS)
diff --git a/absl/types/any_test.cc b/absl/types/any_test.cc
index 87104721..70e4ba22 100644
--- a/absl/types/any_test.cc
+++ b/absl/types/any_test.cc
@@ -14,6 +14,9 @@
#include "absl/types/any.h"
+// This test is a no-op when absl::any is an alias for std::any.
+#if !defined(ABSL_USES_STD_ANY)
+
#include <initializer_list>
#include <type_traits>
#include <utility>
@@ -639,7 +642,7 @@ TEST(AnyTest, ConversionConstructionCausesOneCopy) {
// Tests for Exception Behavior //
//////////////////////////////////
-#if defined(ABSL_HAVE_STD_ANY)
+#if defined(ABSL_USES_STD_ANY)
// If using a std `any` implementation, we can't check for a specific message.
#define ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(...) \
@@ -653,7 +656,7 @@ TEST(AnyTest, ConversionConstructionCausesOneCopy) {
ABSL_BASE_INTERNAL_EXPECT_FAIL((__VA_ARGS__), absl::bad_any_cast, \
"Bad any cast")
-#endif // defined(ABSL_HAVE_STD_ANY)
+#endif // defined(ABSL_USES_STD_ANY)
TEST(AnyTest, ThrowBadAlloc) {
{
@@ -761,7 +764,7 @@ TEST(AnyTest, FailedEmplace) {
BadCopyable bad;
absl::any target(absl::in_place_type<int>);
ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace<BadCopyable>(bad));
-#if defined(ABSL_HAVE_STD_ANY) && defined(__GLIBCXX__)
+#if defined(ABSL_USES_STD_ANY) && defined(__GLIBCXX__)
// libstdc++ std::any::emplace() implementation (as of 7.2) has a bug: if an
// exception is thrown, *this contains a value.
#define ABSL_GLIBCXX_ANY_EMPLACE_EXCEPTION_BUG 1
@@ -774,3 +777,5 @@ TEST(AnyTest, FailedEmplace) {
}
} // namespace
+
+#endif // #if !defined(ABSL_USES_STD_ANY)
diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc
index 062675df..b0592cc9 100644
--- a/absl/types/bad_any_cast.cc
+++ b/absl/types/bad_any_cast.cc
@@ -14,7 +14,7 @@
#include "absl/types/bad_any_cast.h"
-#ifndef ABSL_HAVE_STD_ANY
+#ifndef ABSL_USES_STD_ANY
#include <cstdlib>
@@ -22,7 +22,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
bad_any_cast::~bad_any_cast() = default;
@@ -40,7 +40,7 @@ void ThrowBadAnyCast() {
}
} // namespace any_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#endif // ABSL_HAVE_STD_ANY
+#endif // ABSL_USES_STD_ANY
diff --git a/absl/types/bad_any_cast.h b/absl/types/bad_any_cast.h
index 8a8476ce..114cef80 100644
--- a/absl/types/bad_any_cast.h
+++ b/absl/types/bad_any_cast.h
@@ -25,20 +25,20 @@
#include "absl/base/config.h"
-#ifdef ABSL_HAVE_STD_ANY
+#ifdef ABSL_USES_STD_ANY
#include <any>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
using std::bad_any_cast;
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#else // ABSL_HAVE_STD_ANY
+#else // ABSL_USES_STD_ANY
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// bad_any_cast
@@ -67,9 +67,9 @@ namespace any_internal {
[[noreturn]] void ThrowBadAnyCast();
} // namespace any_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#endif // ABSL_HAVE_STD_ANY
+#endif // ABSL_USES_STD_ANY
#endif // ABSL_TYPES_BAD_ANY_CAST_H_
diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc
index 440adac2..26aca70d 100644
--- a/absl/types/bad_optional_access.cc
+++ b/absl/types/bad_optional_access.cc
@@ -14,7 +14,7 @@
#include "absl/types/bad_optional_access.h"
-#ifndef ABSL_HAVE_STD_OPTIONAL
+#ifndef ABSL_USES_STD_OPTIONAL
#include <cstdlib>
@@ -22,7 +22,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
bad_optional_access::~bad_optional_access() = default;
@@ -42,7 +42,7 @@ void throw_bad_optional_access() {
}
} // namespace optional_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#endif // ABSL_HAVE_STD_OPTIONAL
+#endif // ABSL_USES_STD_OPTIONAL
diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h
index 585c7c77..a500286a 100644
--- a/absl/types/bad_optional_access.h
+++ b/absl/types/bad_optional_access.h
@@ -25,20 +25,20 @@
#include "absl/base/config.h"
-#ifdef ABSL_HAVE_STD_OPTIONAL
+#ifdef ABSL_USES_STD_OPTIONAL
#include <optional>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
using std::bad_optional_access;
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#else // ABSL_HAVE_STD_OPTIONAL
+#else // ABSL_USES_STD_OPTIONAL
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// bad_optional_access
@@ -70,9 +70,9 @@ namespace optional_internal {
[[noreturn]] void throw_bad_optional_access();
} // namespace optional_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#endif // ABSL_HAVE_STD_OPTIONAL
+#endif // ABSL_USES_STD_OPTIONAL
#endif // ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_
diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc
index d60dae9a..3dc88cc0 100644
--- a/absl/types/bad_variant_access.cc
+++ b/absl/types/bad_variant_access.cc
@@ -14,7 +14,7 @@
#include "absl/types/bad_variant_access.h"
-#ifndef ABSL_HAVE_STD_VARIANT
+#ifndef ABSL_USES_STD_VARIANT
#include <cstdlib>
#include <stdexcept>
@@ -23,7 +23,7 @@
#include "absl/base/internal/raw_logging.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
//////////////////////////
// [variant.bad.access] //
@@ -58,7 +58,7 @@ void Rethrow() {
}
} // namespace variant_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#endif // ABSL_HAVE_STD_VARIANT
+#endif // ABSL_USES_STD_VARIANT
diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h
index 8d635b57..095969f9 100644
--- a/absl/types/bad_variant_access.h
+++ b/absl/types/bad_variant_access.h
@@ -25,20 +25,20 @@
#include "absl/base/config.h"
-#ifdef ABSL_HAVE_STD_VARIANT
+#ifdef ABSL_USES_STD_VARIANT
#include <variant>
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
using std::bad_variant_access;
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#else // ABSL_HAVE_STD_VARIANT
+#else // ABSL_USES_STD_VARIANT
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// bad_variant_access
@@ -74,9 +74,9 @@ namespace variant_internal {
[[noreturn]] void Rethrow();
} // namespace variant_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#endif // ABSL_HAVE_STD_VARIANT
+#endif // ABSL_USES_STD_VARIANT
#endif // ABSL_TYPES_BAD_VARIANT_ACCESS_H_
diff --git a/absl/types/compare.h b/absl/types/compare.h
index 6f371be7..62ca70f9 100644
--- a/absl/types/compare.h
+++ b/absl/types/compare.h
@@ -39,7 +39,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace compare_internal {
using value_type = int8_t;
@@ -80,79 +80,72 @@ enum class ord : value_type { less = -1, greater = 1 };
enum class ncmp : value_type { unordered = -127 };
+// Define macros to allow for creation or emulation of C++17 inline variables
+// based on whether the feature is supported. Note: we can't use
+// ABSL_INTERNAL_INLINE_CONSTEXPR here because the variables here are of
+// incomplete types so they need to be defined after the types are complete.
+#ifdef __cpp_inline_variables
+
+#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name)
+
+#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) \
+ static const type name
+
+#define ABSL_COMPARE_INLINE_INIT(type, name, init) \
+ inline constexpr type type::name(init)
+
+#else // __cpp_inline_variables
+
+#define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) \
+ ABSL_CONST_INIT static const T name
+
+#define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name)
+
+#define ABSL_COMPARE_INLINE_INIT(type, name, init) \
+ template <typename T> \
+ const T compare_internal::type##_base<T>::name(init)
+
+#endif // __cpp_inline_variables
+
// These template base classes allow for defining the values of the constants
// 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_CONST_INIT static const T equivalent;
- ABSL_CONST_INIT static const T nonequivalent;
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent);
};
-template <typename T>
-const T weak_equality_base<T>::equivalent(eq::equivalent);
-template <typename T>
-const T weak_equality_base<T>::nonequivalent(eq::nonequivalent);
template <typename T>
struct strong_equality_base {
- ABSL_CONST_INIT static const T equal;
- ABSL_CONST_INIT static const T nonequal;
- ABSL_CONST_INIT static const T equivalent;
- ABSL_CONST_INIT static const T nonequivalent;
+ 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>
-const T strong_equality_base<T>::equal(eq::equal);
-template <typename T>
-const T strong_equality_base<T>::nonequal(eq::nonequal);
-template <typename T>
-const T strong_equality_base<T>::equivalent(eq::equivalent);
-template <typename T>
-const T strong_equality_base<T>::nonequivalent(eq::nonequivalent);
template <typename T>
struct partial_ordering_base {
- ABSL_CONST_INIT static const T less;
- ABSL_CONST_INIT static const T equivalent;
- ABSL_CONST_INIT static const T greater;
- ABSL_CONST_INIT static const T unordered;
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(greater);
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(unordered);
};
-template <typename T>
-const T partial_ordering_base<T>::less(ord::less);
-template <typename T>
-const T partial_ordering_base<T>::equivalent(eq::equivalent);
-template <typename T>
-const T partial_ordering_base<T>::greater(ord::greater);
-template <typename T>
-const T partial_ordering_base<T>::unordered(ncmp::unordered);
template <typename T>
struct weak_ordering_base {
- ABSL_CONST_INIT static const T less;
- ABSL_CONST_INIT static const T equivalent;
- ABSL_CONST_INIT static const T greater;
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(greater);
};
-template <typename T>
-const T weak_ordering_base<T>::less(ord::less);
-template <typename T>
-const T weak_ordering_base<T>::equivalent(eq::equivalent);
-template <typename T>
-const T weak_ordering_base<T>::greater(ord::greater);
template <typename T>
struct strong_ordering_base {
- ABSL_CONST_INIT static const T less;
- ABSL_CONST_INIT static const T equal;
- ABSL_CONST_INIT static const T equivalent;
- ABSL_CONST_INIT static const T greater;
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(less);
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(equal);
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent);
+ ABSL_COMPARE_INLINE_BASECLASS_DECL(greater);
};
-template <typename T>
-const T strong_ordering_base<T>::less(ord::less);
-template <typename T>
-const T strong_ordering_base<T>::equal(eq::equal);
-template <typename T>
-const T strong_ordering_base<T>::equivalent(eq::equivalent);
-template <typename T>
-const T strong_ordering_base<T>::greater(ord::greater);
} // namespace compare_internal
@@ -163,6 +156,9 @@ class weak_equality
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 {
@@ -180,10 +176,22 @@ class weak_equality
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> {
@@ -192,6 +200,11 @@ class strong_equality
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
@@ -214,10 +227,25 @@ class strong_equality
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> {
@@ -235,6 +263,11 @@ class partial_ordering
}
public:
+ ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, less);
+ ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, equivalent);
+ 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
@@ -289,10 +322,25 @@ class partial_ordering
partial_ordering v) noexcept {
return v.is_ordered() && 0 >= v.value_;
}
+ friend constexpr bool operator==(partial_ordering v1,
+ partial_ordering v2) noexcept {
+ return v1.value_ == v2.value_;
+ }
+ friend constexpr bool operator!=(partial_ordering v1,
+ partial_ordering v2) noexcept {
+ return v1.value_ != v2.value_;
+ }
private:
compare_internal::value_type value_;
};
+ABSL_COMPARE_INLINE_INIT(partial_ordering, less, compare_internal::ord::less);
+ABSL_COMPARE_INLINE_INIT(partial_ordering, equivalent,
+ compare_internal::eq::equivalent);
+ABSL_COMPARE_INLINE_INIT(partial_ordering, greater,
+ compare_internal::ord::greater);
+ABSL_COMPARE_INLINE_INIT(partial_ordering, unordered,
+ compare_internal::ncmp::unordered);
class weak_ordering
: public compare_internal::weak_ordering_base<weak_ordering> {
@@ -303,6 +351,10 @@ class weak_ordering
friend struct compare_internal::weak_ordering_base<weak_ordering>;
public:
+ ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, less);
+ ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, equivalent);
+ ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, greater);
+
// Conversions
constexpr operator weak_equality() const noexcept { // NOLINT
return value_ == 0 ? weak_equality::equivalent
@@ -362,10 +414,23 @@ class weak_ordering
weak_ordering v) noexcept {
return 0 >= v.value_;
}
+ friend constexpr bool operator==(weak_ordering v1,
+ weak_ordering v2) noexcept {
+ return v1.value_ == v2.value_;
+ }
+ friend constexpr bool operator!=(weak_ordering v1,
+ weak_ordering v2) noexcept {
+ return v1.value_ != v2.value_;
+ }
private:
compare_internal::value_type value_;
};
+ABSL_COMPARE_INLINE_INIT(weak_ordering, less, compare_internal::ord::less);
+ABSL_COMPARE_INLINE_INIT(weak_ordering, equivalent,
+ compare_internal::eq::equivalent);
+ABSL_COMPARE_INLINE_INIT(weak_ordering, greater,
+ compare_internal::ord::greater);
class strong_ordering
: public compare_internal::strong_ordering_base<strong_ordering> {
@@ -376,6 +441,11 @@ class strong_ordering
friend struct compare_internal::strong_ordering_base<strong_ordering>;
public:
+ ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, less);
+ ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equal);
+ ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equivalent);
+ ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, greater);
+
// Conversions
constexpr operator weak_equality() const noexcept { // NOLINT
return value_ == 0 ? weak_equality::equivalent
@@ -443,10 +513,28 @@ class strong_ordering
strong_ordering v) noexcept {
return 0 >= v.value_;
}
+ friend constexpr bool operator==(strong_ordering v1,
+ strong_ordering v2) noexcept {
+ return v1.value_ == v2.value_;
+ }
+ friend constexpr bool operator!=(strong_ordering v1,
+ strong_ordering v2) noexcept {
+ return v1.value_ != v2.value_;
+ }
private:
compare_internal::value_type value_;
};
+ABSL_COMPARE_INLINE_INIT(strong_ordering, less, compare_internal::ord::less);
+ABSL_COMPARE_INLINE_INIT(strong_ordering, equal, compare_internal::eq::equal);
+ABSL_COMPARE_INLINE_INIT(strong_ordering, equivalent,
+ compare_internal::eq::equivalent);
+ABSL_COMPARE_INLINE_INIT(strong_ordering, greater,
+ compare_internal::ord::greater);
+
+#undef ABSL_COMPARE_INLINE_BASECLASS_DECL
+#undef ABSL_COMPARE_INLINE_SUBCLASS_DECL
+#undef ABSL_COMPARE_INLINE_INIT
namespace compare_internal {
// We also provide these comparator adapter functions for internal absl use.
@@ -504,7 +592,7 @@ constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare,
}
} // namespace compare_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TYPES_COMPARE_H_
diff --git a/absl/types/compare_test.cc b/absl/types/compare_test.cc
index 11b3ad40..8095baf9 100644
--- a/absl/types/compare_test.cc
+++ b/absl/types/compare_test.cc
@@ -18,7 +18,7 @@
#include "absl/base/casts.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
// This is necessary to avoid a bunch of lint warnings suggesting that we use
@@ -31,6 +31,15 @@ TEST(Compare, WeakEquality) {
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) {
@@ -42,6 +51,18 @@ TEST(Compare, StrongEquality) {
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) {
@@ -65,6 +86,16 @@ TEST(Compare, PartialOrdering) {
EXPECT_FALSE(Identity(0 > partial_ordering::unordered));
EXPECT_FALSE(Identity(partial_ordering::unordered >= 0));
EXPECT_FALSE(Identity(0 >= partial_ordering::unordered));
+ const partial_ordering values[] = {
+ partial_ordering::less, partial_ordering::equivalent,
+ partial_ordering::greater, partial_ordering::unordered};
+ 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, WeakOrdering) {
@@ -78,6 +109,15 @@ TEST(Compare, WeakOrdering) {
EXPECT_TRUE(Identity(0 < weak_ordering::greater));
EXPECT_TRUE(Identity(weak_ordering::greater >= 0));
EXPECT_TRUE(Identity(0 <= weak_ordering::greater));
+ const weak_ordering values[] = {
+ weak_ordering::less, weak_ordering::equivalent, weak_ordering::greater};
+ 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, StrongOrdering) {
@@ -93,6 +133,16 @@ TEST(Compare, StrongOrdering) {
EXPECT_TRUE(Identity(0 < strong_ordering::greater));
EXPECT_TRUE(Identity(strong_ordering::greater >= 0));
EXPECT_TRUE(Identity(0 <= strong_ordering::greater));
+ const strong_ordering values[] = {
+ strong_ordering::less, strong_ordering::equal, strong_ordering::greater};
+ 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_ordering::equivalent == strong_ordering::equal));
}
TEST(Compare, Conversions) {
@@ -190,7 +240,7 @@ TEST(Compare, Conversions) {
struct WeakOrderingLess {
template <typename T>
- absl::weak_ordering operator()(const T &a, const T &b) const {
+ absl::weak_ordering operator()(const T& a, const T& b) const {
return a < b ? absl::weak_ordering::less
: a == b ? absl::weak_ordering::equivalent
: absl::weak_ordering::greater;
@@ -224,10 +274,10 @@ TEST(DoLessThanComparison, SanityTest) {
}
TEST(CompareResultAsOrdering, SanityTest) {
- EXPECT_TRUE(Identity(
- absl::compare_internal::compare_result_as_ordering(-1) < 0));
- EXPECT_FALSE(Identity(
- absl::compare_internal::compare_result_as_ordering(-1) == 0));
+ EXPECT_TRUE(
+ Identity(absl::compare_internal::compare_result_as_ordering(-1) < 0));
+ EXPECT_FALSE(
+ Identity(absl::compare_internal::compare_result_as_ordering(-1) == 0));
EXPECT_FALSE(
Identity(absl::compare_internal::compare_result_as_ordering(-1) > 0));
EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering(
@@ -237,31 +287,31 @@ TEST(CompareResultAsOrdering, SanityTest) {
EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
weak_ordering::less) > 0));
- EXPECT_FALSE(Identity(
- absl::compare_internal::compare_result_as_ordering(0) < 0));
- EXPECT_TRUE(Identity(
- absl::compare_internal::compare_result_as_ordering(0) == 0));
- EXPECT_FALSE(Identity(
- absl::compare_internal::compare_result_as_ordering(0) > 0));
+ EXPECT_FALSE(
+ Identity(absl::compare_internal::compare_result_as_ordering(0) < 0));
+ EXPECT_TRUE(
+ Identity(absl::compare_internal::compare_result_as_ordering(0) == 0));
+ EXPECT_FALSE(
+ Identity(absl::compare_internal::compare_result_as_ordering(0) > 0));
EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
- weak_ordering::equivalent) < 0));
+ weak_ordering::equivalent) < 0));
EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering(
- weak_ordering::equivalent) == 0));
+ weak_ordering::equivalent) == 0));
EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
weak_ordering::equivalent) > 0));
- EXPECT_FALSE(Identity(
- absl::compare_internal::compare_result_as_ordering(1) < 0));
- EXPECT_FALSE(Identity(
- absl::compare_internal::compare_result_as_ordering(1) == 0));
- EXPECT_TRUE(Identity(
- absl::compare_internal::compare_result_as_ordering(1) > 0));
+ EXPECT_FALSE(
+ Identity(absl::compare_internal::compare_result_as_ordering(1) < 0));
+ EXPECT_FALSE(
+ Identity(absl::compare_internal::compare_result_as_ordering(1) == 0));
+ EXPECT_TRUE(
+ Identity(absl::compare_internal::compare_result_as_ordering(1) > 0));
EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
- weak_ordering::greater) < 0));
+ weak_ordering::greater) < 0));
EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering(
weak_ordering::greater) == 0));
EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering(
- weak_ordering::greater) > 0));
+ weak_ordering::greater) > 0));
}
TEST(DoThreeWayComparison, SanityTest) {
@@ -308,6 +358,32 @@ TEST(DoThreeWayComparison, SanityTest) {
absl::compare_internal::do_three_way_comparison(weak, 10, 5) > 0));
}
+#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, "");
+ static_assert(partial_ordering::unordered != 0, "");
+
+ static_assert(weak_ordering::less < 0, "");
+ static_assert(weak_ordering::equivalent == 0, "");
+ static_assert(weak_ordering::greater > 0, "");
+
+ static_assert(strong_ordering::less < 0, "");
+ static_assert(strong_ordering::equal == 0, "");
+ static_assert(strong_ordering::equivalent == 0, "");
+ static_assert(strong_ordering::greater > 0, "");
+}
+#endif // __cpp_inline_variables
+
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/types/internal/conformance_aliases.h b/absl/types/internal/conformance_aliases.h
new file mode 100644
index 00000000..0cc6884e
--- /dev/null
+++ b/absl/types/internal/conformance_aliases.h
@@ -0,0 +1,447 @@
+// 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
new file mode 100644
index 00000000..2349e0f7
--- /dev/null
+++ b/absl/types/internal/conformance_archetype.h
@@ -0,0 +1,978 @@
+// 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
new file mode 100644
index 00000000..e62004fd
--- /dev/null
+++ b/absl/types/internal/conformance_profile.h
@@ -0,0 +1,376 @@
+// 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 <type_traits>
+#include <utility>
+
+#include "absl/meta/type_traits.h"
+
+// TODO(calabrese) Add support for extending profiles.
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace types_internal {
+
+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.
+
+enum class function_kind { maybe, yes, nothrow, trivial };
+
+#define ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(name) \
+ enum class name { maybe, yes, nothrow, trivial }
+
+ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(default_constructible);
+ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(move_constructible);
+ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(copy_constructible);
+ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(move_assignable);
+ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(copy_assignable);
+ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(destructible);
+
+#undef ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM
+
+#define ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(name) \
+ enum class name { maybe, yes, nothrow }
+
+ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(equality_comparable);
+ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(inequality_comparable);
+ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(less_than_comparable);
+ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(less_equal_comparable);
+ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(greater_equal_comparable);
+ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(greater_than_comparable);
+
+ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(swappable);
+
+#undef ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM
+
+enum class hashable { maybe, yes };
+
+constexpr const char* PropertyName(hashable v) {
+ return "support for std::hash";
+}
+
+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;
+};
+
+#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)
+
+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);
+
+#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF
+#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL
+
+// Converts an enum to its underlying integral value.
+template <class Enum>
+constexpr absl::underlying_type_t<Enum> UnderlyingValue(Enum value) {
+ return static_cast<absl::underlying_type_t<Enum>>(value);
+}
+
+// Retrieve the enum with the 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 {};
+
+} // namespace types_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_
diff --git a/absl/types/internal/conformance_testing_test.cc b/absl/types/internal/conformance_testing_test.cc
new file mode 100644
index 00000000..3dcf5305
--- /dev/null
+++ b/absl/types/internal/conformance_testing_test.cc
@@ -0,0 +1,1186 @@
+// 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 <new>
+#include <type_traits>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "absl/meta/type_traits.h"
+#include "absl/types/internal/conformance_aliases.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);
+
+// TODO(calabrese) Test runtime results
+
+} // namespace
diff --git a/absl/types/internal/optional.h b/absl/types/internal/optional.h
index 3c8e7cca..92932b60 100644
--- a/absl/types/internal/optional.h
+++ b/absl/types/internal/optional.h
@@ -54,7 +54,7 @@
#endif
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// Forward declaration
template <typename T>
@@ -85,8 +85,8 @@ class optional_data_dtor_base {
bool engaged_;
// Data storage
union {
- dummy_type dummy_;
T data_;
+ dummy_type dummy_;
};
void destruct() noexcept {
@@ -120,8 +120,8 @@ class optional_data_dtor_base<T, true> {
bool engaged_;
// Data storage
union {
- dummy_type dummy_;
T data_;
+ dummy_type dummy_;
};
void destruct() noexcept { engaged_ = false; }
@@ -388,7 +388,7 @@ struct optional_hash_base<T, decltype(std::hash<absl::remove_const_t<T> >()(
};
} // namespace optional_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS
diff --git a/absl/types/internal/span.h b/absl/types/internal/span.h
index 873ae160..112612f4 100644
--- a/absl/types/internal/span.h
+++ b/absl/types/internal/span.h
@@ -26,7 +26,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace span_internal {
// A constexpr min function
@@ -122,7 +122,7 @@ template <typename From, typename To>
using EnableIfConvertibleTo =
typename std::enable_if<IsConvertible<From, To>::value>::type;
} // namespace span_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TYPES_INTERNAL_SPAN_H_
diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h
index 4f29f617..71bd3adf 100644
--- a/absl/types/internal/variant.h
+++ b/absl/types/internal/variant.h
@@ -37,10 +37,10 @@
#include "absl/types/bad_variant_access.h"
#include "absl/utility/utility.h"
-#if !defined(ABSL_HAVE_STD_VARIANT)
+#if !defined(ABSL_USES_STD_VARIANT)
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
template <class... Types>
class variant;
@@ -1639,8 +1639,8 @@ struct VariantHashBase<Variant,
};
} // namespace variant_internal
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#endif // !defined(ABSL_HAVE_STD_VARIANT)
+#endif // !defined(ABSL_USES_STD_VARIANT)
#endif // ABSL_TYPES_variant_internal_H_
diff --git a/absl/types/optional.h b/absl/types/optional.h
index 6614d7bd..2025e29f 100644
--- a/absl/types/optional.h
+++ b/absl/types/optional.h
@@ -38,21 +38,21 @@
#include "absl/base/config.h" // TODO(calabrese) IWYU removal?
#include "absl/utility/utility.h"
-#ifdef ABSL_HAVE_STD_OPTIONAL
+#ifdef ABSL_USES_STD_OPTIONAL
#include <optional> // IWYU pragma: export
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
using std::bad_optional_access;
using std::optional;
using std::make_optional;
using std::nullopt_t;
using std::nullopt;
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#else // ABSL_HAVE_STD_OPTIONAL
+#else // ABSL_USES_STD_OPTIONAL
#include <cassert>
#include <functional>
@@ -67,7 +67,7 @@ using std::nullopt;
#include "absl/types/internal/optional.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// nullopt_t
//
@@ -757,7 +757,7 @@ constexpr auto operator>=(const U& v, const optional<T>& x)
return static_cast<bool>(x) ? static_cast<bool>(v >= *x) : true;
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
namespace std {
@@ -771,6 +771,6 @@ struct hash<absl::optional<T> >
#undef ABSL_MSVC_CONSTEXPR_BUG_IN_UNION_LIKE_CLASS
-#endif // ABSL_HAVE_STD_OPTIONAL
+#endif // ABSL_USES_STD_OPTIONAL
#endif // ABSL_TYPES_OPTIONAL_H_
diff --git a/absl/types/optional_exception_safety_test.cc b/absl/types/optional_exception_safety_test.cc
index 056ced42..8e5fe851 100644
--- a/absl/types/optional_exception_safety_test.cc
+++ b/absl/types/optional_exception_safety_test.cc
@@ -14,11 +14,17 @@
#include "absl/types/optional.h"
+#include "absl/base/config.h"
+
+// This test is a no-op when absl::optional is an alias for std::optional and
+// when exceptions are not enabled.
+#if !defined(ABSL_USES_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS)
+
#include "gtest/gtest.h"
#include "absl/base/internal/exception_safety_testing.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
@@ -280,5 +286,7 @@ TEST(OptionalExceptionSafety, NothrowMoveAssign) {
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
+
+#endif // #if !defined(ABSL_USES_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS)
diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc
index e6a36eb8..47d5c8a2 100644
--- a/absl/types/optional_test.cc
+++ b/absl/types/optional_test.cc
@@ -14,6 +14,9 @@
#include "absl/types/optional.h"
+// This test is a no-op when absl::optional is an alias for std::optional.
+#if !defined(ABSL_USES_STD_OPTIONAL)
+
#include <string>
#include <type_traits>
#include <utility>
@@ -221,7 +224,7 @@ TEST(optionalTest, CopyConstructor) {
EXPECT_FALSE(
absl::is_trivially_copy_constructible<absl::optional<Copyable>>::value);
-#if defined(ABSL_HAVE_STD_OPTIONAL) && defined(__GLIBCXX__)
+#if defined(ABSL_USES_STD_OPTIONAL) && defined(__GLIBCXX__)
// libstdc++ std::optional implementation (as of 7.2) has a bug: when T is
// trivially copyable, optional<T> is not trivially copyable (due to one of
// its base class is unconditionally nontrivial).
@@ -276,7 +279,7 @@ TEST(optionalTest, CopyConstructor) {
// std::optional when T is volatile-qualified. So skipping this test.
// Bug report:
// https://connect.microsoft.com/VisualStudio/feedback/details/3142534
-#if defined(ABSL_HAVE_STD_OPTIONAL) && defined(_MSC_VER) && _MSC_VER >= 1911
+#if defined(ABSL_USES_STD_OPTIONAL) && defined(_MSC_VER) && _MSC_VER >= 1911
#define ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG 1
#endif
#ifndef ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG
@@ -302,7 +305,7 @@ TEST(optionalTest, MoveConstructor) {
EXPECT_FALSE(std::is_move_constructible<absl::optional<NonMovable>>::value);
// test noexcept
EXPECT_TRUE(std::is_nothrow_move_constructible<absl::optional<int>>::value);
-#ifndef ABSL_HAVE_STD_OPTIONAL
+#ifndef ABSL_USES_STD_OPTIONAL
EXPECT_EQ(
absl::default_allocator_is_nothrow::value,
std::is_nothrow_move_constructible<absl::optional<MoveableThrow>>::value);
@@ -636,7 +639,7 @@ TEST(optionalTest, CopyAssignment) {
EXPECT_FALSE(absl::is_trivially_copy_assignable<NonTrivial>::value);
// std::optional doesn't support volatile nontrivial types.
-#ifndef ABSL_HAVE_STD_OPTIONAL
+#ifndef ABSL_USES_STD_OPTIONAL
{
StructorListener listener;
Listenable::listener = &listener;
@@ -655,7 +658,7 @@ TEST(optionalTest, CopyAssignment) {
EXPECT_EQ(1, listener.destruct);
EXPECT_EQ(1, listener.volatile_copy_assign);
}
-#endif // ABSL_HAVE_STD_OPTIONAL
+#endif // ABSL_USES_STD_OPTIONAL
}
TEST(optionalTest, MoveAssignment) {
@@ -679,7 +682,7 @@ TEST(optionalTest, MoveAssignment) {
EXPECT_EQ(1, listener.move_assign);
}
// std::optional doesn't support volatile nontrivial types.
-#ifndef ABSL_HAVE_STD_OPTIONAL
+#ifndef ABSL_USES_STD_OPTIONAL
{
StructorListener listener;
Listenable::listener = &listener;
@@ -699,7 +702,7 @@ TEST(optionalTest, MoveAssignment) {
EXPECT_EQ(1, listener.destruct);
EXPECT_EQ(1, listener.volatile_move_assign);
}
-#endif // ABSL_HAVE_STD_OPTIONAL
+#endif // ABSL_USES_STD_OPTIONAL
EXPECT_FALSE(absl::is_move_assignable<absl::optional<const int>>::value);
EXPECT_TRUE(absl::is_move_assignable<absl::optional<Copyable>>::value);
EXPECT_TRUE(absl::is_move_assignable<absl::optional<MoveableThrow>>::value);
@@ -941,7 +944,7 @@ TEST(optionalTest, Swap) {
template <int v>
struct DeletedOpAddr {
- constexpr static const int value = v;
+ int value = v;
constexpr DeletedOpAddr() = default;
constexpr const DeletedOpAddr<v>* operator&() const = delete; // NOLINT
DeletedOpAddr<v>* operator&() = delete; // NOLINT
@@ -951,9 +954,9 @@ struct DeletedOpAddr {
// to document the fact that the current implementation of absl::optional<T>
// expects such usecases to be malformed and not compile.
TEST(optionalTest, OperatorAddr) {
- constexpr const int v = -1;
+ constexpr int v = -1;
{ // constexpr
- constexpr const absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{});
+ constexpr absl::optional<DeletedOpAddr<v>> opt(absl::in_place_t{});
static_assert(opt.has_value(), "");
// static_assert(opt->value == v, "");
static_assert((*opt).value == v, "");
@@ -1557,7 +1560,7 @@ TEST(optionalTest, NoExcept) {
static_assert(
std::is_nothrow_move_constructible<absl::optional<MoveMeNoThrow>>::value,
"");
-#ifndef ABSL_HAVE_STD_OPTIONAL
+#ifndef ABSL_USES_STD_OPTIONAL
static_assert(absl::default_allocator_is_nothrow::value ==
std::is_nothrow_move_constructible<
absl::optional<MoveMeThrow>>::value,
@@ -1654,3 +1657,5 @@ TEST(optionalTest, InPlaceTSFINAEBug) {
#endif // !defined(__EMSCRIPTEN__)
} // namespace
+
+#endif // #if !defined(ABSL_USES_STD_OPTIONAL)
diff --git a/absl/types/span.h b/absl/types/span.h
index 98c6cdc8..3283145a 100644
--- a/absl/types/span.h
+++ b/absl/types/span.h
@@ -71,7 +71,7 @@
#include "absl/types/internal/span.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
//------------------------------------------------------------------------------
// Span
@@ -708,6 +708,6 @@ template <int&... ExplicitArgumentBarrier, typename T, size_t N>
constexpr Span<const T> MakeConstSpan(const T (&array)[N]) noexcept {
return Span<const T>(array, N);
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TYPES_SPAN_H_
diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc
index 9269f911..22467a0a 100644
--- a/absl/types/span_test.cc
+++ b/absl/types/span_test.cc
@@ -35,7 +35,7 @@
namespace {
MATCHER_P(DataIs, data,
- absl::StrCat("data() is ", negation ? "is " : "isn't ",
+ absl::StrCat("data() ", negation ? "isn't " : "is ",
testing::PrintToString(data))) {
return arg.data() == data;
}
diff --git a/absl/types/variant.h b/absl/types/variant.h
index 1c1962b1..776d19a1 100644
--- a/absl/types/variant.h
+++ b/absl/types/variant.h
@@ -24,7 +24,7 @@
// should always hold a value of one of its alternative types (except in the
// "valueless by exception state" -- see below). A default-constructed
// `absl::variant` will hold the value of its first alternative type, provided
-// it is default-constructable.
+// it is default-constructible.
//
// In exceptional cases due to error, an `absl::variant` can hold no
// value (known as a "valueless by exception" state), though this is not the
@@ -45,12 +45,12 @@
#include "absl/base/config.h"
#include "absl/utility/utility.h"
-#ifdef ABSL_HAVE_STD_VARIANT
+#ifdef ABSL_USES_STD_VARIANT
#include <variant> // IWYU pragma: export
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
using std::bad_variant_access;
using std::get;
using std::get_if;
@@ -63,10 +63,10 @@ using std::variant_npos;
using std::variant_size;
using std::variant_size_v;
using std::visit;
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
-#else // ABSL_HAVE_STD_VARIANT
+#else // ABSL_USES_STD_VARIANT
#include <functional>
#include <new>
@@ -79,7 +79,7 @@ using std::visit;
#include "absl/types/internal/variant.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// -----------------------------------------------------------------------------
// absl::variant
@@ -95,7 +95,7 @@ inline namespace lts_2019_08_08 {
// // assign it to a std::string.
// absl::variant<int, std::string> v = std::string("abc");
//
-// // A default-contructed variant will hold a value-initialized value of
+// // A default-constructed variant will hold a value-initialized value of
// // the first alternative type.
// auto a = absl::variant<int, std::string>(); // Holds an int of value '0'.
//
@@ -798,7 +798,7 @@ operator>=(const variant<Types...>& a, const variant<Types...>& b) {
a.index());
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
namespace std {
@@ -816,10 +816,10 @@ struct hash<absl::variant<T...>>
} // namespace std
-#endif // ABSL_HAVE_STD_VARIANT
+#endif // ABSL_USES_STD_VARIANT
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace variant_internal {
// Helper visitor for converting a variant<Ts...>` into another type (mostly
@@ -855,7 +855,7 @@ To ConvertVariantTo(Variant&& variant) {
std::forward<Variant>(variant));
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_TYPES_VARIANT_H_
diff --git a/absl/types/variant_benchmark.cc b/absl/types/variant_benchmark.cc
index efe02310..350b1753 100644
--- a/absl/types/variant_benchmark.cc
+++ b/absl/types/variant_benchmark.cc
@@ -28,7 +28,7 @@
#include "absl/utility/utility.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
template <std::size_t I>
@@ -218,5 +218,5 @@ BENCHMARK_TEMPLATE(BM_RedundantVisit, 4, 2)
->DenseRange(0, integral_pow(4, 2) - 1);
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc
index 31662545..439c6e1d 100644
--- a/absl/types/variant_exception_safety_test.cc
+++ b/absl/types/variant_exception_safety_test.cc
@@ -14,6 +14,12 @@
#include "absl/types/variant.h"
+#include "absl/base/config.h"
+
+// This test is a no-op when absl::variant is an alias for std::variant and when
+// exceptions are not enabled.
+#if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS)
+
#include <iostream>
#include <memory>
#include <utility>
@@ -21,7 +27,6 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include "absl/base/config.h"
#include "absl/base/internal/exception_safety_testing.h"
#include "absl/memory/memory.h"
@@ -29,7 +34,7 @@
#if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
using ::testing::MakeExceptionSafetyTester;
@@ -233,7 +238,7 @@ TEST(VariantExceptionSafetyTest, CopyAssign) {
}
// libstdc++ std::variant has bugs on copy assignment regarding exception
// safety.
-#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
// index() != j
// if is_nothrow_copy_constructible_v<Tj> or
// !is_nothrow_move_constructible<Tj> is true, equivalent to
@@ -264,7 +269,7 @@ TEST(VariantExceptionSafetyTest, CopyAssign) {
.Test());
EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test());
}
-#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
{
// is_nothrow_copy_constructible_v<Tj> == false &&
// is_nothrow_move_constructible_v<Tj> == true
@@ -321,7 +326,7 @@ TEST(VariantExceptionSafetyTest, MoveAssign) {
// The fix is targeted for gcc-9.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87431#c7
// https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=267614
-#if !(defined(ABSL_HAVE_STD_VARIANT) && \
+#if !(defined(ABSL_USES_STD_VARIANT) && \
defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8)
// - otherwise (index() != j), equivalent to
// emplace<j>(get<j>(std::move(rhs)))
@@ -338,7 +343,7 @@ TEST(VariantExceptionSafetyTest, MoveAssign) {
auto copy = rhs;
*lhs = std::move(copy);
}));
-#endif // !(defined(ABSL_HAVE_STD_VARIANT) &&
+#endif // !(defined(ABSL_USES_STD_VARIANT) &&
// defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8)
}
}
@@ -441,7 +446,7 @@ TEST(VariantExceptionSafetyTest, ValueAssign) {
// and operator=(variant&&) invokes Tj's move ctor which doesn't throw.
// libstdc++ std::variant has bugs on conversion assignment regarding
// exception safety.
-#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
{
MoveNothrow rhs;
EXPECT_TRUE(MakeExceptionSafetyTester()
@@ -449,7 +454,7 @@ TEST(VariantExceptionSafetyTest, ValueAssign) {
.WithContracts(VariantInvariants, strong_guarantee)
.Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; }));
}
-#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
}
TEST(VariantExceptionSafetyTest, Emplace) {
@@ -519,7 +524,9 @@ TEST(VariantExceptionSafetyTest, Swap) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE)
+
+#endif // #if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS)
diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc
index ff0f187a..96393333 100644
--- a/absl/types/variant_test.cc
+++ b/absl/types/variant_test.cc
@@ -19,6 +19,9 @@
#include "absl/types/variant.h"
+// This test is a no-op when absl::variant is an alias for std::variant.
+#if !defined(ABSL_USES_STD_VARIANT)
+
#include <algorithm>
#include <cstddef>
#include <functional>
@@ -67,7 +70,7 @@ struct hash<Hashable> {
struct NonHashable {};
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
namespace {
using ::testing::DoubleEq;
@@ -840,7 +843,7 @@ TEST(VariantTest, TestBackupAssign) {
}
// libstdc++ doesn't pass this test
-#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
EXPECT_EQ(3, counter[0]);
EXPECT_EQ(2, counter[1]);
EXPECT_EQ(2, counter[2]);
@@ -1040,8 +1043,6 @@ TEST(VariantTest, MemberSwap) {
using V = variant<MoveCanThrow, std::string, int>;
int i = 33;
std::string s = "abc";
- V valueless(in_place_index<0>);
- ToValuelessByException(valueless);
{
// lhs and rhs holds different alternative
V lhs(i), rhs(s);
@@ -1049,6 +1050,9 @@ TEST(VariantTest, MemberSwap) {
EXPECT_THAT(lhs, VariantWith<std::string>(s));
EXPECT_THAT(rhs, VariantWith<int>(i));
}
+#ifdef ABSL_HAVE_EXCEPTIONS
+ V valueless(in_place_index<0>);
+ ToValuelessByException(valueless);
{
// lhs is valueless
V lhs(valueless), rhs(i);
@@ -1070,6 +1074,7 @@ TEST(VariantTest, MemberSwap) {
EXPECT_TRUE(lhs.valueless_by_exception());
EXPECT_TRUE(rhs.valueless_by_exception());
}
+#endif // ABSL_HAVE_EXCEPTIONS
}
//////////////////////
@@ -1929,7 +1934,7 @@ TEST(VariantTest, VisitReferenceWrapper) {
}
// libstdc++ std::variant doesn't support the INVOKE semantics.
-#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
TEST(VariantTest, VisitMemberFunction) {
absl::variant<std::unique_ptr<Class>> p(absl::make_unique<Class>());
absl::variant<std::unique_ptr<const Class>> cp(
@@ -1953,7 +1958,7 @@ TEST(VariantTest, VisitDataMember) {
EXPECT_EQ(42, absl::visit(&Class::member, cp));
}
-#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
/////////////////////////
// [variant.monostate] //
@@ -2031,7 +2036,7 @@ TEST(VariantTest, NonmemberSwap) {
std::swap(a, b);
EXPECT_THAT(a, VariantWith<SpecialSwap>(v2));
EXPECT_THAT(b, VariantWith<SpecialSwap>(v1));
-#ifndef ABSL_HAVE_STD_VARIANT
+#ifndef ABSL_USES_STD_VARIANT
EXPECT_FALSE(absl::get<SpecialSwap>(a).special_swap);
#endif
@@ -2079,7 +2084,7 @@ TEST(VariantTest, Hash) {
// MSVC std::hash<std::variant> does not use the index, thus produce the same
// result on the same value as different alternative.
-#if !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT))
+#if !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT))
{
// same value as different alternative
variant<int, int> v0(in_place_index<0>, 42);
@@ -2087,7 +2092,7 @@ TEST(VariantTest, Hash) {
std::hash<variant<int, int>> hash;
EXPECT_NE(hash(v0), hash(v1));
}
-#endif // !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT))
+#endif // !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT))
{
std::hash<variant<int>> hash;
@@ -2114,7 +2119,7 @@ TEST(VariantTest, Hash) {
////////////////////////////////////////
// Test that a set requiring a basic type conversion works correctly
-#if !defined(ABSL_HAVE_STD_VARIANT)
+#if !defined(ABSL_USES_STD_VARIANT)
TEST(VariantTest, TestConvertingSet) {
typedef variant<double> Variant;
Variant v(1.0);
@@ -2124,7 +2129,7 @@ TEST(VariantTest, TestConvertingSet) {
ASSERT_TRUE(nullptr != absl::get_if<double>(&v));
EXPECT_DOUBLE_EQ(2, absl::get<double>(v));
}
-#endif // ABSL_HAVE_STD_VARIANT
+#endif // ABSL_USES_STD_VARIANT
// Test that a vector of variants behaves reasonably.
TEST(VariantTest, Container) {
@@ -2276,7 +2281,7 @@ struct Convertible2 {
};
TEST(VariantTest, TestRvalueConversion) {
-#if !defined(ABSL_HAVE_STD_VARIANT)
+#if !defined(ABSL_USES_STD_VARIANT)
variant<double, std::string> var(
ConvertVariantTo<variant<double, std::string>>(
variant<std::string, int>(0)));
@@ -2309,7 +2314,7 @@ TEST(VariantTest, TestRvalueConversion) {
variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42));
ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2));
EXPECT_EQ(42, absl::get<uint32_t>(variant2));
-#endif // !ABSL_HAVE_STD_VARIANT
+#endif // !ABSL_USES_STD_VARIANT
variant<Convertible1, Convertible2> variant3(
ConvertVariantTo<variant<Convertible1, Convertible2>>(
@@ -2322,7 +2327,7 @@ TEST(VariantTest, TestRvalueConversion) {
}
TEST(VariantTest, TestLvalueConversion) {
-#if !defined(ABSL_HAVE_STD_VARIANT)
+#if !defined(ABSL_USES_STD_VARIANT)
variant<std::string, int> source1 = 0;
variant<double, std::string> destination(
ConvertVariantTo<variant<double, std::string>>(source1));
@@ -2424,7 +2429,7 @@ TEST(VariantTest, DoesNotMoveFromLvalues) {
}
TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) {
-#if !defined(ABSL_HAVE_STD_VARIANT)
+#if !defined(ABSL_USES_STD_VARIANT)
variant<double, std::string> var(
ConvertVariantTo<variant<double, std::string>>(
variant<std::string, int>(3)));
@@ -2463,7 +2468,7 @@ TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) {
}
TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) {
-#if !defined(ABSL_HAVE_STD_VARIANT)
+#if !defined(ABSL_USES_STD_VARIANT)
variant<std::string, int> source1 = 3;
variant<double, std::string> destination(
ConvertVariantTo<variant<double, std::string>>(source1));
@@ -2495,7 +2500,7 @@ TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) {
variant<uint32_t> source6(42);
variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6);
EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42));
-#endif // !ABSL_HAVE_STD_VARIANT
+#endif // !ABSL_USES_STD_VARIANT
variant<Convertible2, Convertible1> source7((Convertible1()));
variant<Convertible1, Convertible2> variant3(
@@ -2529,7 +2534,7 @@ TEST(VariantTest, TestMoveConversionViaConvertVariantTo) {
// standard and we know that libstdc++ variant doesn't have this feature.
// For more details see the paper:
// http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0602r0.html
-#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__))
+#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__))
#define ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY 1
#endif
@@ -2705,5 +2710,7 @@ TEST(VariantTest, MoveCtorBug) {
}
} // namespace
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
+
+#endif // #if !defined(ABSL_USES_STD_VARIANT)
diff --git a/absl/utility/BUILD.bazel b/absl/utility/BUILD.bazel
index d41317e3..6881f939 100644
--- a/absl/utility/BUILD.bazel
+++ b/absl/utility/BUILD.bazel
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load(
"//absl:copts/configure_copts.bzl",
"ABSL_DEFAULT_COPTS",
@@ -11,7 +28,9 @@ licenses(["notice"]) # Apache 2.0
cc_library(
name = "utility",
- hdrs = ["utility.h"],
+ hdrs = [
+ "utility.h",
+ ],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
diff --git a/absl/utility/utility.h b/absl/utility/utility.h
index bc9af048..e6647c7b 100644
--- a/absl/utility/utility.h
+++ b/absl/utility/utility.h
@@ -51,7 +51,7 @@
#include "absl/meta/type_traits.h"
namespace absl {
-inline namespace lts_2019_08_08 {
+ABSL_NAMESPACE_BEGIN
// integer_sequence
//
@@ -159,12 +159,12 @@ using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
// Tag types
-#ifdef ABSL_HAVE_STD_OPTIONAL
+#ifdef ABSL_USES_STD_OPTIONAL
using std::in_place_t;
using std::in_place;
-#else // ABSL_HAVE_STD_OPTIONAL
+#else // ABSL_USES_STD_OPTIONAL
// in_place_t
//
@@ -175,9 +175,9 @@ struct in_place_t {};
ABSL_INTERNAL_INLINE_CONSTEXPR(in_place_t, in_place, {});
-#endif // ABSL_HAVE_STD_OPTIONAL
+#endif // ABSL_USES_STD_OPTIONAL
-#if defined(ABSL_HAVE_STD_ANY) || defined(ABSL_HAVE_STD_VARIANT)
+#if defined(ABSL_USES_STD_ANY) || defined(ABSL_USES_STD_VARIANT)
using std::in_place_type;
using std::in_place_type_t;
#else
@@ -192,9 +192,9 @@ using in_place_type_t = void (*)(utility_internal::InPlaceTypeTag<T>);
template <typename T>
void in_place_type(utility_internal::InPlaceTypeTag<T>) {}
-#endif // ABSL_HAVE_STD_ANY || ABSL_HAVE_STD_VARIANT
+#endif // ABSL_USES_STD_ANY || ABSL_USES_STD_VARIANT
-#ifdef ABSL_HAVE_STD_VARIANT
+#ifdef ABSL_USES_STD_VARIANT
using std::in_place_index;
using std::in_place_index_t;
#else
@@ -209,7 +209,7 @@ using in_place_index_t = void (*)(utility_internal::InPlaceIndexTag<I>);
template <size_t I>
void in_place_index(utility_internal::InPlaceIndexTag<I>) {}
-#endif // ABSL_HAVE_STD_VARIANT
+#endif // ABSL_USES_STD_VARIANT
// Constexpr move and forward
@@ -344,7 +344,7 @@ constexpr T make_from_tuple(Tuple&& tup) {
std::tuple_size<absl::decay_t<Tuple>>::value>{});
}
-} // inline namespace lts_2019_08_08
+ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_UTILITY_UTILITY_H_