From aa844899c937bde5d2b24f276b59997e5b668bde Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 8 Aug 2019 10:56:58 -0700 Subject: Creation of LTS branch "lts_2019_08_08" - 9ee91d3e430fb33a4590486573792eb0fa146c2d Export of internal Abseil changes by Abseil Team - 8efba58a3b656e9b41fb0471ae6453425a61c520 Export of internal Abseil changes by Abseil Team - b49b8d16b67ec6912899684b732e6367f258cfdb Export of internal Abseil changes by Abseil Team - 67222ffc4c83d918ce8395aa61769eeb77df4c4d Export of internal Abseil changes by Abseil Team - c5c4db4f5191fe5e76cbf68dcc71fb28702f7d2b Export of internal Abseil changes by Abseil Team - 14550beb3b7b97195e483fb74b5efb906395c31e Export of internal Abseil changes. by Abseil Team - 52e88ee56b72cf32bc66534d942c7398ce481331 Export of internal Abseil changes. by Abseil Team - 36d37ab992038f52276ca66b9da80c1cf0f57dc2 Export of internal Abseil changes. by Abseil Team - ad1485c8986246b2ae9105e512738d0e97aec887 Export of internal Abseil changes. by Abseil Team - f3840bc5e33ce4932e35986cf3718450c6f02af2 Export of internal Abseil changes. by Abseil Team - 278b26058c036833a4f7f3047d3f4d9296527f87 Export of internal Abseil changes. by Abseil Team - c6c3c1b498e4ee939b24be59cae29d59c3863be8 Export of internal Abseil changes. by Abseil Team - 44efe96dfca674a17b45ca53fc77fb69f1e29bf4 Export of internal Abseil changes. by Abseil Team - 3c98fcc0461bd2a4b9c149d4748a7373a225cf4b Merge pull request #340 from jtsylve/macos_cxx17_fix by Matt Calabrese <38107210+mattcalabrese-google@users.noreply.github.com> - 74d91756c11bc22f9b0108b94da9326f7f9e376f Export of internal Abseil changes. by Abseil Team - e6b050212c859fbaf67abac76105da10ec348274 Export of internal Abseil changes. by Abseil Team - c964fcffac27bd4a9ff67fe393410dd1146ef8b8 Export of internal Abseil changes. by Abseil Team - 72e09a54d993b192db32be14c65adf7e9bd08c31 Export of internal Abseil changes. by Abseil Team - d65e19dfcd8697076f68598c0131c6930cdcd74d Export of internal Abseil changes. by Abseil Team - 5162fc83d2f3b79a9753ed59594c43966afdd37a Merge pull request #336 from shields/patch-2 by Shaindel Schwartz <31392632+shaindelschwartz@users.noreply.github.com> - 0389f7bf58fa41f35b3ad60be61d32f31e4f8ed6 Merge pull request #335 from shields/patch-1 by Shaindel Schwartz <31392632+shaindelschwartz@users.noreply.github.com> - e9324d926a9189e222741fce6e676f0944661a72 Export of internal Abseil changes. by Abseil Team - 43ef2148c0936ebf7cb4be6b19927a9d9d145b8f Export of internal Abseil changes. by Abseil Team - a13d3df2b3ba68aeead92e2d078fba0510d55024 Merge pull request #323 from gosnik/master by Gennadiy Rozental - 310a11865c97c5cdcc42a4ee2c2e3578423afe69 Merge pull request #324 from RasPat1/patch-1 by Gennadiy Rozental - 8f11724067248acc330b4d1f12f0c76d03f2cfb1 Export of internal Abseil changes. by Abseil Team - b1dd425423380126f6441ce4fbb6f8f6c75b793a Export of internal Abseil changes. by Abseil Team - 361cb8a9db2f2130442389fd80593255be26d681 Export of internal Abseil changes. by Abseil Team - 0238ab0a831f179518c1a814f9584e99da2d75a3 Merge pull request #321 from christoph-cullmann/c4245_fix... by Xiaoyi Zhang - 61c9bf3e3e1c28a4aa6d7f1be4b37fd473bb5529 Export of internal Abseil changes. by Abseil Team - bc9101f9982391019521161a36179b52555ed212 Merge pull request #320 from christoph-cullmann/master by Xiaoyi Zhang - 2f76a9bf50046e396138cc8eeb3cdc17b7a5ac24 Export of internal Abseil changes. by Abseil Team - 4adaf5490921f13028b55018c9f550277de5aebb Export of internal Abseil changes. by Abseil Team - 27c30ec671cb7b5ba84c4e79feff7fd0b0ac6338 Avoid undefined behavior when nullptr is passed to memcpy... by Roman Gershman - ce65f5ac3cbf897bb5e3de1a51d80fd00866abaa Export of internal Abseil changes. by Abseil Team - a18fc7461e7409c2ad64e28537261db1e02e76fa Export of internal Abseil changes. by Abseil Team - 8a394b19c149cab50534b04c5e21d42bc2217a7d Export of internal Abseil changes. by Abseil Team - daf381e8535a1f1f1b8a75966a74e7cca63dee89 Export of internal Abseil changes. by Abseil Team - fa00c321073c7ea40a4fc3dfc8a06309eae3d025 Export of internal Abseil changes. by Abseil Team - 436ba6c4a0ea3a06eca6e055f9c8d296bf3bae12 Export of internal Abseil changes. by Abseil Team - 0cbdc774b97f7e80ab60dbe2ed4eaca3b2e33fc8 Export of internal Abseil changes. by Abseil Team - 27c2f6e2f3b5929fbd322b0f0ca392eb02efd9f8 Export of internal Abseil changes. by Abseil Team - aa468ad75539619b47979911297efbb629c52e44 Export of internal Abseil changes. by Abseil Team - cd86d0d20ab167c33b23d3875db68d1d4bad3a3b Export of internal Abseil changes. by Abseil Team - 33841c5c963aa9c3f096ef8e6c1e71624b941940 Export of internal Abseil changes. by Abseil Team - ca3f87560a0eef716195cadf66dc6b938a579ec6 Export of internal Abseil changes. by Abseil Team - d902eb869bcfacc1bad14933ed9af4bed006d481 Export of internal Abseil changes. by Abseil Team - a02f62f456f2c4a7ecf2be3104fe0c6e16fbad9a Export of internal Abseil changes. by Abseil Team - 0b545b460141b882b244a1efcef7621d59278160 Export of internal Abseil changes. by Abseil Team - dbae8764fbd429bf7d7745e24bcf73962177a7c0 Export of internal Abseil changes. by Abseil Team - 044da8a29c923506af0f0b46bc46f43c1e1300b5 Export of internal Abseil changes. by Abseil Team - 6cc6ac44e065b9e8975fadfd6ccb99cbcf89aac4 Export of internal Abseil changes. by Abseil Team - 666fc1266bccfd8e6eaaa084e7b42580bb8eb199 Export of internal Abseil changes. by Abseil Team - 93dfcf74cb5fccae3da07897d8613ae6cab958a0 Export of internal Abseil changes. by Abseil Team - 2c8421e1c6cef0da9e8a20b01c15256ec9ec116d Export of internal Abseil changes. by Abseil Team - 5b65c4af5107176555b23a638e5947686410ac1f Export of internal Abseil changes. by Abseil Team - eab2078b53c9e3d9d240135c09d27e3393acb50a Export of internal Abseil changes. by Abseil Team - 253eb7416421661873afbaa33828a850db978541 [CMake] Set correct flags for clang-cl (#278) by Loo Rong Jie - e75672f6afc7e8f23ee7b532e86d1b3b9be3984e Export of internal Abseil changes. by Abseil Team - bf29470384a101b307873b26d358433138c857fc Export of internal Abseil changes. by Abseil Team - 6fd827124facd8336981e73218997f9e73029b4f Merge pull request #280 from chiumichael/master by Derek Mauro <761129+derekmauro@users.noreply.github.com> - 7c7754fb3ed9ffb57d35fe8658f3ba4d73a31e72 Export of internal Abseil changes. by Abseil Team - 256be563447a315f2a7993ec669460ba475fa86a Export of internal Abseil changes. by Abseil Team - 88a152ae747c3c42dc9167d46c590929b048d436 Export of internal Abseil changes. by Abseil Team - c1cecb25a94c075725e9d2640f6b978a8f61957b Implement Span::first and Span::last from C++20 (#274) by Girts - 38b704384cd2f17590b3922b97744be0b43622c9 Changed HTTP URLs to HTTPS where possible (#270) by nik7273 - febc5ee6a92d0eb7dac1fceaa6c648cf6521b4dc Export of internal Abseil changes. by Abseil Team - 9fdf5e5b805412cb2a2e624d3e9a11588120465f Export of internal Abseil changes. by Abseil Team - 419f3184f8ebcdb23105295eadd2a569f3351eb9 Export of internal Abseil changes. by Abseil Team - b312c3cb53a0aad75a85ac2bf57c4a614fbd48d4 Export of internal Abseil changes. by Abseil Team - 308ce31528a7edfa39f5f6d36142278a0ae1bf45 Export of internal Abseil changes. by Abseil Team - 93d155bc4414f6c121bb1f19dba9fdb27c8943bc Export of internal Abseil changes. by Abseil Team - 426eaa4aa44e4580418bee46c1bd13911151bfb1 Export of internal Abseil changes. by Abseil Team - 2901ec32a919311384d6ad4194e2d927c06831f7 Export of internal Abseil changes. by Abseil Team - d78310fe5a82f2e0e6e16509ef8079c8d7e4674e Export of internal Abseil changes. by Abseil Team - a4cb1c8ba61531a63f9d309eea01ac1d43d8371d Export of internal Abseil changes. by Abseil Team - 540e2537b92cd4abfae6ceddfe24304345461f32 Export of internal Abseil changes. by Abseil Team - 89ea0c5ff34aaa5855cfc7aa41f323b8a0ef0ede Merge pull request #255 from uilianries/hotfix/conan by ahedberg - 5e0dcf72c64fae912184d2e0de87195fe8f0a425 Export of internal Abseil changes. by Abseil Team - 0dffca4e36791c7beeda04da10460b534283948a Export of internal Abseil changes. by Abseil Team - 6b4201f9ef650637510a21b8d6cbcc3bee4a606f Fix GCC8 warnings by Boris Staletic - 0b1e6d417b414aad9282e32e8c49c719edeb63c1 Export of internal Abseil changes. by Abseil Team - efccc502606bed768e50a6cd5806d8eb13e4e935 Export of internal Abseil changes. by Abseil Team - 5e6a78131f7bd5940218462c07d88cdefdd75dbe Export of internal Abseil changes. by Abseil Team - 5eea0f713c14ac17788b83e496f11903f8e2bbb0 Export of internal Abseil changes. by Abseil Team - 66f9becbb98ecc083f4db349b4b1e0ca9de93b15 Export of internal Abseil changes. by Abseil Team - 018b4db1d73ec8238e6dc4b17fd9e1fd7468d0ed Export of internal Abseil changes. by Abseil Team - 9449ae94397f2fd683851348e25ed8c93f75b3b9 Merge pull request #243 from ThomsonTan/FixIntrinsic by Alex Strelnikov - b16aeb6756bdab08cdf12d40baab5b51f7d15b16 Export of internal Abseil changes. by Abseil Team - 7ffbe09f3d85504bd018783bbe1e2c12992fe47c Export of internal Abseil changes. by Abseil Team - 01b471d9f3ebef27f5aaca14b66509099fa8cd6c Export of internal Abseil changes. by Abseil Team - 7bd8f36c741c7cbe311611d7981bf38ba04c6fef Export of internal Abseil changes. by Abseil Team - 968a34ffdaadd7db062a9621dfbdf8b2d16e05af Export of internal Abseil changes. by Abseil Team - 3e2e9b5557e76d098de4b8a2a659125b98ca519b Merge pull request #231 from uilianries/feature/conan by Mark Barolak - 111ca7060a6ff50115ca85b59f6b5d8c8c5e9105 Export of internal Abseil changes. by Abseil Team - 389ec3f906f018661a5308458d623d01f96d7b23 Export of internal Abseil changes. by Abseil Team - 8fbcdb90952c57828c4a9c2f6d79fcd7cae9088f Export of internal Abseil changes. by Abseil Team - 455dc17ba1af9635f0b60155bc565bc572a1e722 Export of internal Abseil changes. by Abseil Team - f197d7c72a54064cfde5a2058f1513a4a0ee36fb Export of internal Abseil changes. by Abseil Team - 284378a71b32dfb3af4e3661f585e671d1b603a3 Export of internal Abseil changes. by Abseil Team GitOrigin-RevId: 9ee91d3e430fb33a4590486573792eb0fa146c2d Change-Id: Ia06e548bc106cc9d136f6c65714be6645317aced --- .gitignore | 3 + CMake/AbseilConfigureCopts.cmake | 145 -- CMake/AbseilHelpers.cmake | 228 +-- CMake/AbseilInstallDirs.cmake | 20 + CMake/CMakeLists.txt.in | 15 - CMake/DownloadGTest.cmake | 32 - CMake/Googletest/CMakeLists.txt.in | 15 + CMake/Googletest/DownloadGTest.cmake | 32 + CMake/README.md | 20 +- CMake/abslConfig.cmake.in | 7 + CMake/install_test_project/CMakeLists.txt | 27 + CMake/install_test_project/simple.cc | 23 + CMake/install_test_project/test.sh | 144 ++ CMakeLists.txt | 105 +- LICENSE | 7 +- LTS.md | 1 + README.md | 10 +- UPGRADES.md | 17 + WORKSPACE | 6 +- absl/BUILD.bazel | 17 +- absl/CMakeLists.txt | 4 +- absl/algorithm/BUILD.bazel | 10 +- absl/algorithm/CMakeLists.txt | 6 +- absl/algorithm/algorithm.h | 8 +- absl/algorithm/algorithm_test.cc | 2 +- absl/algorithm/container.h | 18 +- absl/algorithm/container_test.cc | 15 +- absl/algorithm/equal_benchmark.cc | 2 +- absl/base/BUILD.bazel | 151 +- absl/base/CMakeLists.txt | 130 +- absl/base/attributes.h | 23 +- absl/base/bit_cast_test.cc | 6 +- absl/base/call_once.h | 8 +- absl/base/call_once_test.cc | 11 +- absl/base/casts.h | 29 +- absl/base/config.h | 46 +- absl/base/config_test.cc | 2 +- absl/base/const_init.h | 74 + absl/base/dynamic_annotations.cc | 2 +- absl/base/dynamic_annotations.h | 5 +- absl/base/exception_safety_testing_test.cc | 2 +- absl/base/inline_variable_test.cc | 6 +- absl/base/inline_variable_test_a.cc | 6 +- absl/base/inline_variable_test_b.cc | 6 +- absl/base/internal/atomic_hook.h | 6 +- absl/base/internal/atomic_hook_test.cc | 2 +- absl/base/internal/bits.h | 6 +- absl/base/internal/bits_test.cc | 2 +- absl/base/internal/cmake_thread_test.cc | 22 + absl/base/internal/cycleclock.cc | 32 +- absl/base/internal/cycleclock.h | 21 +- absl/base/internal/direct_mmap.h | 10 +- absl/base/internal/endian.h | 10 +- absl/base/internal/endian_test.cc | 6 +- absl/base/internal/exception_safety_testing.cc | 2 +- absl/base/internal/exception_safety_testing.h | 12 +- absl/base/internal/exception_testing.h | 2 +- absl/base/internal/hide_ptr.h | 6 +- absl/base/internal/identity.h | 6 +- absl/base/internal/inline_variable.h | 2 +- absl/base/internal/inline_variable_testing.h | 6 +- absl/base/internal/invoke.h | 43 +- absl/base/internal/low_level_alloc.cc | 11 +- absl/base/internal/low_level_alloc.h | 8 +- absl/base/internal/low_level_alloc_test.cc | 7 +- absl/base/internal/low_level_scheduling.h | 9 +- absl/base/internal/per_thread_tls.h | 14 +- absl/base/internal/pretty_function.h | 2 +- absl/base/internal/raw_logging.cc | 9 +- absl/base/internal/raw_logging.h | 17 +- absl/base/internal/scheduling_mode.h | 6 +- absl/base/internal/scoped_set_env.cc | 81 ++ absl/base/internal/scoped_set_env.h | 43 + absl/base/internal/scoped_set_env_test.cc | 99 ++ absl/base/internal/spinlock.cc | 6 +- absl/base/internal/spinlock.h | 13 +- absl/base/internal/spinlock_akaros.inc | 2 +- absl/base/internal/spinlock_benchmark.cc | 2 +- absl/base/internal/spinlock_linux.inc | 19 +- absl/base/internal/spinlock_posix.inc | 2 +- absl/base/internal/spinlock_wait.cc | 19 +- absl/base/internal/spinlock_wait.h | 6 +- absl/base/internal/spinlock_win32.inc | 2 +- absl/base/internal/sysinfo.cc | 6 +- absl/base/internal/sysinfo.h | 6 +- absl/base/internal/sysinfo_test.cc | 12 +- absl/base/internal/thread_annotations.h | 271 ++++ absl/base/internal/thread_identity.cc | 6 +- absl/base/internal/thread_identity.h | 11 +- absl/base/internal/thread_identity_benchmark.cc | 2 +- absl/base/internal/thread_identity_test.cc | 6 +- absl/base/internal/throw_delegate.cc | 6 +- absl/base/internal/throw_delegate.h | 6 +- absl/base/internal/tsan_mutex_interface.h | 2 +- absl/base/internal/unaligned_access.h | 181 +-- absl/base/internal/unscaledcycleclock.cc | 6 +- absl/base/internal/unscaledcycleclock.h | 11 +- absl/base/invoke_test.cc | 27 +- absl/base/log_severity.cc | 27 + absl/base/log_severity.h | 12 +- absl/base/log_severity_test.cc | 43 + absl/base/macros.h | 19 +- absl/base/optimization.h | 28 +- absl/base/policy_checks.h | 2 +- absl/base/port.h | 2 +- absl/base/raw_logging_test.cc | 2 +- absl/base/spinlock_test_common.cc | 10 +- absl/base/thread_annotations.h | 186 +-- absl/base/throw_delegate_test.cc | 2 +- absl/compiler_config_setting.bzl | 43 +- absl/container/BUILD.bazel | 197 ++- absl/container/CMakeLists.txt | 190 ++- absl/container/fixed_array.h | 7 +- absl/container/fixed_array_benchmark.cc | 11 +- .../container/fixed_array_exception_safety_test.cc | 11 +- absl/container/fixed_array_test.cc | 77 +- absl/container/flat_hash_map.h | 25 +- absl/container/flat_hash_map_test.cc | 32 +- absl/container/flat_hash_set.h | 20 +- absl/container/flat_hash_set_test.cc | 14 +- absl/container/inlined_vector.h | 1504 ++++++-------------- absl/container/inlined_vector_benchmark.cc | 561 +++++++- .../inlined_vector_exception_safety_test.cc | 489 +++++++ absl/container/inlined_vector_test.cc | 230 ++- absl/container/internal/common.h | 198 +++ absl/container/internal/compressed_tuple.h | 162 ++- absl/container/internal/compressed_tuple_test.cc | 269 +++- absl/container/internal/container_memory.h | 69 +- absl/container/internal/container_memory_test.cc | 6 +- absl/container/internal/counting_allocator.h | 81 ++ absl/container/internal/hash_function_defaults.h | 11 +- .../internal/hash_function_defaults_test.cc | 28 +- absl/container/internal/hash_generator_testing.cc | 6 +- absl/container/internal/hash_generator_testing.h | 6 +- absl/container/internal/hash_policy_testing.h | 6 +- .../container/internal/hash_policy_testing_test.cc | 6 +- absl/container/internal/hash_policy_traits.h | 6 +- absl/container/internal/hash_policy_traits_test.cc | 6 +- absl/container/internal/hashtable_debug.h | 8 +- absl/container/internal/hashtable_debug_hooks.h | 6 +- absl/container/internal/hashtablez_sampler.cc | 310 ++++ absl/container/internal/hashtablez_sampler.h | 290 ++++ .../hashtablez_sampler_force_weak_definition.cc | 29 + absl/container/internal/hashtablez_sampler_test.cc | 357 +++++ absl/container/internal/have_sse.h | 49 + absl/container/internal/inlined_vector.h | 895 ++++++++++++ absl/container/internal/layout.h | 9 +- absl/container/internal/layout_test.cc | 34 +- absl/container/internal/node_hash_policy.h | 6 +- absl/container/internal/node_hash_policy_test.cc | 6 +- absl/container/internal/raw_hash_map.h | 21 +- absl/container/internal/raw_hash_set.cc | 6 +- absl/container/internal/raw_hash_set.h | 392 ++--- .../internal/raw_hash_set_allocator_test.cc | 6 +- absl/container/internal/raw_hash_set_test.cc | 304 ++-- absl/container/internal/test_instance_tracker.cc | 6 +- absl/container/internal/test_instance_tracker.h | 18 +- .../internal/test_instance_tracker_test.cc | 4 +- absl/container/internal/tracked.h | 6 +- .../internal/unordered_map_constructor_test.h | 148 +- .../container/internal/unordered_map_lookup_test.h | 8 +- .../internal/unordered_map_members_test.h | 87 ++ .../internal/unordered_map_modifiers_test.h | 8 +- absl/container/internal/unordered_map_test.cc | 14 +- .../internal/unordered_set_constructor_test.h | 155 +- .../container/internal/unordered_set_lookup_test.h | 8 +- .../internal/unordered_set_members_test.h | 86 ++ .../internal/unordered_set_modifiers_test.h | 8 +- absl/container/internal/unordered_set_test.cc | 24 +- absl/container/node_hash_map.h | 6 +- absl/container/node_hash_map_test.cc | 14 +- absl/container/node_hash_set.h | 6 +- absl/container/node_hash_set_test.cc | 16 +- absl/copts.bzl | 170 --- absl/copts/AbseilConfigureCopts.cmake | 60 + absl/copts/GENERATED_AbseilCopts.cmake | 236 +++ absl/copts/GENERATED_copts.bzl | 237 +++ absl/copts/configure_copts.bzl | 89 ++ absl/copts/copts.py | 214 +++ absl/copts/generate_copts.py | 109 ++ absl/debugging/BUILD.bazel | 47 +- absl/debugging/CMakeLists.txt | 28 +- absl/debugging/failure_signal_handler.cc | 6 +- absl/debugging/failure_signal_handler.h | 6 +- absl/debugging/failure_signal_handler_test.cc | 11 +- absl/debugging/internal/address_is_readable.cc | 10 +- absl/debugging/internal/address_is_readable.h | 7 +- absl/debugging/internal/demangle.cc | 16 +- absl/debugging/internal/demangle.h | 6 +- absl/debugging/internal/demangle_test.cc | 12 +- absl/debugging/internal/elf_mem_image.cc | 6 +- absl/debugging/internal/elf_mem_image.h | 8 +- absl/debugging/internal/examine_stack.cc | 6 +- absl/debugging/internal/examine_stack.h | 6 +- absl/debugging/internal/stack_consumption.cc | 6 +- absl/debugging/internal/stack_consumption.h | 6 +- absl/debugging/internal/stack_consumption_test.cc | 6 +- absl/debugging/internal/stacktrace_aarch64-inl.inc | 4 +- absl/debugging/internal/stacktrace_arm-inl.inc | 4 +- absl/debugging/internal/stacktrace_config.h | 2 +- absl/debugging/internal/stacktrace_generic-inl.inc | 40 +- absl/debugging/internal/stacktrace_powerpc-inl.inc | 12 +- .../internal/stacktrace_unimplemented-inl.inc | 4 +- absl/debugging/internal/stacktrace_win32-inl.inc | 8 +- absl/debugging/internal/stacktrace_x86-inl.inc | 7 +- absl/debugging/internal/symbolize.h | 17 +- absl/debugging/internal/vdso_support.cc | 6 +- absl/debugging/internal/vdso_support.h | 6 +- absl/debugging/leak_check.cc | 11 +- absl/debugging/leak_check.h | 6 +- absl/debugging/leak_check_disable.cc | 2 +- absl/debugging/leak_check_fail_test.cc | 2 +- absl/debugging/leak_check_test.cc | 2 +- absl/debugging/stacktrace.cc | 7 +- absl/debugging/stacktrace.h | 6 +- absl/debugging/symbolize.cc | 2 +- absl/debugging/symbolize.h | 6 +- absl/debugging/symbolize_elf.inc | 72 +- absl/debugging/symbolize_test.cc | 17 +- absl/debugging/symbolize_unimplemented.inc | 9 +- absl/debugging/symbolize_win32.inc | 14 +- absl/flags/BUILD.bazel | 384 +++++ absl/flags/CMakeLists.txt | 346 +++++ absl/flags/config.h | 48 + absl/flags/config_test.cc | 61 + absl/flags/declare.h | 60 + absl/flags/flag.cc | 46 + absl/flags/flag.h | 259 ++++ absl/flags/flag_test.cc | 482 +++++++ absl/flags/flag_test_defs.cc | 22 + absl/flags/internal/commandlineflag.cc | 496 +++++++ absl/flags/internal/commandlineflag.h | 385 +++++ absl/flags/internal/commandlineflag_test.cc | 196 +++ absl/flags/internal/flag.h | 123 ++ absl/flags/internal/parse.h | 50 + absl/flags/internal/path_util.h | 62 + absl/flags/internal/path_util_test.cc | 46 + absl/flags/internal/program_name.cc | 55 + absl/flags/internal/program_name.h | 49 + absl/flags/internal/program_name_test.cc | 60 + absl/flags/internal/registry.cc | 445 ++++++ absl/flags/internal/registry.h | 169 +++ absl/flags/internal/type_erased.cc | 108 ++ absl/flags/internal/type_erased.h | 99 ++ absl/flags/internal/type_erased_test.cc | 147 ++ absl/flags/internal/usage.cc | 385 +++++ absl/flags/internal/usage.h | 80 ++ absl/flags/internal/usage_test.cc | 404 ++++++ absl/flags/marshalling.cc | 191 +++ absl/flags/marshalling.h | 255 ++++ absl/flags/marshalling_test.cc | 899 ++++++++++++ absl/flags/parse.cc | 755 ++++++++++ absl/flags/parse.h | 60 + absl/flags/parse_test.cc | 858 +++++++++++ absl/flags/usage.cc | 58 + absl/flags/usage.h | 42 + absl/flags/usage_config.cc | 154 ++ absl/flags/usage_config.h | 133 ++ absl/flags/usage_config_test.cc | 198 +++ absl/hash/BUILD.bazel | 11 +- absl/hash/CMakeLists.txt | 8 +- absl/hash/hash.h | 42 +- absl/hash/hash_test.cc | 404 +++++- absl/hash/hash_testing.h | 10 +- absl/hash/internal/city.cc | 6 +- absl/hash/internal/city.h | 9 +- absl/hash/internal/city_test.cc | 6 +- absl/hash/internal/hash.cc | 6 +- absl/hash/internal/hash.h | 205 ++- absl/hash/internal/print_hash_of.cc | 2 +- absl/hash/internal/spy_hash_state.h | 12 +- absl/memory/BUILD.bazel | 9 +- absl/memory/CMakeLists.txt | 2 +- absl/memory/memory.h | 25 +- absl/memory/memory_exception_safety_test.cc | 6 +- absl/memory/memory_test.cc | 6 +- absl/meta/BUILD.bazel | 6 +- absl/meta/CMakeLists.txt | 60 +- absl/meta/type_traits.h | 314 +++- absl/meta/type_traits_test.cc | 425 +++++- absl/numeric/BUILD.bazel | 8 +- absl/numeric/CMakeLists.txt | 78 +- absl/numeric/int128.cc | 30 +- absl/numeric/int128.h | 42 +- absl/numeric/int128_benchmark.cc | 2 +- absl/numeric/int128_have_intrinsic.inc | 4 +- absl/numeric/int128_no_intrinsic.inc | 4 +- absl/numeric/int128_stream_test.cc | 2 +- absl/numeric/int128_test.cc | 45 +- absl/random/BUILD.bazel | 390 +++++ absl/random/CMakeLists.txt | 1034 ++++++++++++++ absl/random/benchmarks.cc | 383 +++++ absl/random/bernoulli_distribution.h | 200 +++ absl/random/bernoulli_distribution_test.cc | 213 +++ absl/random/beta_distribution.h | 416 ++++++ absl/random/beta_distribution_test.cc | 614 ++++++++ absl/random/discrete_distribution.cc | 98 ++ absl/random/discrete_distribution.h | 247 ++++ absl/random/discrete_distribution_test.cc | 246 ++++ absl/random/distribution_format_traits.h | 251 ++++ absl/random/distributions.h | 444 ++++++ absl/random/distributions_test.cc | 494 +++++++ absl/random/examples_test.cc | 99 ++ absl/random/exponential_distribution.h | 159 +++ absl/random/exponential_distribution_test.cc | 422 ++++++ absl/random/gaussian_distribution.cc | 104 ++ absl/random/gaussian_distribution.h | 262 ++++ absl/random/gaussian_distribution_test.cc | 573 ++++++++ absl/random/generators_test.cc | 179 +++ absl/random/internal/BUILD.bazel | 658 +++++++++ absl/random/internal/chi_square.cc | 232 +++ absl/random/internal/chi_square.h | 87 ++ absl/random/internal/chi_square_test.cc | 365 +++++ absl/random/internal/distribution_caller.h | 58 + absl/random/internal/distribution_impl.h | 262 ++++ absl/random/internal/distribution_impl_test.cc | 506 +++++++ absl/random/internal/distribution_test_util.cc | 418 ++++++ absl/random/internal/distribution_test_util.h | 113 ++ .../random/internal/distribution_test_util_test.cc | 193 +++ absl/random/internal/distributions.h | 84 ++ absl/random/internal/explicit_seed_seq.h | 89 ++ absl/random/internal/explicit_seed_seq_test.cc | 204 +++ absl/random/internal/fast_uniform_bits.h | 262 ++++ absl/random/internal/fast_uniform_bits_test.cc | 274 ++++ absl/random/internal/fastmath.h | 74 + absl/random/internal/fastmath_test.cc | 110 ++ .../internal/gaussian_distribution_gentables.cc | 147 ++ absl/random/internal/iostream_state_saver.h | 245 ++++ absl/random/internal/iostream_state_saver_test.cc | 369 +++++ absl/random/internal/named_generator.cc | 30 + absl/random/internal/nanobenchmark.cc | 812 +++++++++++ absl/random/internal/nanobenchmark.h | 170 +++ absl/random/internal/nanobenchmark_test.cc | 77 + absl/random/internal/nonsecure_base.h | 150 ++ absl/random/internal/nonsecure_base_test.cc | 244 ++++ absl/random/internal/pcg_engine.h | 307 ++++ absl/random/internal/pcg_engine_test.cc | 638 +++++++++ absl/random/internal/platform.h | 170 +++ absl/random/internal/pool_urbg.cc | 254 ++++ absl/random/internal/pool_urbg.h | 131 ++ absl/random/internal/pool_urbg_test.cc | 182 +++ absl/random/internal/randen-keys.inc | 207 +++ absl/random/internal/randen.cc | 91 ++ absl/random/internal/randen.h | 102 ++ absl/random/internal/randen_benchmarks.cc | 174 +++ absl/random/internal/randen_detect.cc | 221 +++ absl/random/internal/randen_detect.h | 31 + absl/random/internal/randen_engine.h | 230 +++ absl/random/internal/randen_engine_test.cc | 656 +++++++++ absl/random/internal/randen_hwaes.cc | 704 +++++++++ absl/random/internal/randen_hwaes.h | 48 + absl/random/internal/randen_hwaes_test.cc | 102 ++ absl/random/internal/randen_slow.cc | 514 +++++++ absl/random/internal/randen_slow.h | 45 + absl/random/internal/randen_slow_test.cc | 61 + absl/random/internal/randen_test.cc | 70 + absl/random/internal/randen_traits.h | 61 + absl/random/internal/salted_seed_seq.h | 167 +++ absl/random/internal/salted_seed_seq_test.cc | 168 +++ absl/random/internal/seed_material.cc | 207 +++ absl/random/internal/seed_material.h | 104 ++ absl/random/internal/seed_material_test.cc | 202 +++ .../internal/seed_salting_sequence_generator.cc | 30 + ...ed_salting_sequence_generator_empty_sequence.cc | 30 + absl/random/internal/sequence_urbg.h | 58 + absl/random/internal/traits.h | 101 ++ absl/random/internal/traits_test.cc | 126 ++ absl/random/internal/uniform_helper.h | 152 ++ absl/random/log_uniform_int_distribution.h | 252 ++++ absl/random/log_uniform_int_distribution_test.cc | 277 ++++ absl/random/poisson_distribution.h | 256 ++++ absl/random/poisson_distribution_test.cc | 565 ++++++++ absl/random/random.h | 189 +++ absl/random/seed_gen_exception.cc | 46 + absl/random/seed_gen_exception.h | 53 + absl/random/seed_sequences.cc | 29 + absl/random/seed_sequences.h | 110 ++ absl/random/seed_sequences_test.cc | 127 ++ absl/random/uniform_int_distribution.h | 275 ++++ absl/random/uniform_int_distribution_test.cc | 250 ++++ absl/random/uniform_real_distribution.h | 195 +++ absl/random/uniform_real_distribution_test.cc | 322 +++++ absl/random/zipf_distribution.h | 271 ++++ absl/random/zipf_distribution_test.cc | 423 ++++++ absl/strings/BUILD.bazel | 13 +- absl/strings/CMakeLists.txt | 793 ++++++----- absl/strings/ascii.cc | 6 +- absl/strings/ascii.h | 6 +- absl/strings/ascii_benchmark.cc | 2 +- absl/strings/ascii_test.cc | 2 +- absl/strings/charconv.cc | 13 +- absl/strings/charconv.h | 10 +- absl/strings/charconv_benchmark.cc | 2 +- absl/strings/charconv_test.cc | 5 +- absl/strings/escaping.cc | 54 +- absl/strings/escaping.h | 24 +- absl/strings/escaping_benchmark.cc | 2 +- absl/strings/escaping_test.cc | 80 +- absl/strings/internal/char_map.h | 6 +- absl/strings/internal/char_map_benchmark.cc | 2 +- absl/strings/internal/char_map_test.cc | 2 +- absl/strings/internal/charconv_bigint.cc | 6 +- absl/strings/internal/charconv_bigint.h | 20 +- absl/strings/internal/charconv_bigint_test.cc | 6 +- absl/strings/internal/charconv_parse.cc | 6 +- absl/strings/internal/charconv_parse.h | 6 +- absl/strings/internal/charconv_parse_test.cc | 2 +- absl/strings/internal/escaping_test_common.h | 6 +- absl/strings/internal/memutil.cc | 6 +- absl/strings/internal/memutil.h | 6 +- absl/strings/internal/memutil_benchmark.cc | 2 +- absl/strings/internal/memutil_test.cc | 2 +- absl/strings/internal/numbers_test_common.h | 6 +- absl/strings/internal/ostringstream.cc | 6 +- absl/strings/internal/ostringstream.h | 22 +- absl/strings/internal/ostringstream_benchmark.cc | 2 +- absl/strings/internal/ostringstream_test.cc | 2 +- absl/strings/internal/pow10_helper.cc | 6 +- absl/strings/internal/pow10_helper.h | 12 +- absl/strings/internal/pow10_helper_test.cc | 6 +- absl/strings/internal/resize_uninitialized.h | 47 +- absl/strings/internal/resize_uninitialized_test.cc | 26 +- absl/strings/internal/stl_type_traits.h | 6 +- absl/strings/internal/str_format/arg.cc | 4 +- absl/strings/internal/str_format/arg.h | 16 +- absl/strings/internal/str_format/arg_test.cc | 6 +- absl/strings/internal/str_format/bind.cc | 15 +- absl/strings/internal/str_format/bind.h | 32 +- absl/strings/internal/str_format/bind_test.cc | 28 +- absl/strings/internal/str_format/checker.h | 4 +- absl/strings/internal/str_format/checker_test.cc | 70 +- absl/strings/internal/str_format/convert_test.cc | 27 +- absl/strings/internal/str_format/extension.cc | 6 +- absl/strings/internal/str_format/extension.h | 9 +- absl/strings/internal/str_format/extension_test.cc | 3 +- .../internal/str_format/float_conversion.cc | 8 +- .../strings/internal/str_format/float_conversion.h | 4 +- absl/strings/internal/str_format/output.cc | 6 +- absl/strings/internal/str_format/output.h | 6 +- absl/strings/internal/str_format/output_test.cc | 13 +- absl/strings/internal/str_format/parser.cc | 150 +- absl/strings/internal/str_format/parser.h | 132 +- absl/strings/internal/str_format/parser_test.cc | 31 +- absl/strings/internal/str_join_internal.h | 19 +- absl/strings/internal/str_split_internal.h | 18 +- absl/strings/internal/utf8.cc | 6 +- absl/strings/internal/utf8.h | 7 +- absl/strings/internal/utf8_test.cc | 19 +- absl/strings/match.cc | 19 +- absl/strings/match.h | 13 +- absl/strings/match_test.cc | 6 +- absl/strings/numbers.cc | 67 +- absl/strings/numbers.h | 39 +- absl/strings/numbers_benchmark.cc | 2 +- absl/strings/numbers_test.cc | 15 +- absl/strings/str_cat.cc | 26 +- absl/strings/str_cat.h | 44 +- absl/strings/str_cat_benchmark.cc | 2 +- absl/strings/str_cat_test.cc | 55 +- absl/strings/str_format.h | 67 +- absl/strings/str_format_test.cc | 57 +- absl/strings/str_join.h | 84 +- absl/strings/str_join_benchmark.cc | 5 +- absl/strings/str_join_test.cc | 14 +- absl/strings/str_replace.cc | 11 +- absl/strings/str_replace.h | 46 +- absl/strings/str_replace_benchmark.cc | 14 +- absl/strings/str_replace_test.cc | 4 +- absl/strings/str_split.cc | 6 +- absl/strings/str_split.h | 51 +- absl/strings/str_split_benchmark.cc | 28 +- absl/strings/str_split_test.cc | 62 +- absl/strings/string_view.cc | 20 +- absl/strings/string_view.h | 71 +- absl/strings/string_view_benchmark.cc | 2 +- absl/strings/string_view_test.cc | 24 +- absl/strings/strip.h | 6 +- absl/strings/strip_test.cc | 5 +- absl/strings/substitute.cc | 6 +- absl/strings/substitute.h | 91 +- absl/strings/substitute_test.cc | 14 +- absl/synchronization/BUILD.bazel | 37 +- absl/synchronization/CMakeLists.txt | 273 ++-- absl/synchronization/barrier.cc | 6 +- absl/synchronization/barrier.h | 6 +- absl/synchronization/barrier_test.cc | 2 +- absl/synchronization/blocking_counter.cc | 6 +- absl/synchronization/blocking_counter.h | 6 +- absl/synchronization/blocking_counter_test.cc | 6 +- .../internal/create_thread_identity.cc | 35 +- .../internal/create_thread_identity.h | 7 +- absl/synchronization/internal/graphcycles.cc | 6 +- absl/synchronization/internal/graphcycles.h | 6 +- .../internal/graphcycles_benchmark.cc | 2 +- absl/synchronization/internal/graphcycles_test.cc | 6 +- absl/synchronization/internal/kernel_timeout.h | 12 +- absl/synchronization/internal/mutex_nonprod.cc | 6 +- absl/synchronization/internal/mutex_nonprod.inc | 7 +- absl/synchronization/internal/per_thread_sem.cc | 7 +- absl/synchronization/internal/per_thread_sem.h | 7 +- .../internal/per_thread_sem_test.cc | 23 +- absl/synchronization/internal/thread_pool.h | 9 +- absl/synchronization/internal/waiter.cc | 65 +- absl/synchronization/internal/waiter.h | 26 +- absl/synchronization/lifetime_test.cc | 50 +- absl/synchronization/mutex.cc | 148 +- absl/synchronization/mutex.h | 32 +- absl/synchronization/mutex_benchmark.cc | 2 +- absl/synchronization/mutex_test.cc | 45 +- absl/synchronization/notification.cc | 6 +- absl/synchronization/notification.h | 7 +- absl/synchronization/notification_test.cc | 16 +- absl/time/BUILD.bazel | 10 +- absl/time/CMakeLists.txt | 167 ++- absl/time/civil_time.cc | 21 +- absl/time/civil_time.h | 25 +- absl/time/civil_time_benchmark.cc | 2 +- absl/time/civil_time_test.cc | 16 +- absl/time/clock.cc | 20 +- absl/time/clock.h | 6 +- absl/time/clock_benchmark.cc | 4 +- absl/time/clock_test.cc | 2 +- absl/time/duration.cc | 19 +- absl/time/duration_benchmark.cc | 3 +- absl/time/duration_test.cc | 8 +- absl/time/format.cc | 17 +- absl/time/format_benchmark.cc | 2 +- absl/time/format_test.cc | 11 +- absl/time/internal/cctz/BUILD.bazel | 27 +- absl/time/internal/cctz/include/cctz/civil_time.h | 14 +- .../internal/cctz/include/cctz/civil_time_detail.h | 68 +- absl/time/internal/cctz/include/cctz/time_zone.h | 8 +- .../internal/cctz/include/cctz/zone_info_source.h | 10 +- absl/time/internal/cctz/src/cctz_benchmark.cc | 53 +- absl/time/internal/cctz/src/civil_time_detail.cc | 6 +- absl/time/internal/cctz/src/civil_time_test.cc | 22 +- absl/time/internal/cctz/src/time_zone_fixed.cc | 22 +- absl/time/internal/cctz/src/time_zone_fixed.h | 6 +- absl/time/internal/cctz/src/time_zone_format.cc | 104 +- .../internal/cctz/src/time_zone_format_test.cc | 367 +++-- absl/time/internal/cctz/src/time_zone_if.cc | 6 +- absl/time/internal/cctz/src/time_zone_if.h | 6 +- absl/time/internal/cctz/src/time_zone_impl.cc | 6 +- absl/time/internal/cctz/src/time_zone_impl.h | 6 +- absl/time/internal/cctz/src/time_zone_info.cc | 29 +- absl/time/internal/cctz/src/time_zone_info.h | 8 +- absl/time/internal/cctz/src/time_zone_libc.cc | 109 +- absl/time/internal/cctz/src/time_zone_libc.h | 6 +- absl/time/internal/cctz/src/time_zone_lookup.cc | 41 +- .../internal/cctz/src/time_zone_lookup_test.cc | 33 +- absl/time/internal/cctz/src/time_zone_posix.cc | 16 +- absl/time/internal/cctz/src/time_zone_posix.h | 6 +- absl/time/internal/cctz/src/tzfile.h | 20 +- absl/time/internal/cctz/src/zone_info_source.cc | 34 +- absl/time/internal/cctz/testdata/version | 2 +- .../internal/cctz/testdata/zoneinfo/Africa/Abidjan | Bin 156 -> 148 bytes .../internal/cctz/testdata/zoneinfo/Africa/Accra | Bin 828 -> 816 bytes .../cctz/testdata/zoneinfo/Africa/Addis_Ababa | Bin 271 -> 251 bytes .../internal/cctz/testdata/zoneinfo/Africa/Algiers | Bin 751 -> 735 bytes .../internal/cctz/testdata/zoneinfo/Africa/Asmara | Bin 271 -> 251 bytes .../internal/cctz/testdata/zoneinfo/Africa/Asmera | Bin 271 -> 251 bytes .../internal/cctz/testdata/zoneinfo/Africa/Bamako | Bin 156 -> 148 bytes .../internal/cctz/testdata/zoneinfo/Africa/Bangui | Bin 157 -> 149 bytes .../internal/cctz/testdata/zoneinfo/Africa/Banjul | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/Africa/Blantyre | Bin 157 -> 149 bytes .../cctz/testdata/zoneinfo/Africa/Brazzaville | Bin 157 -> 149 bytes .../cctz/testdata/zoneinfo/Africa/Bujumbura | Bin 157 -> 149 bytes .../internal/cctz/testdata/zoneinfo/Africa/Cairo | Bin 1963 -> 1955 bytes .../cctz/testdata/zoneinfo/Africa/Casablanca | Bin 1533 -> 2429 bytes .../internal/cctz/testdata/zoneinfo/Africa/Ceuta | Bin 2050 -> 2036 bytes .../internal/cctz/testdata/zoneinfo/Africa/Conakry | Bin 156 -> 148 bytes .../internal/cctz/testdata/zoneinfo/Africa/Dakar | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/Africa/Dar_es_Salaam | Bin 271 -> 251 bytes .../cctz/testdata/zoneinfo/Africa/Djibouti | Bin 271 -> 251 bytes .../internal/cctz/testdata/zoneinfo/Africa/Douala | Bin 157 -> 149 bytes .../cctz/testdata/zoneinfo/Africa/El_Aaiun | Bin 1403 -> 2295 bytes .../cctz/testdata/zoneinfo/Africa/Freetown | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/Africa/Gaborone | Bin 157 -> 149 bytes .../internal/cctz/testdata/zoneinfo/Africa/Harare | Bin 157 -> 149 bytes .../cctz/testdata/zoneinfo/Africa/Johannesburg | Bin 262 -> 246 bytes .../internal/cctz/testdata/zoneinfo/Africa/Juba | Bin 669 -> 653 bytes .../internal/cctz/testdata/zoneinfo/Africa/Kampala | Bin 271 -> 251 bytes .../cctz/testdata/zoneinfo/Africa/Khartoum | Bin 699 -> 679 bytes .../internal/cctz/testdata/zoneinfo/Africa/Kigali | Bin 157 -> 149 bytes .../cctz/testdata/zoneinfo/Africa/Kinshasa | Bin 157 -> 149 bytes .../internal/cctz/testdata/zoneinfo/Africa/Lagos | Bin 157 -> 149 bytes .../cctz/testdata/zoneinfo/Africa/Libreville | Bin 157 -> 149 bytes .../internal/cctz/testdata/zoneinfo/Africa/Lome | Bin 156 -> 148 bytes .../internal/cctz/testdata/zoneinfo/Africa/Luanda | Bin 157 -> 149 bytes .../cctz/testdata/zoneinfo/Africa/Lubumbashi | Bin 157 -> 149 bytes .../internal/cctz/testdata/zoneinfo/Africa/Lusaka | Bin 157 -> 149 bytes .../internal/cctz/testdata/zoneinfo/Africa/Malabo | Bin 157 -> 149 bytes .../internal/cctz/testdata/zoneinfo/Africa/Maputo | Bin 157 -> 149 bytes .../internal/cctz/testdata/zoneinfo/Africa/Maseru | Bin 262 -> 246 bytes .../internal/cctz/testdata/zoneinfo/Africa/Mbabane | Bin 262 -> 246 bytes .../cctz/testdata/zoneinfo/Africa/Mogadishu | Bin 271 -> 251 bytes .../cctz/testdata/zoneinfo/Africa/Monrovia | Bin 224 -> 208 bytes .../internal/cctz/testdata/zoneinfo/Africa/Nairobi | Bin 271 -> 251 bytes .../cctz/testdata/zoneinfo/Africa/Ndjamena | Bin 211 -> 199 bytes .../internal/cctz/testdata/zoneinfo/Africa/Niamey | Bin 157 -> 149 bytes .../cctz/testdata/zoneinfo/Africa/Nouakchott | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/Africa/Ouagadougou | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/Africa/Porto-Novo | Bin 157 -> 149 bytes .../cctz/testdata/zoneinfo/Africa/Sao_Tome | Bin 225 -> 254 bytes .../cctz/testdata/zoneinfo/Africa/Timbuktu | Bin 156 -> 148 bytes .../internal/cctz/testdata/zoneinfo/Africa/Tripoli | Bin 641 -> 625 bytes .../internal/cctz/testdata/zoneinfo/Africa/Tunis | Bin 701 -> 689 bytes .../cctz/testdata/zoneinfo/Africa/Windhoek | Bin 979 -> 955 bytes .../cctz/testdata/zoneinfo/America/Anguilla | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/Antigua | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/Araguaina | Bin 896 -> 884 bytes .../zoneinfo/America/Argentina/Buenos_Aires | Bin 1100 -> 1076 bytes .../testdata/zoneinfo/America/Argentina/Catamarca | Bin 1100 -> 1076 bytes .../zoneinfo/America/Argentina/ComodRivadavia | Bin 1100 -> 1076 bytes .../testdata/zoneinfo/America/Argentina/Cordoba | Bin 1100 -> 1076 bytes .../cctz/testdata/zoneinfo/America/Argentina/Jujuy | Bin 1072 -> 1048 bytes .../testdata/zoneinfo/America/Argentina/La_Rioja | Bin 1114 -> 1090 bytes .../testdata/zoneinfo/America/Argentina/Mendoza | Bin 1100 -> 1076 bytes .../zoneinfo/America/Argentina/Rio_Gallegos | Bin 1100 -> 1076 bytes .../cctz/testdata/zoneinfo/America/Argentina/Salta | Bin 1072 -> 1048 bytes .../testdata/zoneinfo/America/Argentina/San_Juan | Bin 1114 -> 1090 bytes .../testdata/zoneinfo/America/Argentina/San_Luis | Bin 1130 -> 1102 bytes .../testdata/zoneinfo/America/Argentina/Tucuman | Bin 1128 -> 1104 bytes .../testdata/zoneinfo/America/Argentina/Ushuaia | Bin 1100 -> 1076 bytes .../internal/cctz/testdata/zoneinfo/America/Aruba | Bin 198 -> 186 bytes .../cctz/testdata/zoneinfo/America/Asuncion | Bin 2068 -> 2044 bytes .../internal/cctz/testdata/zoneinfo/America/Bahia | Bin 1036 -> 1024 bytes .../cctz/testdata/zoneinfo/America/Bahia_Banderas | Bin 1574 -> 1546 bytes .../cctz/testdata/zoneinfo/America/Barbados | Bin 330 -> 314 bytes .../internal/cctz/testdata/zoneinfo/America/Belem | Bin 588 -> 576 bytes .../internal/cctz/testdata/zoneinfo/America/Belize | Bin 964 -> 948 bytes .../cctz/testdata/zoneinfo/America/Boa_Vista | Bin 644 -> 632 bytes .../internal/cctz/testdata/zoneinfo/America/Bogota | Bin 262 -> 246 bytes .../cctz/testdata/zoneinfo/America/Buenos_Aires | Bin 1100 -> 1076 bytes .../cctz/testdata/zoneinfo/America/Campo_Grande | Bin 2002 -> 1444 bytes .../internal/cctz/testdata/zoneinfo/America/Cancun | Bin 802 -> 782 bytes .../cctz/testdata/zoneinfo/America/Caracas | Bin 280 -> 264 bytes .../cctz/testdata/zoneinfo/America/Catamarca | Bin 1100 -> 1076 bytes .../cctz/testdata/zoneinfo/America/Cayenne | Bin 210 -> 198 bytes .../internal/cctz/testdata/zoneinfo/America/Cayman | Bin 194 -> 182 bytes .../cctz/testdata/zoneinfo/America/Chihuahua | Bin 1508 -> 1484 bytes .../cctz/testdata/zoneinfo/America/Cordoba | Bin 1100 -> 1076 bytes .../cctz/testdata/zoneinfo/America/Costa_Rica | Bin 332 -> 316 bytes .../cctz/testdata/zoneinfo/America/Creston | Bin 224 -> 208 bytes .../internal/cctz/testdata/zoneinfo/America/Cuiaba | Bin 1974 -> 1416 bytes .../cctz/testdata/zoneinfo/America/Curacao | Bin 198 -> 186 bytes .../cctz/testdata/zoneinfo/America/Dominica | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/Eirunepe | Bin 676 -> 656 bytes .../cctz/testdata/zoneinfo/America/El_Salvador | Bin 236 -> 224 bytes .../cctz/testdata/zoneinfo/America/Fortaleza | Bin 728 -> 716 bytes .../cctz/testdata/zoneinfo/America/Grand_Turk | Bin 1872 -> 1848 bytes .../cctz/testdata/zoneinfo/America/Grenada | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/Guadeloupe | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/Guatemala | Bin 292 -> 280 bytes .../cctz/testdata/zoneinfo/America/Guayaquil | Bin 262 -> 246 bytes .../internal/cctz/testdata/zoneinfo/America/Guyana | Bin 252 -> 236 bytes .../internal/cctz/testdata/zoneinfo/America/Havana | Bin 2428 -> 2416 bytes .../cctz/testdata/zoneinfo/America/Hermosillo | Bin 440 -> 416 bytes .../internal/cctz/testdata/zoneinfo/America/Inuvik | Bin 1914 -> 1894 bytes .../cctz/testdata/zoneinfo/America/Jamaica | Bin 498 -> 482 bytes .../internal/cctz/testdata/zoneinfo/America/Jujuy | Bin 1072 -> 1048 bytes .../cctz/testdata/zoneinfo/America/Kralendijk | Bin 198 -> 186 bytes .../internal/cctz/testdata/zoneinfo/America/La_Paz | Bin 248 -> 232 bytes .../internal/cctz/testdata/zoneinfo/America/Lima | Bin 422 -> 406 bytes .../cctz/testdata/zoneinfo/America/Lower_Princes | Bin 198 -> 186 bytes .../internal/cctz/testdata/zoneinfo/America/Maceio | Bin 756 -> 744 bytes .../cctz/testdata/zoneinfo/America/Managua | Bin 454 -> 430 bytes .../internal/cctz/testdata/zoneinfo/America/Manaus | Bin 616 -> 604 bytes .../cctz/testdata/zoneinfo/America/Marigot | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/Martinique | Bin 248 -> 232 bytes .../cctz/testdata/zoneinfo/America/Matamoros | Bin 1402 -> 1390 bytes .../cctz/testdata/zoneinfo/America/Mazatlan | Bin 1550 -> 1526 bytes .../cctz/testdata/zoneinfo/America/Mendoza | Bin 1100 -> 1076 bytes .../internal/cctz/testdata/zoneinfo/America/Merida | Bin 1442 -> 1422 bytes .../cctz/testdata/zoneinfo/America/Metlakatla | Bin 1409 -> 1423 bytes .../cctz/testdata/zoneinfo/America/Mexico_City | Bin 1604 -> 1584 bytes .../cctz/testdata/zoneinfo/America/Miquelon | Bin 1682 -> 1666 bytes .../cctz/testdata/zoneinfo/America/Monterrey | Bin 1402 -> 1390 bytes .../cctz/testdata/zoneinfo/America/Montevideo | Bin 1550 -> 1510 bytes .../cctz/testdata/zoneinfo/America/Montserrat | Bin 156 -> 148 bytes .../internal/cctz/testdata/zoneinfo/America/Nassau | Bin 2270 -> 2258 bytes .../cctz/testdata/zoneinfo/America/Noronha | Bin 728 -> 716 bytes .../cctz/testdata/zoneinfo/America/Ojinaga | Bin 1508 -> 1484 bytes .../internal/cctz/testdata/zoneinfo/America/Panama | Bin 194 -> 182 bytes .../cctz/testdata/zoneinfo/America/Paramaribo | Bin 282 -> 262 bytes .../cctz/testdata/zoneinfo/America/Phoenix | Bin 344 -> 328 bytes .../cctz/testdata/zoneinfo/America/Port-au-Prince | Bin 1446 -> 1434 bytes .../cctz/testdata/zoneinfo/America/Port_of_Spain | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/Porto_Acre | Bin 648 -> 628 bytes .../cctz/testdata/zoneinfo/America/Porto_Velho | Bin 588 -> 576 bytes .../cctz/testdata/zoneinfo/America/Rankin_Inlet | Bin 1916 -> 1892 bytes .../internal/cctz/testdata/zoneinfo/America/Recife | Bin 728 -> 716 bytes .../cctz/testdata/zoneinfo/America/Resolute | Bin 1916 -> 1892 bytes .../cctz/testdata/zoneinfo/America/Rio_Branco | Bin 648 -> 628 bytes .../cctz/testdata/zoneinfo/America/Rosario | Bin 1100 -> 1076 bytes .../cctz/testdata/zoneinfo/America/Santarem | Bin 618 -> 602 bytes .../cctz/testdata/zoneinfo/America/Santo_Domingo | Bin 482 -> 458 bytes .../cctz/testdata/zoneinfo/America/Sao_Paulo | Bin 2002 -> 1444 bytes .../cctz/testdata/zoneinfo/America/St_Barthelemy | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/St_Kitts | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/St_Lucia | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/St_Thomas | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/St_Vincent | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/Tegucigalpa | Bin 264 -> 252 bytes .../internal/cctz/testdata/zoneinfo/America/Thule | Bin 1514 -> 1502 bytes .../cctz/testdata/zoneinfo/America/Tortola | Bin 156 -> 148 bytes .../internal/cctz/testdata/zoneinfo/America/Virgin | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/America/Winnipeg | Bin 2882 -> 2868 bytes .../testdata/zoneinfo/Antarctica/DumontDUrville | Bin 202 -> 194 bytes .../cctz/testdata/zoneinfo/Antarctica/Macquarie | Bin 1534 -> 1520 bytes .../cctz/testdata/zoneinfo/Antarctica/Mawson | Bin 211 -> 199 bytes .../cctz/testdata/zoneinfo/Antarctica/McMurdo | Bin 2451 -> 2437 bytes .../cctz/testdata/zoneinfo/Antarctica/Rothera | Bin 172 -> 164 bytes .../cctz/testdata/zoneinfo/Antarctica/South_Pole | Bin 2451 -> 2437 bytes .../cctz/testdata/zoneinfo/Antarctica/Syowa | Bin 173 -> 165 bytes .../cctz/testdata/zoneinfo/Antarctica/Vostok | Bin 173 -> 165 bytes .../cctz/testdata/zoneinfo/Arctic/Longyearbyen | Bin 2242 -> 2228 bytes .../time/internal/cctz/testdata/zoneinfo/Asia/Aden | Bin 173 -> 165 bytes .../internal/cctz/testdata/zoneinfo/Asia/Almaty | Bin 1017 -> 997 bytes .../internal/cctz/testdata/zoneinfo/Asia/Amman | Bin 1863 -> 1853 bytes .../internal/cctz/testdata/zoneinfo/Asia/Anadyr | Bin 1208 -> 1188 bytes .../internal/cctz/testdata/zoneinfo/Asia/Aqtau | Bin 1003 -> 983 bytes .../internal/cctz/testdata/zoneinfo/Asia/Aqtobe | Bin 1033 -> 1011 bytes .../internal/cctz/testdata/zoneinfo/Asia/Ashgabat | Bin 637 -> 619 bytes .../internal/cctz/testdata/zoneinfo/Asia/Ashkhabad | Bin 637 -> 619 bytes .../internal/cctz/testdata/zoneinfo/Asia/Atyrau | Bin 1011 -> 991 bytes .../internal/cctz/testdata/zoneinfo/Asia/Baghdad | Bin 995 -> 983 bytes .../internal/cctz/testdata/zoneinfo/Asia/Bahrain | Bin 211 -> 199 bytes .../time/internal/cctz/testdata/zoneinfo/Asia/Baku | Bin 1255 -> 1227 bytes .../internal/cctz/testdata/zoneinfo/Asia/Bangkok | Bin 211 -> 199 bytes .../internal/cctz/testdata/zoneinfo/Asia/Barnaul | Bin 1241 -> 1221 bytes .../internal/cctz/testdata/zoneinfo/Asia/Beirut | Bin 2166 -> 2154 bytes .../internal/cctz/testdata/zoneinfo/Asia/Bishkek | Bin 999 -> 983 bytes .../internal/cctz/testdata/zoneinfo/Asia/Brunei | Bin 215 -> 203 bytes .../internal/cctz/testdata/zoneinfo/Asia/Calcutta | Bin 303 -> 285 bytes .../internal/cctz/testdata/zoneinfo/Asia/Chita | Bin 1243 -> 1221 bytes .../cctz/testdata/zoneinfo/Asia/Choibalsan | Bin 977 -> 949 bytes .../internal/cctz/testdata/zoneinfo/Asia/Chongqing | Bin 545 -> 533 bytes .../internal/cctz/testdata/zoneinfo/Asia/Chungking | Bin 545 -> 533 bytes .../internal/cctz/testdata/zoneinfo/Asia/Colombo | Bin 404 -> 372 bytes .../internal/cctz/testdata/zoneinfo/Asia/Dacca | Bin 361 -> 337 bytes .../internal/cctz/testdata/zoneinfo/Asia/Damascus | Bin 2306 -> 2294 bytes .../internal/cctz/testdata/zoneinfo/Asia/Dhaka | Bin 361 -> 337 bytes .../time/internal/cctz/testdata/zoneinfo/Asia/Dili | Bin 239 -> 227 bytes .../internal/cctz/testdata/zoneinfo/Asia/Dubai | Bin 173 -> 165 bytes .../internal/cctz/testdata/zoneinfo/Asia/Dushanbe | Bin 607 -> 591 bytes .../time/internal/cctz/testdata/zoneinfo/Asia/Gaza | Bin 2286 -> 2316 bytes .../internal/cctz/testdata/zoneinfo/Asia/Harbin | Bin 545 -> 533 bytes .../internal/cctz/testdata/zoneinfo/Asia/Hebron | Bin 2314 -> 2344 bytes .../cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh | Bin 375 -> 351 bytes .../internal/cctz/testdata/zoneinfo/Asia/Hong_Kong | Bin 1175 -> 1193 bytes .../time/internal/cctz/testdata/zoneinfo/Asia/Hovd | Bin 907 -> 891 bytes .../internal/cctz/testdata/zoneinfo/Asia/Irkutsk | Bin 1267 -> 1243 bytes .../internal/cctz/testdata/zoneinfo/Asia/Istanbul | Bin 2157 -> 2143 bytes .../internal/cctz/testdata/zoneinfo/Asia/Jakarta | Bin 383 -> 355 bytes .../internal/cctz/testdata/zoneinfo/Asia/Jayapura | Bin 237 -> 221 bytes .../internal/cctz/testdata/zoneinfo/Asia/Jerusalem | Bin 2256 -> 2288 bytes .../internal/cctz/testdata/zoneinfo/Asia/Kabul | Bin 220 -> 208 bytes .../internal/cctz/testdata/zoneinfo/Asia/Kamchatka | Bin 1184 -> 1166 bytes .../internal/cctz/testdata/zoneinfo/Asia/Karachi | Bin 403 -> 379 bytes .../internal/cctz/testdata/zoneinfo/Asia/Kashgar | Bin 173 -> 165 bytes .../internal/cctz/testdata/zoneinfo/Asia/Kathmandu | Bin 224 -> 212 bytes .../internal/cctz/testdata/zoneinfo/Asia/Katmandu | Bin 224 -> 212 bytes .../internal/cctz/testdata/zoneinfo/Asia/Khandyga | Bin 1297 -> 1271 bytes .../internal/cctz/testdata/zoneinfo/Asia/Kolkata | Bin 303 -> 285 bytes .../cctz/testdata/zoneinfo/Asia/Krasnoyarsk | Bin 1229 -> 1207 bytes .../cctz/testdata/zoneinfo/Asia/Kuala_Lumpur | Bin 415 -> 383 bytes .../internal/cctz/testdata/zoneinfo/Asia/Kuching | Bin 507 -> 483 bytes .../internal/cctz/testdata/zoneinfo/Asia/Kuwait | Bin 173 -> 165 bytes .../internal/cctz/testdata/zoneinfo/Asia/Macao | Bin 1241 -> 1227 bytes .../internal/cctz/testdata/zoneinfo/Asia/Macau | Bin 1241 -> 1227 bytes .../internal/cctz/testdata/zoneinfo/Asia/Magadan | Bin 1244 -> 1222 bytes .../internal/cctz/testdata/zoneinfo/Asia/Makassar | Bin 274 -> 254 bytes .../internal/cctz/testdata/zoneinfo/Asia/Manila | Bin 350 -> 328 bytes .../internal/cctz/testdata/zoneinfo/Asia/Muscat | Bin 173 -> 165 bytes .../cctz/testdata/zoneinfo/Asia/Novokuznetsk | Bin 1183 -> 1165 bytes .../cctz/testdata/zoneinfo/Asia/Novosibirsk | Bin 1241 -> 1221 bytes .../time/internal/cctz/testdata/zoneinfo/Asia/Omsk | Bin 1229 -> 1207 bytes .../time/internal/cctz/testdata/zoneinfo/Asia/Oral | Bin 1025 -> 1005 bytes .../cctz/testdata/zoneinfo/Asia/Phnom_Penh | Bin 211 -> 199 bytes .../internal/cctz/testdata/zoneinfo/Asia/Pontianak | Bin 381 -> 353 bytes .../internal/cctz/testdata/zoneinfo/Asia/Pyongyang | Bin 253 -> 237 bytes .../internal/cctz/testdata/zoneinfo/Asia/Qatar | Bin 211 -> 199 bytes .../internal/cctz/testdata/zoneinfo/Asia/Qostanay | Bin 0 -> 1011 bytes .../internal/cctz/testdata/zoneinfo/Asia/Qyzylorda | Bin 1017 -> 1025 bytes .../internal/cctz/testdata/zoneinfo/Asia/Rangoon | Bin 288 -> 268 bytes .../internal/cctz/testdata/zoneinfo/Asia/Riyadh | Bin 173 -> 165 bytes .../internal/cctz/testdata/zoneinfo/Asia/Saigon | Bin 375 -> 351 bytes .../internal/cctz/testdata/zoneinfo/Asia/Sakhalin | Bin 1220 -> 1202 bytes .../internal/cctz/testdata/zoneinfo/Asia/Samarkand | Bin 605 -> 577 bytes .../internal/cctz/testdata/zoneinfo/Asia/Seoul | Bin 517 -> 493 bytes .../internal/cctz/testdata/zoneinfo/Asia/Shanghai | Bin 545 -> 533 bytes .../internal/cctz/testdata/zoneinfo/Asia/Singapore | Bin 415 -> 383 bytes .../cctz/testdata/zoneinfo/Asia/Srednekolymsk | Bin 1230 -> 1208 bytes .../internal/cctz/testdata/zoneinfo/Asia/Taipei | Bin 781 -> 761 bytes .../internal/cctz/testdata/zoneinfo/Asia/Tashkent | Bin 621 -> 591 bytes .../internal/cctz/testdata/zoneinfo/Asia/Tbilisi | Bin 1071 -> 1035 bytes .../internal/cctz/testdata/zoneinfo/Asia/Tehran | Bin 1704 -> 2582 bytes .../internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv | Bin 2256 -> 2288 bytes .../internal/cctz/testdata/zoneinfo/Asia/Thimbu | Bin 215 -> 203 bytes .../internal/cctz/testdata/zoneinfo/Asia/Thimphu | Bin 215 -> 203 bytes .../internal/cctz/testdata/zoneinfo/Asia/Tomsk | Bin 1241 -> 1221 bytes .../cctz/testdata/zoneinfo/Asia/Ujung_Pandang | Bin 274 -> 254 bytes .../cctz/testdata/zoneinfo/Asia/Ulaanbaatar | Bin 907 -> 891 bytes .../cctz/testdata/zoneinfo/Asia/Ulan_Bator | Bin 907 -> 891 bytes .../internal/cctz/testdata/zoneinfo/Asia/Urumqi | Bin 173 -> 165 bytes .../internal/cctz/testdata/zoneinfo/Asia/Ust-Nera | Bin 1276 -> 1252 bytes .../internal/cctz/testdata/zoneinfo/Asia/Vientiane | Bin 211 -> 199 bytes .../cctz/testdata/zoneinfo/Asia/Vladivostok | Bin 1230 -> 1208 bytes .../internal/cctz/testdata/zoneinfo/Asia/Yakutsk | Bin 1229 -> 1207 bytes .../internal/cctz/testdata/zoneinfo/Asia/Yangon | Bin 288 -> 268 bytes .../cctz/testdata/zoneinfo/Asia/Yekaterinburg | Bin 1267 -> 1243 bytes .../internal/cctz/testdata/zoneinfo/Asia/Yerevan | Bin 1199 -> 1151 bytes .../cctz/testdata/zoneinfo/Atlantic/Bermuda | Bin 1990 -> 1978 bytes .../cctz/testdata/zoneinfo/Atlantic/Jan_Mayen | Bin 2242 -> 2228 bytes .../cctz/testdata/zoneinfo/Atlantic/Reykjavik | Bin 1174 -> 1162 bytes .../cctz/testdata/zoneinfo/Atlantic/South_Georgia | Bin 172 -> 164 bytes .../cctz/testdata/zoneinfo/Atlantic/St_Helena | Bin 156 -> 148 bytes .../cctz/testdata/zoneinfo/Atlantic/Stanley | Bin 1242 -> 1214 bytes .../internal/cctz/testdata/zoneinfo/Australia/ACT | Bin 2214 -> 2204 bytes .../cctz/testdata/zoneinfo/Australia/Adelaide | Bin 2233 -> 2222 bytes .../cctz/testdata/zoneinfo/Australia/Brisbane | Bin 443 -> 433 bytes .../cctz/testdata/zoneinfo/Australia/Broken_Hill | Bin 2269 -> 2243 bytes .../cctz/testdata/zoneinfo/Australia/Canberra | Bin 2214 -> 2204 bytes .../cctz/testdata/zoneinfo/Australia/Currie | Bin 2214 -> 2204 bytes .../cctz/testdata/zoneinfo/Australia/Darwin | Bin 318 -> 304 bytes .../cctz/testdata/zoneinfo/Australia/Eucla | Bin 494 -> 484 bytes .../cctz/testdata/zoneinfo/Australia/Hobart | Bin 2326 -> 2316 bytes .../internal/cctz/testdata/zoneinfo/Australia/LHI | Bin 1880 -> 1860 bytes .../cctz/testdata/zoneinfo/Australia/Lindeman | Bin 513 -> 489 bytes .../cctz/testdata/zoneinfo/Australia/Lord_Howe | Bin 1880 -> 1860 bytes .../cctz/testdata/zoneinfo/Australia/Melbourne | Bin 2214 -> 2204 bytes .../internal/cctz/testdata/zoneinfo/Australia/NSW | Bin 2214 -> 2204 bytes .../cctz/testdata/zoneinfo/Australia/North | Bin 318 -> 304 bytes .../cctz/testdata/zoneinfo/Australia/Perth | Bin 470 -> 460 bytes .../cctz/testdata/zoneinfo/Australia/Queensland | Bin 443 -> 433 bytes .../cctz/testdata/zoneinfo/Australia/South | Bin 2233 -> 2222 bytes .../cctz/testdata/zoneinfo/Australia/Sydney | Bin 2214 -> 2204 bytes .../cctz/testdata/zoneinfo/Australia/Tasmania | Bin 2326 -> 2316 bytes .../cctz/testdata/zoneinfo/Australia/Victoria | Bin 2214 -> 2204 bytes .../internal/cctz/testdata/zoneinfo/Australia/West | Bin 470 -> 460 bytes .../cctz/testdata/zoneinfo/Australia/Yancowinna | Bin 2269 -> 2243 bytes .../internal/cctz/testdata/zoneinfo/Brazil/Acre | Bin 648 -> 628 bytes .../cctz/testdata/zoneinfo/Brazil/DeNoronha | Bin 728 -> 716 bytes .../internal/cctz/testdata/zoneinfo/Brazil/East | Bin 2002 -> 1444 bytes .../internal/cctz/testdata/zoneinfo/Brazil/West | Bin 616 -> 604 bytes absl/time/internal/cctz/testdata/zoneinfo/CET | Bin 2102 -> 2094 bytes absl/time/internal/cctz/testdata/zoneinfo/CST6CDT | Bin 2294 -> 2310 bytes .../internal/cctz/testdata/zoneinfo/Canada/Central | Bin 2882 -> 2868 bytes absl/time/internal/cctz/testdata/zoneinfo/Cuba | Bin 2428 -> 2416 bytes absl/time/internal/cctz/testdata/zoneinfo/EET | Bin 1876 -> 1908 bytes absl/time/internal/cctz/testdata/zoneinfo/EST | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/EST5EDT | Bin 2294 -> 2310 bytes absl/time/internal/cctz/testdata/zoneinfo/Egypt | Bin 1963 -> 1955 bytes absl/time/internal/cctz/testdata/zoneinfo/Eire | Bin 3522 -> 3492 bytes absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT | Bin 118 -> 114 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 | Bin 118 -> 114 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 | Bin 120 -> 116 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+10 | Bin 121 -> 117 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+11 | Bin 121 -> 117 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+12 | Bin 121 -> 117 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 | Bin 120 -> 116 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 | Bin 120 -> 116 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 | Bin 120 -> 116 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 | Bin 120 -> 116 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 | Bin 120 -> 116 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 | Bin 120 -> 116 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 | Bin 120 -> 116 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 | Bin 120 -> 116 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 | Bin 118 -> 114 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 | Bin 121 -> 117 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-10 | Bin 122 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-11 | Bin 122 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-12 | Bin 122 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-13 | Bin 122 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-14 | Bin 122 -> 118 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 | Bin 121 -> 117 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 | Bin 121 -> 117 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 | Bin 121 -> 117 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 | Bin 121 -> 117 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 | Bin 121 -> 117 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 | Bin 121 -> 117 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 | Bin 121 -> 117 bytes .../time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 | Bin 121 -> 117 bytes absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 | Bin 118 -> 114 bytes .../internal/cctz/testdata/zoneinfo/Etc/Greenwich | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC | Bin 118 -> 114 bytes .../internal/cctz/testdata/zoneinfo/Etc/Universal | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu | Bin 118 -> 114 bytes .../cctz/testdata/zoneinfo/Europe/Amsterdam | Bin 2940 -> 2910 bytes .../cctz/testdata/zoneinfo/Europe/Astrakhan | Bin 1183 -> 1165 bytes .../internal/cctz/testdata/zoneinfo/Europe/Belfast | Bin 3678 -> 3648 bytes .../cctz/testdata/zoneinfo/Europe/Belgrade | Bin 1948 -> 1920 bytes .../internal/cctz/testdata/zoneinfo/Europe/Berlin | Bin 2326 -> 2298 bytes .../cctz/testdata/zoneinfo/Europe/Bratislava | Bin 2329 -> 2301 bytes .../cctz/testdata/zoneinfo/Europe/Brussels | Bin 2961 -> 2933 bytes .../cctz/testdata/zoneinfo/Europe/Bucharest | Bin 2212 -> 2184 bytes .../cctz/testdata/zoneinfo/Europe/Budapest | Bin 2396 -> 2368 bytes .../cctz/testdata/zoneinfo/Europe/Chisinau | Bin 2436 -> 2390 bytes .../cctz/testdata/zoneinfo/Europe/Copenhagen | Bin 2151 -> 2137 bytes .../internal/cctz/testdata/zoneinfo/Europe/Dublin | Bin 3522 -> 3492 bytes .../cctz/testdata/zoneinfo/Europe/Guernsey | Bin 3678 -> 3648 bytes .../cctz/testdata/zoneinfo/Europe/Isle_of_Man | Bin 3678 -> 3648 bytes .../cctz/testdata/zoneinfo/Europe/Istanbul | Bin 2157 -> 2143 bytes .../internal/cctz/testdata/zoneinfo/Europe/Jersey | Bin 3678 -> 3648 bytes .../cctz/testdata/zoneinfo/Europe/Kaliningrad | Bin 1509 -> 1479 bytes .../cctz/testdata/zoneinfo/Europe/Ljubljana | Bin 1948 -> 1920 bytes .../internal/cctz/testdata/zoneinfo/Europe/London | Bin 3678 -> 3648 bytes .../cctz/testdata/zoneinfo/Europe/Luxembourg | Bin 2960 -> 2946 bytes .../internal/cctz/testdata/zoneinfo/Europe/Madrid | Bin 2628 -> 2614 bytes .../internal/cctz/testdata/zoneinfo/Europe/Minsk | Bin 1361 -> 1321 bytes .../internal/cctz/testdata/zoneinfo/Europe/Oslo | Bin 2242 -> 2228 bytes .../cctz/testdata/zoneinfo/Europe/Podgorica | Bin 1948 -> 1920 bytes .../internal/cctz/testdata/zoneinfo/Europe/Prague | Bin 2329 -> 2301 bytes .../internal/cctz/testdata/zoneinfo/Europe/Riga | Bin 2226 -> 2198 bytes .../internal/cctz/testdata/zoneinfo/Europe/Rome | Bin 2683 -> 2641 bytes .../cctz/testdata/zoneinfo/Europe/San_Marino | Bin 2683 -> 2641 bytes .../cctz/testdata/zoneinfo/Europe/Sarajevo | Bin 1948 -> 1920 bytes .../cctz/testdata/zoneinfo/Europe/Simferopol | Bin 1481 -> 1453 bytes .../internal/cctz/testdata/zoneinfo/Europe/Skopje | Bin 1948 -> 1920 bytes .../internal/cctz/testdata/zoneinfo/Europe/Sofia | Bin 2121 -> 2077 bytes .../internal/cctz/testdata/zoneinfo/Europe/Tallinn | Bin 2178 -> 2148 bytes .../cctz/testdata/zoneinfo/Europe/Tiraspol | Bin 2436 -> 2390 bytes .../cctz/testdata/zoneinfo/Europe/Uzhgorod | Bin 2094 -> 2050 bytes .../internal/cctz/testdata/zoneinfo/Europe/Vatican | Bin 2683 -> 2641 bytes .../internal/cctz/testdata/zoneinfo/Europe/Vienna | Bin 2228 -> 2200 bytes .../internal/cctz/testdata/zoneinfo/Europe/Vilnius | Bin 2190 -> 2162 bytes .../cctz/testdata/zoneinfo/Europe/Volgograd | Bin 1183 -> 1165 bytes .../internal/cctz/testdata/zoneinfo/Europe/Warsaw | Bin 2696 -> 2654 bytes .../internal/cctz/testdata/zoneinfo/Europe/Zagreb | Bin 1948 -> 1920 bytes absl/time/internal/cctz/testdata/zoneinfo/Factory | Bin 120 -> 116 bytes absl/time/internal/cctz/testdata/zoneinfo/GB | Bin 3678 -> 3648 bytes absl/time/internal/cctz/testdata/zoneinfo/GB-Eire | Bin 3678 -> 3648 bytes absl/time/internal/cctz/testdata/zoneinfo/GMT | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/GMT+0 | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/GMT-0 | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/GMT0 | Bin 118 -> 114 bytes .../time/internal/cctz/testdata/zoneinfo/Greenwich | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/HST | Bin 119 -> 115 bytes absl/time/internal/cctz/testdata/zoneinfo/Hongkong | Bin 1175 -> 1193 bytes absl/time/internal/cctz/testdata/zoneinfo/Iceland | Bin 1174 -> 1162 bytes .../cctz/testdata/zoneinfo/Indian/Antananarivo | Bin 271 -> 251 bytes .../internal/cctz/testdata/zoneinfo/Indian/Chagos | Bin 211 -> 199 bytes .../cctz/testdata/zoneinfo/Indian/Christmas | Bin 173 -> 165 bytes .../internal/cctz/testdata/zoneinfo/Indian/Cocos | Bin 182 -> 174 bytes .../internal/cctz/testdata/zoneinfo/Indian/Comoro | Bin 271 -> 251 bytes .../cctz/testdata/zoneinfo/Indian/Kerguelen | Bin 173 -> 165 bytes .../internal/cctz/testdata/zoneinfo/Indian/Mahe | Bin 173 -> 165 bytes .../cctz/testdata/zoneinfo/Indian/Maldives | Bin 211 -> 199 bytes .../cctz/testdata/zoneinfo/Indian/Mauritius | Bin 253 -> 241 bytes .../internal/cctz/testdata/zoneinfo/Indian/Mayotte | Bin 271 -> 251 bytes .../internal/cctz/testdata/zoneinfo/Indian/Reunion | Bin 173 -> 165 bytes absl/time/internal/cctz/testdata/zoneinfo/Iran | Bin 1704 -> 2582 bytes absl/time/internal/cctz/testdata/zoneinfo/Israel | Bin 2256 -> 2288 bytes absl/time/internal/cctz/testdata/zoneinfo/Jamaica | Bin 498 -> 482 bytes .../time/internal/cctz/testdata/zoneinfo/Kwajalein | Bin 250 -> 316 bytes absl/time/internal/cctz/testdata/zoneinfo/Libya | Bin 641 -> 625 bytes absl/time/internal/cctz/testdata/zoneinfo/MET | Bin 2102 -> 2094 bytes absl/time/internal/cctz/testdata/zoneinfo/MST | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/MST7MDT | Bin 2294 -> 2310 bytes .../internal/cctz/testdata/zoneinfo/Mexico/BajaSur | Bin 1550 -> 1526 bytes .../internal/cctz/testdata/zoneinfo/Mexico/General | Bin 1604 -> 1584 bytes absl/time/internal/cctz/testdata/zoneinfo/NZ | Bin 2451 -> 2437 bytes absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT | Bin 2078 -> 2068 bytes absl/time/internal/cctz/testdata/zoneinfo/PRC | Bin 545 -> 533 bytes absl/time/internal/cctz/testdata/zoneinfo/PST8PDT | Bin 2294 -> 2310 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Apia | Bin 1125 -> 1097 bytes .../cctz/testdata/zoneinfo/Pacific/Auckland | Bin 2451 -> 2437 bytes .../cctz/testdata/zoneinfo/Pacific/Bougainville | Bin 286 -> 268 bytes .../cctz/testdata/zoneinfo/Pacific/Chatham | Bin 2078 -> 2068 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Chuuk | Bin 174 -> 269 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Efate | Bin 478 -> 466 bytes .../cctz/testdata/zoneinfo/Pacific/Enderbury | Bin 250 -> 234 bytes .../cctz/testdata/zoneinfo/Pacific/Fakaofo | Bin 212 -> 200 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Fiji | Bin 1090 -> 1078 bytes .../cctz/testdata/zoneinfo/Pacific/Funafuti | Bin 174 -> 166 bytes .../cctz/testdata/zoneinfo/Pacific/Galapagos | Bin 254 -> 238 bytes .../cctz/testdata/zoneinfo/Pacific/Gambier | Bin 172 -> 164 bytes .../cctz/testdata/zoneinfo/Pacific/Guadalcanal | Bin 174 -> 166 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Guam | Bin 216 -> 494 bytes .../cctz/testdata/zoneinfo/Pacific/Kiritimati | Bin 254 -> 238 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Kosrae | Bin 242 -> 351 bytes .../cctz/testdata/zoneinfo/Pacific/Kwajalein | Bin 250 -> 316 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Majuro | Bin 212 -> 310 bytes .../cctz/testdata/zoneinfo/Pacific/Marquesas | Bin 181 -> 173 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Midway | Bin 187 -> 175 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Nauru | Bin 268 -> 252 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Niue | Bin 257 -> 241 bytes .../cctz/testdata/zoneinfo/Pacific/Norfolk | Bin 314 -> 294 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Noumea | Bin 314 -> 304 bytes .../cctz/testdata/zoneinfo/Pacific/Pago_Pago | Bin 187 -> 175 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Palau | Bin 173 -> 180 bytes .../cctz/testdata/zoneinfo/Pacific/Pitcairn | Bin 214 -> 202 bytes .../cctz/testdata/zoneinfo/Pacific/Pohnpei | Bin 174 -> 303 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Ponape | Bin 174 -> 303 bytes .../cctz/testdata/zoneinfo/Pacific/Port_Moresby | Bin 196 -> 186 bytes .../cctz/testdata/zoneinfo/Pacific/Rarotonga | Bin 593 -> 577 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Saipan | Bin 216 -> 494 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Samoa | Bin 187 -> 175 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Tahiti | Bin 173 -> 165 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Tarawa | Bin 174 -> 166 bytes .../cctz/testdata/zoneinfo/Pacific/Tongatapu | Bin 384 -> 372 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Truk | Bin 174 -> 269 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Wake | Bin 174 -> 166 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Wallis | Bin 174 -> 166 bytes .../internal/cctz/testdata/zoneinfo/Pacific/Yap | Bin 174 -> 269 bytes absl/time/internal/cctz/testdata/zoneinfo/Poland | Bin 2696 -> 2654 bytes absl/time/internal/cctz/testdata/zoneinfo/ROC | Bin 781 -> 761 bytes absl/time/internal/cctz/testdata/zoneinfo/ROK | Bin 517 -> 493 bytes .../time/internal/cctz/testdata/zoneinfo/Singapore | Bin 415 -> 383 bytes absl/time/internal/cctz/testdata/zoneinfo/Turkey | Bin 2157 -> 2143 bytes absl/time/internal/cctz/testdata/zoneinfo/UCT | Bin 118 -> 114 bytes .../internal/cctz/testdata/zoneinfo/US/Arizona | Bin 344 -> 328 bytes absl/time/internal/cctz/testdata/zoneinfo/US/Samoa | Bin 187 -> 175 bytes absl/time/internal/cctz/testdata/zoneinfo/UTC | Bin 118 -> 114 bytes .../time/internal/cctz/testdata/zoneinfo/Universal | Bin 118 -> 114 bytes absl/time/internal/cctz/testdata/zoneinfo/WET | Bin 1873 -> 1905 bytes absl/time/internal/cctz/testdata/zoneinfo/Zulu | Bin 118 -> 114 bytes .../internal/cctz/testdata/zoneinfo/iso3166.tab | 8 +- .../internal/cctz/testdata/zoneinfo/zone1970.tab | 4 +- absl/time/internal/get_current_time_chrono.inc | 6 +- absl/time/internal/get_current_time_posix.inc | 4 +- absl/time/internal/test_util.cc | 10 +- absl/time/internal/test_util.h | 6 +- absl/time/time.cc | 16 +- absl/time/time.h | 40 +- absl/time/time_benchmark.cc | 2 +- absl/time/time_test.cc | 24 +- absl/time/time_zone_test.cc | 2 +- absl/types/BUILD.bazel | 69 +- absl/types/CMakeLists.txt | 462 +++--- absl/types/any.h | 12 +- absl/types/any_exception_safety_test.cc | 3 +- absl/types/any_test.cc | 70 +- absl/types/bad_any_cast.cc | 6 +- absl/types/bad_any_cast.h | 10 +- absl/types/bad_optional_access.cc | 6 +- absl/types/bad_optional_access.h | 10 +- absl/types/bad_variant_access.cc | 6 +- absl/types/bad_variant_access.h | 10 +- absl/types/compare.h | 510 +++++++ absl/types/compare_test.cc | 313 ++++ absl/types/internal/optional.h | 396 ++++++ absl/types/internal/span.h | 128 ++ absl/types/internal/variant.h | 74 +- absl/types/optional.cc | 26 - absl/types/optional.h | 454 +----- absl/types/optional_exception_safety_test.cc | 6 +- absl/types/optional_test.cc | 90 +- absl/types/span.h | 221 ++- absl/types/span_test.cc | 57 +- absl/types/variant.h | 33 +- absl/types/variant_benchmark.cc | 6 +- absl/types/variant_exception_safety_test.cc | 15 +- absl/types/variant_test.cc | 242 +++- absl/utility/BUILD.bazel | 5 +- absl/utility/CMakeLists.txt | 56 +- absl/utility/utility.h | 99 +- absl/utility/utility_test.cc | 35 +- ci/cmake_install_test.sh | 33 + ci/linux_clang-latest_libcxx_asan_bazel.sh | 89 ++ ci/linux_clang-latest_libcxx_bazel.sh | 78 + ci/linux_clang-latest_libcxx_tsan_bazel.sh | 83 ++ ci/linux_clang-latest_libstdcxx_bazel.sh | 77 + ci/linux_gcc-4.8_libstdcxx_cmake.sh | 61 + ci/linux_gcc-4.9_libstdcxx_bazel.sh | 75 + ci/linux_gcc-latest_libstdcxx_bazel.sh | 75 + ci/linux_gcc-latest_libstdcxx_cmake.sh | 61 + ci/macos_xcode_bazel.sh | 41 + ci/macos_xcode_cmake.sh | 43 + conanfile.py | 51 + 1072 files changed, 48105 insertions(+), 6910 deletions(-) delete mode 100644 CMake/AbseilConfigureCopts.cmake create mode 100644 CMake/AbseilInstallDirs.cmake delete mode 100644 CMake/CMakeLists.txt.in delete mode 100644 CMake/DownloadGTest.cmake create mode 100644 CMake/Googletest/CMakeLists.txt.in create mode 100644 CMake/Googletest/DownloadGTest.cmake create mode 100644 CMake/abslConfig.cmake.in create mode 100644 CMake/install_test_project/CMakeLists.txt create mode 100644 CMake/install_test_project/simple.cc create mode 100755 CMake/install_test_project/test.sh create mode 100644 UPGRADES.md create mode 100644 absl/base/const_init.h create mode 100644 absl/base/internal/cmake_thread_test.cc create mode 100644 absl/base/internal/scoped_set_env.cc create mode 100644 absl/base/internal/scoped_set_env.h create mode 100644 absl/base/internal/scoped_set_env_test.cc create mode 100644 absl/base/internal/thread_annotations.h create mode 100644 absl/base/log_severity.cc create mode 100644 absl/base/log_severity_test.cc create mode 100644 absl/container/inlined_vector_exception_safety_test.cc create mode 100644 absl/container/internal/common.h create mode 100644 absl/container/internal/counting_allocator.h create mode 100644 absl/container/internal/hashtablez_sampler.cc create mode 100644 absl/container/internal/hashtablez_sampler.h create mode 100644 absl/container/internal/hashtablez_sampler_force_weak_definition.cc create mode 100644 absl/container/internal/hashtablez_sampler_test.cc create mode 100644 absl/container/internal/have_sse.h create mode 100644 absl/container/internal/inlined_vector.h create mode 100644 absl/container/internal/unordered_map_members_test.h create mode 100644 absl/container/internal/unordered_set_members_test.h delete mode 100644 absl/copts.bzl create mode 100644 absl/copts/AbseilConfigureCopts.cmake create mode 100644 absl/copts/GENERATED_AbseilCopts.cmake create mode 100644 absl/copts/GENERATED_copts.bzl create mode 100644 absl/copts/configure_copts.bzl create mode 100644 absl/copts/copts.py create mode 100755 absl/copts/generate_copts.py create mode 100644 absl/flags/BUILD.bazel create mode 100644 absl/flags/CMakeLists.txt create mode 100644 absl/flags/config.h create mode 100644 absl/flags/config_test.cc create mode 100644 absl/flags/declare.h create mode 100644 absl/flags/flag.cc create mode 100644 absl/flags/flag.h create mode 100644 absl/flags/flag_test.cc create mode 100644 absl/flags/flag_test_defs.cc create mode 100644 absl/flags/internal/commandlineflag.cc create mode 100644 absl/flags/internal/commandlineflag.h create mode 100644 absl/flags/internal/commandlineflag_test.cc create mode 100644 absl/flags/internal/flag.h create mode 100644 absl/flags/internal/parse.h create mode 100644 absl/flags/internal/path_util.h create mode 100644 absl/flags/internal/path_util_test.cc create mode 100644 absl/flags/internal/program_name.cc create mode 100644 absl/flags/internal/program_name.h create mode 100644 absl/flags/internal/program_name_test.cc create mode 100644 absl/flags/internal/registry.cc create mode 100644 absl/flags/internal/registry.h create mode 100644 absl/flags/internal/type_erased.cc create mode 100644 absl/flags/internal/type_erased.h create mode 100644 absl/flags/internal/type_erased_test.cc create mode 100644 absl/flags/internal/usage.cc create mode 100644 absl/flags/internal/usage.h create mode 100644 absl/flags/internal/usage_test.cc create mode 100644 absl/flags/marshalling.cc create mode 100644 absl/flags/marshalling.h create mode 100644 absl/flags/marshalling_test.cc create mode 100644 absl/flags/parse.cc create mode 100644 absl/flags/parse.h create mode 100644 absl/flags/parse_test.cc create mode 100644 absl/flags/usage.cc create mode 100644 absl/flags/usage.h create mode 100644 absl/flags/usage_config.cc create mode 100644 absl/flags/usage_config.h create mode 100644 absl/flags/usage_config_test.cc create mode 100644 absl/random/BUILD.bazel create mode 100644 absl/random/CMakeLists.txt create mode 100644 absl/random/benchmarks.cc create mode 100644 absl/random/bernoulli_distribution.h create mode 100644 absl/random/bernoulli_distribution_test.cc create mode 100644 absl/random/beta_distribution.h create mode 100644 absl/random/beta_distribution_test.cc create mode 100644 absl/random/discrete_distribution.cc create mode 100644 absl/random/discrete_distribution.h create mode 100644 absl/random/discrete_distribution_test.cc create mode 100644 absl/random/distribution_format_traits.h create mode 100644 absl/random/distributions.h create mode 100644 absl/random/distributions_test.cc create mode 100644 absl/random/examples_test.cc create mode 100644 absl/random/exponential_distribution.h create mode 100644 absl/random/exponential_distribution_test.cc create mode 100644 absl/random/gaussian_distribution.cc create mode 100644 absl/random/gaussian_distribution.h create mode 100644 absl/random/gaussian_distribution_test.cc create mode 100644 absl/random/generators_test.cc create mode 100644 absl/random/internal/BUILD.bazel create mode 100644 absl/random/internal/chi_square.cc create mode 100644 absl/random/internal/chi_square.h create mode 100644 absl/random/internal/chi_square_test.cc create mode 100644 absl/random/internal/distribution_caller.h create mode 100644 absl/random/internal/distribution_impl.h create mode 100644 absl/random/internal/distribution_impl_test.cc create mode 100644 absl/random/internal/distribution_test_util.cc create mode 100644 absl/random/internal/distribution_test_util.h create mode 100644 absl/random/internal/distribution_test_util_test.cc create mode 100644 absl/random/internal/distributions.h create mode 100644 absl/random/internal/explicit_seed_seq.h create mode 100644 absl/random/internal/explicit_seed_seq_test.cc create mode 100644 absl/random/internal/fast_uniform_bits.h create mode 100644 absl/random/internal/fast_uniform_bits_test.cc create mode 100644 absl/random/internal/fastmath.h create mode 100644 absl/random/internal/fastmath_test.cc create mode 100644 absl/random/internal/gaussian_distribution_gentables.cc create mode 100644 absl/random/internal/iostream_state_saver.h create mode 100644 absl/random/internal/iostream_state_saver_test.cc create mode 100644 absl/random/internal/named_generator.cc create mode 100644 absl/random/internal/nanobenchmark.cc create mode 100644 absl/random/internal/nanobenchmark.h create mode 100644 absl/random/internal/nanobenchmark_test.cc create mode 100644 absl/random/internal/nonsecure_base.h create mode 100644 absl/random/internal/nonsecure_base_test.cc create mode 100644 absl/random/internal/pcg_engine.h create mode 100644 absl/random/internal/pcg_engine_test.cc create mode 100644 absl/random/internal/platform.h create mode 100644 absl/random/internal/pool_urbg.cc create mode 100644 absl/random/internal/pool_urbg.h create mode 100644 absl/random/internal/pool_urbg_test.cc create mode 100644 absl/random/internal/randen-keys.inc create mode 100644 absl/random/internal/randen.cc create mode 100644 absl/random/internal/randen.h create mode 100644 absl/random/internal/randen_benchmarks.cc create mode 100644 absl/random/internal/randen_detect.cc create mode 100644 absl/random/internal/randen_detect.h create mode 100644 absl/random/internal/randen_engine.h create mode 100644 absl/random/internal/randen_engine_test.cc create mode 100644 absl/random/internal/randen_hwaes.cc create mode 100644 absl/random/internal/randen_hwaes.h create mode 100644 absl/random/internal/randen_hwaes_test.cc create mode 100644 absl/random/internal/randen_slow.cc create mode 100644 absl/random/internal/randen_slow.h create mode 100644 absl/random/internal/randen_slow_test.cc create mode 100644 absl/random/internal/randen_test.cc create mode 100644 absl/random/internal/randen_traits.h create mode 100644 absl/random/internal/salted_seed_seq.h create mode 100644 absl/random/internal/salted_seed_seq_test.cc create mode 100644 absl/random/internal/seed_material.cc create mode 100644 absl/random/internal/seed_material.h create mode 100644 absl/random/internal/seed_material_test.cc create mode 100644 absl/random/internal/seed_salting_sequence_generator.cc create mode 100644 absl/random/internal/seed_salting_sequence_generator_empty_sequence.cc create mode 100644 absl/random/internal/sequence_urbg.h create mode 100644 absl/random/internal/traits.h create mode 100644 absl/random/internal/traits_test.cc create mode 100644 absl/random/internal/uniform_helper.h create mode 100644 absl/random/log_uniform_int_distribution.h create mode 100644 absl/random/log_uniform_int_distribution_test.cc create mode 100644 absl/random/poisson_distribution.h create mode 100644 absl/random/poisson_distribution_test.cc create mode 100644 absl/random/random.h create mode 100644 absl/random/seed_gen_exception.cc create mode 100644 absl/random/seed_gen_exception.h create mode 100644 absl/random/seed_sequences.cc create mode 100644 absl/random/seed_sequences.h create mode 100644 absl/random/seed_sequences_test.cc create mode 100644 absl/random/uniform_int_distribution.h create mode 100644 absl/random/uniform_int_distribution_test.cc create mode 100644 absl/random/uniform_real_distribution.h create mode 100644 absl/random/uniform_real_distribution_test.cc create mode 100644 absl/random/zipf_distribution.h create mode 100644 absl/random/zipf_distribution_test.cc create mode 100644 absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay create mode 100644 absl/types/compare.h create mode 100644 absl/types/compare_test.cc create mode 100644 absl/types/internal/optional.h create mode 100644 absl/types/internal/span.h delete mode 100644 absl/types/optional.cc create mode 100755 ci/cmake_install_test.sh create mode 100755 ci/linux_clang-latest_libcxx_asan_bazel.sh create mode 100755 ci/linux_clang-latest_libcxx_bazel.sh create mode 100755 ci/linux_clang-latest_libcxx_tsan_bazel.sh create mode 100755 ci/linux_clang-latest_libstdcxx_bazel.sh create mode 100755 ci/linux_gcc-4.8_libstdcxx_cmake.sh create mode 100755 ci/linux_gcc-4.9_libstdcxx_bazel.sh create mode 100755 ci/linux_gcc-latest_libstdcxx_bazel.sh create mode 100755 ci/linux_gcc-latest_libstdcxx_cmake.sh create mode 100755 ci/macos_xcode_bazel.sh create mode 100755 ci/macos_xcode_cmake.sh create mode 100644 conanfile.py diff --git a/.gitignore b/.gitignore index 7175c4f8..d54fa5a9 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ build CMakeLists.txt.user # Ignore VS Code files .vscode/* +# Ignore generated python artifacts +*.pyc +copts/__pycache__/ diff --git a/CMake/AbseilConfigureCopts.cmake b/CMake/AbseilConfigureCopts.cmake deleted file mode 100644 index 96e0390b..00000000 --- a/CMake/AbseilConfigureCopts.cmake +++ /dev/null @@ -1,145 +0,0 @@ -# Abseil-specific compiler flags. See absl/copts.bzl for description. -# DO NOT CHANGE THIS FILE WITHOUT THE CORRESPONDING CHANGE TO absl/copts.bzl - -list(APPEND GCC_FLAGS - -Wall - -Wextra - -Wcast-qual - -Wconversion-null - -Wmissing-declarations - -Woverlength-strings - -Wpointer-arith - -Wunused-local-typedefs - -Wunused-result - -Wvarargs - -Wwrite-strings - -Wno-sign-compare -) - -list(APPEND GCC_TEST_FLAGS - -Wno-conversion-null - -Wno-missing-declarations - -Wno-sign-compare - -Wno-unused-function - -Wno-unused-parameter - -Wno-unused-private-field -) - -list(APPEND LLVM_FLAGS - -Wall - -Wextra - -Weverything - -Wno-c++98-compat-pedantic - -Wno-conversion - -Wno-covered-switch-default - -Wno-deprecated - -Wno-disabled-macro-expansion - -Wno-double-promotion - -Wno-comma - -Wno-extra-semi - -Wno-packed - -Wno-padded - -Wno-sign-compare - -Wno-float-conversion - -Wno-float-equal - -Wno-format-nonliteral - -Wno-gcc-compat - -Wno-global-constructors - -Wno-exit-time-destructors - -Wno-nested-anon-types - -Wno-non-modular-include-in-module - -Wno-old-style-cast - -Wno-range-loop-analysis - -Wno-reserved-id-macro - -Wno-shorten-64-to-32 - -Wno-switch-enum - -Wno-thread-safety-negative - -Wno-undef - -Wno-unknown-warning-option - -Wno-unreachable-code - -Wno-unused-macros - -Wno-weak-vtables - -Wbitfield-enum-conversion - -Wbool-conversion - -Wconstant-conversion - -Wenum-conversion - -Wint-conversion - -Wliteral-conversion - -Wnon-literal-null-conversion - -Wnull-conversion - -Wobjc-literal-conversion - -Wno-sign-conversion - -Wstring-conversion -) - -list(APPEND LLVM_TEST_FLAGS - -Wno-c99-extensions - -Wno-missing-noreturn - -Wno-missing-prototypes - -Wno-missing-variable-declarations - -Wno-null-conversion - -Wno-shadow - -Wno-shift-sign-overflow - -Wno-sign-compare - -Wno-unused-function - -Wno-unused-member-function - -Wno-unused-parameter - -Wno-unused-private-field - -Wno-unused-template - -Wno-used-but-marked-unused - -Wno-zero-as-null-pointer-constant - -Wno-gnu-zero-variadic-macro-arguments -) - -list(APPEND MSVC_FLAGS - /W3 - /wd4005 - /wd4018 - /wd4068 - /wd4180 - /wd4244 - /wd4267 - /wd4800 - /DNOMINMAX - /DWIN32_LEAN_AND_MEAN - /D_CRT_SECURE_NO_WARNINGS - /D_SCL_SECURE_NO_WARNINGS - /D_ENABLE_EXTENDED_ALIGNED_STORAGE -) - -list(APPEND MSVC_TEST_FLAGS - /wd4101 - /wd4503 -) - -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(ABSL_DEFAULT_COPTS "${GCC_FLAGS}") - set(ABSL_TEST_COPTS "${GCC_FLAGS};${GCC_TEST_FLAGS}") - set(ABSL_EXCEPTIONS_FLAG "-fexceptions") -elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - # MATCHES so we get both Clang and AppleClang - set(ABSL_DEFAULT_COPTS "${LLVM_FLAGS}") - set(ABSL_TEST_COPTS "${LLVM_FLAGS};${LLVM_TEST_FLAGS}") - set(ABSL_EXCEPTIONS_FLAG "-fexceptions") -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(ABSL_DEFAULT_COPTS "${MSVC_FLAGS}") - set(ABSL_TEST_COPTS "${MSVC_FLAGS};${MSVC_TEST_FLAGS}") - set(ABSL_EXCEPTIONS_FLAG "/U_HAS_EXCEPTIONS;/D_HAS_EXCEPTIONS=1;/EHsc") -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 "") -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}") - message(STATUS "No CMAKE_CXX_STANDARD set, assuming 11") - set(ABSL_CXX_STANDARD 11) -else() - set(ABSL_CXX_STANDARD "${CMAKE_CXX_STANDARD}") -endif() diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 5402bf51..58f98c8c 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -16,6 +16,7 @@ include(CMakeParseArguments) include(AbseilConfigureCopts) +include(AbseilInstallDirs) # The IDE folder for Abseil that will be used if Abseil is included in a CMake # project that sets @@ -23,53 +24,8 @@ include(AbseilConfigureCopts) # For example, Visual Studio supports folders. set(ABSL_IDE_FOLDER Abseil) +# absl_cc_library() # -# create a library in the absl namespace -# -# parameters -# SOURCES : sources files for the library -# PUBLIC_LIBRARIES: targets and flags for linking phase -# PRIVATE_COMPILE_FLAGS: compile flags for the library. Will not be exported. -# EXPORT_NAME: export name for the absl:: target export -# TARGET: target name -# -# create a target associated to -# libraries are installed under CMAKE_INSTALL_FULL_LIBDIR by default -# -function(absl_library) - cmake_parse_arguments(ABSL_LIB - "DISABLE_INSTALL" # keep that in case we want to support installation one day - "TARGET;EXPORT_NAME" - "SOURCES;PUBLIC_LIBRARIES;PRIVATE_COMPILE_FLAGS" - ${ARGN} - ) - - set(_NAME ${ABSL_LIB_TARGET}) - string(TOUPPER ${_NAME} _UPPER_NAME) - - add_library(${_NAME} STATIC ${ABSL_LIB_SOURCES}) - - target_compile_options(${_NAME} - PRIVATE - ${ABSL_LIB_PRIVATE_COMPILE_FLAGS} - ${ABSL_DEFAULT_COPTS} - ) - target_link_libraries(${_NAME} PUBLIC ${ABSL_LIB_PUBLIC_LIBRARIES}) - target_include_directories(${_NAME} - PUBLIC ${ABSL_COMMON_INCLUDE_DIRS} ${ABSL_LIB_PUBLIC_INCLUDE_DIRS} - PRIVATE ${ABSL_LIB_PRIVATE_INCLUDE_DIRS} - ) - # Add all Abseil targets to a a folder in the IDE for organization. - set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}) - - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) - - if(ABSL_LIB_EXPORT_NAME) - add_library(absl::${ABSL_LIB_EXPORT_NAME} ALIAS ${_NAME}) - endif() -endfunction() - # CMake function to imitate Bazel's cc_library rule. # # Parameters: @@ -80,13 +36,13 @@ endfunction() # COPTS: List of private compile options # DEFINES: List of public defines # LINKOPTS: List of link options -# PUBLIC: Add this so that this library will be exported under absl:: (see Note). +# PUBLIC: Add this so that this library will be exported under absl:: # Also in IDE, target will appear in Abseil folder while non PUBLIC will be in Abseil/internal. # TESTONLY: When added, this target will only be built if user passes -DABSL_RUN_TESTS=ON to CMake. # # Note: -# By default, absl_cc_library will always create a library named absl_internal_${NAME}, -# and alias target absl::${NAME}. +# By default, absl_cc_library will always create a library named absl_${NAME}, +# and alias target absl::${NAME}. The absl:: form should always be used. # This is to reduce namespace pollution. # # absl_cc_library( @@ -103,21 +59,18 @@ endfunction() # SRCS # "b.cc" # DEPS -# absl_internal_awesome # not "awesome"! +# absl::awesome # not "awesome" ! +# PUBLIC # ) # -# If PUBLIC is set, absl_cc_library will instead create a target named -# absl_${NAME} and still an alias absl::${NAME}. -# # absl_cc_library( # NAME # main_lib # ... -# PUBLIC +# DEPS +# absl::fantastic_lib # ) # -# User can then use the library as absl::main_lib (although absl_main_lib is defined too). -# # TODO: Implement "ALWAYSLINK" function(absl_cc_library) cmake_parse_arguments(ABSL_CC_LIB @@ -127,15 +80,24 @@ function(absl_cc_library) ${ARGN} ) - if (NOT ABSL_CC_LIB_TESTONLY OR ABSL_RUN_TESTS) - if (ABSL_CC_LIB_PUBLIC) - set(_NAME "absl_${ABSL_CC_LIB_NAME}") + if(NOT ABSL_CC_LIB_TESTONLY OR ABSL_RUN_TESTS) + if(ABSL_ENABLE_INSTALL) + set(_NAME "${ABSL_CC_LIB_NAME}") else() - set(_NAME "absl_internal_${ABSL_CC_LIB_NAME}") + set(_NAME "absl_${ABSL_CC_LIB_NAME}") endif() # Check if this is a header-only library - if ("${ABSL_CC_LIB_SRCS}" STREQUAL "") + # Note that as of February 2019, many popular OS's (for example, Ubuntu + # 16.04 LTS) only come with cmake 3.5 by default. For this reason, we can't + # use list(FILTER...) + set(ABSL_CC_SRCS "${ABSL_CC_LIB_SRCS}") + foreach(src_file IN LISTS ABSL_CC_SRCS) + if(${src_file} MATCHES ".*\\.(h|inc)") + list(REMOVE_ITEM ABSL_CC_SRCS "${src_file}") + endif() + endforeach() + if("${ABSL_CC_SRCS}" STREQUAL "") set(ABSL_CC_LIB_IS_INTERFACE 1) else() set(ABSL_CC_LIB_IS_INTERFACE 0) @@ -145,12 +107,17 @@ function(absl_cc_library) add_library(${_NAME} STATIC "") target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS}) target_include_directories(${_NAME} - PUBLIC ${ABSL_COMMON_INCLUDE_DIRS}) + PUBLIC + $ + $ + ) target_compile_options(${_NAME} PRIVATE ${ABSL_CC_LIB_COPTS}) target_link_libraries(${_NAME} PUBLIC ${ABSL_CC_LIB_DEPS} - PRIVATE ${ABSL_CC_LIB_LINKOPTS} + PRIVATE + ${ABSL_CC_LIB_LINKOPTS} + ${ABSL_DEFAULT_LINKOPTS} ) target_compile_definitions(${_NAME} PUBLIC ${ABSL_CC_LIB_DEFINES}) @@ -166,17 +133,40 @@ function(absl_cc_library) # INTERFACE libraries can't have the CXX_STANDARD property set set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + + # When being installed, we lose the absl_ prefix. We want to put it back + # to have properly named lib files. This is a no-op when we are not being + # installed. + set_target_properties(${_NAME} PROPERTIES + OUTPUT_NAME "absl_${_NAME}" + ) else() # Generating header-only library add_library(${_NAME} INTERFACE) target_include_directories(${_NAME} - INTERFACE ${ABSL_COMMON_INCLUDE_DIRS}) + INTERFACE + $ + $ + ) target_link_libraries(${_NAME} - INTERFACE ${ABSL_CC_LIB_DEPS} ${ABSL_CC_LIB_LINKOPTS} + INTERFACE + ${ABSL_CC_LIB_DEPS} + ${ABSL_CC_LIB_LINKOPTS} + ${ABSL_DEFAULT_LINKOPTS} ) target_compile_definitions(${_NAME} INTERFACE ${ABSL_CC_LIB_DEFINES}) endif() + # TODO currently we don't install googletest alongside abseil sources, so + # installed abseil can't be tested. + if(NOT ABSL_CC_LIB_TESTONLY AND ABSL_ENABLE_INSTALL) + install(TARGETS ${_NAME} EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${ABSL_INSTALL_BINDIR} + LIBRARY DESTINATION ${ABSL_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${ABSL_INSTALL_LIBDIR} + ) + endif() + add_library(absl::${ABSL_CC_LIB_NAME} ALIAS ${_NAME}) endif() endfunction() @@ -256,116 +246,10 @@ function(absl_cc_test) add_test(NAME ${_NAME} COMMAND ${_NAME}) endfunction() -# -# header only virtual target creation -# -function(absl_header_library) - cmake_parse_arguments(ABSL_HO_LIB - "DISABLE_INSTALL" - "EXPORT_NAME;TARGET" - "PUBLIC_LIBRARIES;PRIVATE_COMPILE_FLAGS;PUBLIC_INCLUDE_DIRS;PRIVATE_INCLUDE_DIRS" - ${ARGN} - ) - - set(_NAME ${ABSL_HO_LIB_TARGET}) - - set(__dummy_header_only_lib_file "${CMAKE_CURRENT_BINARY_DIR}/${_NAME}_header_only_dummy.cc") - - if(NOT EXISTS ${__dummy_header_only_lib_file}) - file(WRITE ${__dummy_header_only_lib_file} - "/* generated file for header-only cmake target */ - - namespace absl { - - // single meaningless symbol - void ${_NAME}__header_fakesym() {} - } // namespace absl - " - ) - endif() - - - add_library(${_NAME} ${__dummy_header_only_lib_file}) - target_link_libraries(${_NAME} PUBLIC ${ABSL_HO_LIB_PUBLIC_LIBRARIES}) - target_include_directories(${_NAME} - PUBLIC ${ABSL_COMMON_INCLUDE_DIRS} ${ABSL_HO_LIB_PUBLIC_INCLUDE_DIRS} - PRIVATE ${ABSL_HO_LIB_PRIVATE_INCLUDE_DIRS} - ) - - # Add all Abseil targets to a a folder in the IDE for organization. - set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}) - - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) - - if(ABSL_HO_LIB_EXPORT_NAME) - add_library(absl::${ABSL_HO_LIB_EXPORT_NAME} ALIAS ${_NAME}) - endif() - -endfunction() - -# -# create an abseil unit_test and add it to the executed test list -# -# parameters -# TARGET: target name prefix -# SOURCES: sources files for the tests -# PUBLIC_LIBRARIES: targets and flags for linking phase. -# PRIVATE_COMPILE_FLAGS: compile flags for the test. Will not be exported. -# -# create a target associated to _bin -# -# all tests will be register for execution with add_test() -# -# test compilation and execution is disable when ABSL_RUN_TESTS=OFF -# -function(absl_test) - - cmake_parse_arguments(ABSL_TEST - "" - "TARGET" - "SOURCES;PUBLIC_LIBRARIES;PRIVATE_COMPILE_FLAGS;PUBLIC_INCLUDE_DIRS" - ${ARGN} - ) - - - if(ABSL_RUN_TESTS) - - set(_NAME "absl_${ABSL_TEST_TARGET}") - string(TOUPPER ${_NAME} _UPPER_NAME) - - add_executable(${_NAME} ${ABSL_TEST_SOURCES}) - - target_compile_options(${_NAME} - PRIVATE - ${ABSL_TEST_PRIVATE_COMPILE_FLAGS} - ${ABSL_TEST_COPTS} - ) - target_link_libraries(${_NAME} PUBLIC ${ABSL_TEST_PUBLIC_LIBRARIES} ${ABSL_TEST_COMMON_LIBRARIES}) - target_include_directories(${_NAME} - PUBLIC ${ABSL_COMMON_INCLUDE_DIRS} ${ABSL_TEST_PUBLIC_INCLUDE_DIRS} - PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} - ) - - # Add all Abseil targets to a a folder in the IDE for organization. - set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}) - - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) - set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) - - add_test(NAME ${_NAME} COMMAND ${_NAME}) - endif(ABSL_RUN_TESTS) - -endfunction() - - - function(check_target my_target) - if(NOT TARGET ${my_target}) message(FATAL_ERROR " ABSL: compiling absl requires a ${my_target} CMake target in your project, see CMake/README.md for more details") endif(NOT TARGET ${my_target}) - endfunction() diff --git a/CMake/AbseilInstallDirs.cmake b/CMake/AbseilInstallDirs.cmake new file mode 100644 index 00000000..b67272f8 --- /dev/null +++ b/CMake/AbseilInstallDirs.cmake @@ -0,0 +1,20 @@ +include(GNUInstallDirs) + +# absl_VERSION is only set if we are an LTS release being installed, in which +# case it may be into a system directory and so we need to make subdirectories +# for each installed version of Abseil. This mechanism is implemented in +# Abseil's internal Copybara (https://github.com/google/copybara) workflows and +# isn't visible in the CMake buildsystem itself. + +if(absl_VERSION) + set(ABSL_SUBDIR "${PROJECT_NAME}_${PROJECT_VERSION}") + set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}/${ABSL_SUBDIR}") + set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${ABSL_SUBDIR}") + set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/{ABSL_SUBDIR}") + set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/${ABSL_SUBDIR}") +else() + set(ABSL_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}") + set(ABSL_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + set(ABSL_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") + set(ABSL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}") +endif() \ No newline at end of file diff --git a/CMake/CMakeLists.txt.in b/CMake/CMakeLists.txt.in deleted file mode 100644 index d60a33e9..00000000 --- a/CMake/CMakeLists.txt.in +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 2.8.2) - -project(googletest-download NONE) - -include(ExternalProject) -ExternalProject_Add(googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG master - SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" - BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" -) \ No newline at end of file diff --git a/CMake/DownloadGTest.cmake b/CMake/DownloadGTest.cmake deleted file mode 100644 index 3c682aef..00000000 --- a/CMake/DownloadGTest.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# Downloads and unpacks googletest at configure time. Based on the instructions -# at https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project - -# Download the latest googletest from Github master -configure_file( - ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in - ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt -) - -# Configure and build the downloaded googletest source -execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) -if(result) - message(FATAL_ERROR "CMake step for googletest failed: ${result}") -endif() - -execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) -if(result) - message(FATAL_ERROR "Build step for googletest failed: ${result}") -endif() - -# Prevent overriding the parent project's compiler/linker settings on Windows -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - -# Add googletest directly to our build. This defines the gtest and gtest_main -# targets. -add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src - ${CMAKE_BINARY_DIR}/googletest-build - EXCLUDE_FROM_ALL) diff --git a/CMake/Googletest/CMakeLists.txt.in b/CMake/Googletest/CMakeLists.txt.in new file mode 100644 index 00000000..d60a33e9 --- /dev/null +++ b/CMake/Googletest/CMakeLists.txt.in @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) \ No newline at end of file diff --git a/CMake/Googletest/DownloadGTest.cmake b/CMake/Googletest/DownloadGTest.cmake new file mode 100644 index 00000000..3c682aef --- /dev/null +++ b/CMake/Googletest/DownloadGTest.cmake @@ -0,0 +1,32 @@ +# Downloads and unpacks googletest at configure time. Based on the instructions +# at https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project + +# Download the latest googletest from Github master +configure_file( + ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in + ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt +) + +# Configure and build the downloaded googletest source +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) +if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") +endif() + +execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) +if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") +endif() + +# Prevent overriding the parent project's compiler/linker settings on Windows +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +# Add googletest directly to our build. This defines the gtest and gtest_main +# targets. +add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src + ${CMAKE_BINARY_DIR}/googletest-build + EXCLUDE_FROM_ALL) diff --git a/CMake/README.md b/CMake/README.md index 79bbe24d..469dfef5 100644 --- a/CMake/README.md +++ b/CMake/README.md @@ -3,7 +3,7 @@ Abseil comes with a CMake build script ([CMakeLists.txt](../CMakeLists.txt)) that can be used on a wide range of platforms ("C" stands for cross-platform.). If you don't have CMake installed already, you can download it for free from -. +. CMake works by generating native makefiles or build projects that can be used in the compiler environment of your choice. @@ -37,20 +37,12 @@ section of your executable or of your library.
Here is a short CMakeLists.txt example of a project file using Abseil. ```cmake -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(my_project) -set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++ ${CMAKE_CXX_FLAGS}") - -if(MSVC) - # /wd4005 macro-redefinition - # /wd4068 unknown pragma - # /wd4244 conversion from 'type1' to 'type2' - # /wd4267 conversion from 'size_t' to 'type2' - # /wd4800 force value to bool 'true' or 'false' (performance warning) - add_compile_options(/wd4005 /wd4068 /wd4244 /wd4267 /wd4800) - add_definitions(/DNOMINMAX /DWIN32_LEAN_AND_MEAN=1 /D_CRT_SECURE_NO_WARNINGS) -endif() +# Pick the C++ standard to compile with. +# Abseil currently supports C++11, C++14, and C++17. +set(CMAKE_CXX_STANDARD 11) add_subdirectory(abseil-cpp) @@ -95,8 +87,8 @@ Here's a non-exhaustive list of Abseil CMake public targets: ```cmake absl::base absl::algorithm -absl::container absl::debugging +absl::flat_hash_map absl::memory absl::meta absl::numeric diff --git a/CMake/abslConfig.cmake.in b/CMake/abslConfig.cmake.in new file mode 100644 index 00000000..60847fa7 --- /dev/null +++ b/CMake/abslConfig.cmake.in @@ -0,0 +1,7 @@ +# absl CMake configuration file. + +include(FindThreads) + +@PACKAGE_INIT@ + +include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") \ No newline at end of file diff --git a/CMake/install_test_project/CMakeLists.txt b/CMake/install_test_project/CMakeLists.txt new file mode 100644 index 00000000..06b797e9 --- /dev/null +++ b/CMake/install_test_project/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# 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. + +# A simple CMakeLists.txt for testing cmake installation + +cmake_minimum_required(VERSION 3.5) +project(absl_cmake_testing CXX) + +set(CMAKE_CXX_STANDARD 11) + +add_executable(simple simple.cc) + +find_package(absl REQUIRED) + +target_link_libraries(simple absl::strings) diff --git a/CMake/install_test_project/simple.cc b/CMake/install_test_project/simple.cc new file mode 100644 index 00000000..e9e35291 --- /dev/null +++ b/CMake/install_test_project/simple.cc @@ -0,0 +1,23 @@ +// +// 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 +#include "absl/strings/substitute.h" + +int main(int argc, char** argv) { + for (int i = 0; i < argc; ++i) { + std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]); + } +} diff --git a/CMake/install_test_project/test.sh b/CMake/install_test_project/test.sh new file mode 100755 index 00000000..99989b03 --- /dev/null +++ b/CMake/install_test_project/test.sh @@ -0,0 +1,144 @@ +#!/bin/bash +# +# Copyright 2019 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# "Unit" and integration tests for Absl CMake installation + +# TODO(absl-team): This script isn't fully hermetic because +# -DABSL_USE_GOOGLETEST_HEAD=ON means that this script isn't pinned to a fixed +# version of GoogleTest. This means that an upstream change to GoogleTest could +# break this test. Fix this by allowing this script to pin to a known-good +# version of GoogleTest. + +# Fail on any error. Treat unset variables an error. Print commands as executed. +set -euox pipefail + +install_absl() { + pushd "${absl_build_dir}" + if [[ "${#}" -eq 1 ]]; then + cmake -DCMAKE_INSTALL_PREFIX="${1}" "${absl_dir}" + else + cmake "${absl_dir}" + fi + cmake --build . --target install -- -j + popd +} + +uninstall_absl() { + xargs rm < "${absl_build_dir}"/install_manifest.txt + rm -rf "${absl_build_dir}" + mkdir -p "${absl_build_dir}" +} + +lts_install="" + +while getopts ":l" lts; do + case "${lts}" in + l ) + lts_install="true" + ;; + esac +done + +absl_dir=/abseil-cpp +absl_build_dir=/buildfs/absl-build +project_dir="${absl_dir}"/CMake/install_test_project +project_build_dir=/buildfs/project-build + +mkdir -p "${absl_build_dir}" +mkdir -p "${project_build_dir}" + +if [[ "${lts_install}" ]]; then + install_dir="/usr/local" +else + install_dir="${project_build_dir}"/install +fi +mkdir -p "${install_dir}" + +# Test build, install, and link against installed abseil +pushd "${project_build_dir}" +if [[ "${lts_install}" ]]; then + install_absl + cmake "${project_dir}" +else + install_absl "${install_dir}" + cmake "${project_dir}" -DCMAKE_PREFIX_PATH="${install_dir}" +fi + +cmake --build . --target simple + +output="$(${project_build_dir}/simple "printme" 2>&1)" +if [[ "${output}" != *"Arg 1: printme"* ]]; then + echo "Faulty output on simple project:" + echo "${output}" + exit 1 +fi + +popd + +# Test that we haven't accidentally made absl::abslblah +pushd "${install_dir}" + +# Starting in CMake 3.12 the default install dir is lib$bit_width +if [[ -d lib64 ]]; then + libdir="lib64" +elif [[ -d lib ]]; then + libdir="lib" +else + echo "ls *, */*, */*/*:" + ls * + ls */* + ls */*/* + echo "unknown lib dir" +fi + +if [[ "${lts_install}" ]]; then + # LTS versions append the date of the release to the subdir. + # 9999/99/99 is the dummy date used in the local_lts workflow. + absl_subdir="absl_99999999" +else + absl_subdir="absl" +fi + +if ! grep absl::strings "${libdir}/cmake/${absl_subdir}/abslTargets.cmake"; then + cat "${libdir}"/cmake/absl/abslTargets.cmake + echo "CMake targets named incorrectly" + exit 1 +fi + +uninstall_absl +popd + +if [[ ! "${lts_install}" ]]; then + # Test that we warn if installed without a prefix or a system prefix + output="$(install_absl 2>&1)" + if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then + echo "Install without prefix didn't warn as expected. Output:" + echo "${output}" + exit 1 + fi + uninstall_absl + + output="$(install_absl /usr 2>&1)" + if [[ "${output}" != *"Please set CMAKE_INSTALL_PREFIX"* ]]; then + echo "Install with /usr didn't warn as expected. Output:" + echo "${output}" + exit 1 + fi + uninstall_absl +fi + +echo "Install test complete!" +exit 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 1eafa407..74a3a4ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,44 +14,40 @@ # limitations under the License. # -# We require 3.0 for modern, target-based CMake. We require 3.1 for the use of -# CXX_STANDARD in our targets. -cmake_minimum_required(VERSION 3.1) +# Most widely used distributions have cmake 3.5 or greater available as of March +# 2019. A notable exception is RHEL-7 (CentOS7). You can install a current +# version of CMake by first installing Extra Packages for Enterprise Linux +# (https://fedoraproject.org/wiki/EPEL#Extra_Packages_for_Enterprise_Linux_.28EPEL.29) +# and then issuing `yum install cmake3` on the command line. +cmake_minimum_required(VERSION 3.5) # Compiler id for Apple Clang is now AppleClang. -if (POLICY CMP0025) - cmake_policy(SET CMP0025 NEW) -endif() - -project(absl) +cmake_policy(SET CMP0025 NEW) -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake) +# if command can use IN_LIST +cmake_policy(SET CMP0057 NEW) -include(GNUInstallDirs) -include(AbseilHelpers) +# Project version variables are the empty std::string if version is unspecified +cmake_policy(SET CMP0048 NEW) +project(absl CXX) -# config options -if (MSVC) - # /wd4005 macro-redefinition - # /wd4068 unknown pragma - # /wd4244 conversion from 'type1' to 'type2' - # /wd4267 conversion from 'size_t' to 'type2' - # /wd4800 force value to bool 'true' or 'false' (performance warning) - add_compile_options(/W3 /wd4005 /wd4068 /wd4244 /wd4267 /wd4800) - # /D_ENABLE_EXTENDED_ALIGNED_STORAGE Introduced in VS 2017 15.8, before the - # member type would non-conformingly have an alignment of only alignof(max_align_t). - add_definitions( - /DNOMINMAX - /DWIN32_LEAN_AND_MEAN=1 - /D_CRT_SECURE_NO_WARNINGS - /D_SCL_SECURE_NO_WARNINGS - /D_ENABLE_EXTENDED_ALIGNED_STORAGE - ) +# when absl is included as subproject (i.e. using add_subdirectory(abseil-cpp)) +# in the source tree of a project that uses it, install rules are disabled. +if(NOT "^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$") + set(ABSL_ENABLE_INSTALL FALSE) else() - set(ABSL_STD_CXX_FLAG "-std=c++11" CACHE STRING "c++ std flag (default: c++11)") + set(ABSL_ENABLE_INSTALL TRUE) endif() +list(APPEND CMAKE_MODULE_PATH + ${CMAKE_CURRENT_LIST_DIR}/CMake + ${CMAKE_CURRENT_LIST_DIR}/absl/copts +) + +include(AbseilInstallDirs) +include(CMakePackageConfigHelpers) +include(AbseilHelpers) ## @@ -68,12 +64,6 @@ endif() # include current path list(APPEND ABSL_COMMON_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) -# -std=X -set(CMAKE_CXX_FLAGS "${ABSL_STD_CXX_FLAG} ${CMAKE_CXX_FLAGS}") - -# -fexceptions -set(ABSL_EXCEPTIONS_FLAG "${CMAKE_CXX_EXCEPTIONS}") - if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(ABSL_USING_CLANG ON) else() @@ -100,7 +90,9 @@ endif() if(BUILD_TESTING) if(${ABSL_USE_GOOGLETEST_HEAD}) - include(CMake/DownloadGTest.cmake) + include(CMake/Googletest/DownloadGTest.cmake) + set(absl_gtest_src_dir ${CMAKE_BINARY_DIR}/googletest-src) + set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build) endif() check_target(gtest) @@ -116,3 +108,42 @@ if(BUILD_TESTING) endif() add_subdirectory(absl) + +if(ABSL_ENABLE_INSTALL) + + # install as a subdirectory only + install(EXPORT ${PROJECT_NAME}Targets + NAMESPACE absl:: + DESTINATION "${ABSL_INSTALL_CONFIGDIR}" + ) + + configure_package_config_file( + CMake/abslConfig.cmake.in + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION "${ABSL_INSTALL_CONFIGDIR}" + ) + install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + DESTINATION "${ABSL_INSTALL_CONFIGDIR}" + ) + + # Abseil only has a version in LTS releases. This mechanism is accomplished + # Abseil's internal Copybara (https://github.com/google/copybara) workflows and + # isn't visible in the CMake buildsystem itself. + if(absl_VERSION) + write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + COMPATIBILITY ExactVersion + ) + + install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION ${ABSL_INSTALL_CONFIGDIR} + ) + endif() # absl_VERSION + + install(DIRECTORY absl + DESTINATION ${ABSL_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.inc" + PATTERN "*.h" + ) +endif() # ABSL_ENABLE_INSTALL diff --git a/LICENSE b/LICENSE index fef7d967..ccd61dcf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -193,12 +193,11 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - - \ No newline at end of file + diff --git a/LTS.md b/LTS.md index 385b4f06..08606f15 100644 --- a/LTS.md +++ b/LTS.md @@ -10,4 +10,5 @@ turn, use Abseil. (For more information about our releases, see the The following lists LTS branches and the dates on which they have been released: +* [LTS Branch December 18, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_12_18/) * [LTS Branch June 20, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_06_20/) diff --git a/README.md b/README.md index e9362be2..85de5696 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ the Abseil code, running tests, and getting a simple binary working. ## Building Abseil -[Bazel](http://bazel.build) is the official build system for Abseil, -which is supported on most major platforms (Linux, Windows, MacOS, for example) +[Bazel](https://bazel.build) is the official build system for Abseil, +which is supported on most major platforms (Linux, Windows, macOS, for example) and compilers. See the [quickstart](https://abseil.io/docs/cpp/quickstart) for more information on building Abseil using the Bazel build system. @@ -106,9 +106,9 @@ license. See [LICENSE](LICENSE) for more information. For more information about Abseil: -* Consult our [Abseil Introduction](http://abseil.io/about/intro) -* Read [Why Adopt Abseil](http://abseil.io/about/philosophy) to understand our +* Consult our [Abseil Introduction](https://abseil.io/about/intro) +* Read [Why Adopt Abseil](https://abseil.io/about/philosophy) to understand our design philosophy. * Peruse our - [Abseil Compatibility Guarantees](http://abseil.io/about/compatibility) to + [Abseil Compatibility Guarantees](https://abseil.io/about/compatibility) to understand both what we promise to you, and what we expect of you in return. diff --git a/UPGRADES.md b/UPGRADES.md new file mode 100644 index 00000000..35599d08 --- /dev/null +++ b/UPGRADES.md @@ -0,0 +1,17 @@ +# C++ Upgrade Tools + +Abseil may occassionally release API-breaking changes. As noted in our +[Compatibility Guidelines][compatibility-guide], we will aim to provide a tool +to do the work of effecting such API-breaking changes, when absolutely +necessary. + +These tools will be listed on the [C++ Upgrade Tools][upgrade-tools] guide on +https://abseil.io. + +For more information, the [C++ Automated Upgrade Guide][api-upgrades-guide] +outlines this process. + +[compatibility-guide]: https://abseil.io/about/compatibility +[api-upgrades-guide]: https://abseil.io/docs/cpp/tools/api-upgrades +[upgrade-tools]: https://abseil.io/docs/cpp/tools/upgrades/ + diff --git a/WORKSPACE b/WORKSPACE index 72ef1398..49e2d3cb 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -15,9 +15,9 @@ http_archive( # GoogleTest/GoogleMock framework. Used by most unit-tests. http_archive( name = "com_google_googletest", - urls = ["https://github.com/google/googletest/archive/b4d4438df9479675a632b2f11125e57133822ece.zip"], # 2018-07-16 - strip_prefix = "googletest-b4d4438df9479675a632b2f11125e57133822ece", - sha256 = "5aaa5d566517cae711e2a3505ea9a6438be1b37fcaae0ebcb96ccba9aa56f23a", + urls = ["https://github.com/google/googletest/archive/b6cd405286ed8635ece71c72f118e659f4ade3fb.zip"], # 2019-01-07 + strip_prefix = "googletest-b6cd405286ed8635ece71c72f118e659f4ade3fb", + sha256 = "ff7a82736e158c077e76188232eac77913a15dac0b22508c390ab3f88e6d6d86", ) # Google benchmark. diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index edd0274c..853330d4 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -25,13 +25,18 @@ create_llvm_config( visibility = [":__subpackages__"], ) -# following configs are based on mapping defined in: https://git.io/v5Ijz +config_setting( + name = "osx", + constraint_values = [ + "@bazel_tools//platforms:osx", + ], +) + config_setting( name = "ios", - values = { - "cpu": "darwin", - }, - visibility = [":__subpackages__"], + constraint_values = [ + "@bazel_tools//platforms:ios", + ], ) config_setting( diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt index 1d09b193..3e78397c 100644 --- a/absl/CMakeLists.txt +++ b/absl/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -20,10 +20,12 @@ add_subdirectory(base) add_subdirectory(algorithm) add_subdirectory(container) add_subdirectory(debugging) +add_subdirectory(flags) add_subdirectory(hash) add_subdirectory(memory) add_subdirectory(meta) add_subdirectory(numeric) +add_subdirectory(random) add_subdirectory(strings) add_subdirectory(synchronization) add_subdirectory(time) diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel index d04dc712..c506f3b9 100644 --- a/absl/algorithm/BUILD.bazel +++ b/absl/algorithm/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,9 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -28,6 +29,7 @@ cc_library( name = "algorithm", hdrs = ["algorithm.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_test( @@ -35,6 +37,7 @@ cc_test( size = "small", srcs = ["algorithm_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":algorithm", "@com_google_googletest//:gtest_main", @@ -45,6 +48,7 @@ cc_test( name = "algorithm_benchmark", srcs = ["equal_benchmark.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], deps = [ ":algorithm", @@ -59,6 +63,7 @@ cc_library( "container.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":algorithm", "//absl/base:core_headers", @@ -70,6 +75,7 @@ cc_test( name = "container_test", srcs = ["container_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container", "//absl/base", diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt index 87a165c0..9fbe36f6 100644 --- a/absl/algorithm/CMakeLists.txt +++ b/absl/algorithm/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -29,6 +29,8 @@ absl_cc_test( algorithm_test SRCS "algorithm_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::algorithm gmock_main @@ -53,6 +55,8 @@ absl_cc_test( container_test SRCS "container_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::algorithm_container absl::base diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h index 1eef16cb..7c2b787e 100644 --- a/absl/algorithm/algorithm.h +++ b/absl/algorithm/algorithm.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace algorithm_internal { @@ -95,7 +95,7 @@ It RotateImpl(It first, It middle, It last, std::false_type) { // then the predicate is never invoked and the function returns false. // // This is a C++11-compatible implementation of C++14 `std::equal`. See -// http://en.cppreference.com/w/cpp/algorithm/equal for more information. +// https://en.cppreference.com/w/cpp/algorithm/equal for more information. template bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, InputIter2 last2, Pred&& pred) { @@ -146,7 +146,7 @@ ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator>()); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_ALGORITHM_ALGORITHM_H_ diff --git a/absl/algorithm/algorithm_test.cc b/absl/algorithm/algorithm_test.cc index e4322bc4..81fccb61 100644 --- a/absl/algorithm/algorithm_test.cc +++ b/absl/algorithm/algorithm_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index b7718206..16389be7 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -36,7 +36,6 @@ // For template parameter and variable naming, `C` indicates the container type // to which the function is applied, `Pred` indicates the predicate object type // to be used by the function and `T` indicates the applicable element type. -// #ifndef ABSL_ALGORITHM_CONTAINER_H_ #define ABSL_ALGORITHM_CONTAINER_H_ @@ -56,7 +55,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_algorithm_internal { // NOTE: it is important to defer to ADL lookup for building with C++ modules, @@ -512,6 +511,16 @@ OutputIterator c_move(C&& src, OutputIterator dest) { container_algorithm_internal::c_end(src), dest); } +// c_move_backward() +// +// Container-based version of the `std::move_backward()` function to +// move a container's elements into an iterator in reverse order. +template +BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { + return std::move_backward(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + // c_swap_ranges() // // Container-based version of the `std::swap_ranges()` function to @@ -649,7 +658,6 @@ container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, // and `unique()` are omitted, because it's not clear whether or not such // functions should call erase on their supplied sequences afterwards. Either // behavior would be surprising for a different set of users. -// // c_remove_copy() // @@ -1698,7 +1706,7 @@ OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, output_first, std::forward(op)); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_ALGORITHM_CONTAINER_H_ diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc index 1502b17f..86bf9d3e 100644 --- a/absl/algorithm/container_test.cc +++ b/absl/algorithm/container_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -636,6 +636,19 @@ TEST(MutatingTest, Move) { Pointee(5))); } +TEST(MutatingTest, MoveBackward) { + std::vector> actual; + actual.emplace_back(absl::make_unique(1)); + actual.emplace_back(absl::make_unique(2)); + actual.emplace_back(absl::make_unique(3)); + actual.emplace_back(absl::make_unique(4)); + actual.emplace_back(absl::make_unique(5)); + auto subrange = absl::MakeSpan(actual.data(), 3); + absl::c_move_backward(subrange, actual.end()); + EXPECT_THAT(actual, ElementsAre(IsNull(), IsNull(), Pointee(1), Pointee(2), + Pointee(3))); +} + TEST(MutatingTest, MoveWithRvalue) { auto MakeRValueSrc = [] { std::vector> src; diff --git a/absl/algorithm/equal_benchmark.cc b/absl/algorithm/equal_benchmark.cc index 19c0780c..7bf62c9a 100644 --- a/absl/algorithm/equal_benchmark.cc +++ b/absl/algorithm/equal_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 4566c697..a512272a 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,17 +15,46 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", - "ABSL_TEST_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_EXCEPTIONS_FLAG", "ABSL_EXCEPTIONS_FLAG_LINKOPTS", + "ABSL_TEST_COPTS", ) package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 +cc_library( + name = "atomic_hook", + hdrs = ["internal/atomic_hook.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], +) + +cc_library( + name = "log_severity", + srcs = ["log_severity.cc"], + hdrs = ["log_severity.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [":core_headers"], +) + +cc_library( + name = "raw_logging_internal", + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], +) + cc_library( name = "spinlock_wait", srcs = [ @@ -40,6 +69,7 @@ cc_library( "internal/spinlock_wait.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/base:__pkg__", ], @@ -53,6 +83,7 @@ cc_library( "policy_checks.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_library( @@ -61,18 +92,24 @@ cc_library( hdrs = ["dynamic_annotations.h"], copts = ABSL_DEFAULT_COPTS, defines = ["__CLANG_SUPPORT_DYN_ANNOTATION__"], + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_library( name = "core_headers", + srcs = [ + "internal/thread_annotations.h", + ], hdrs = [ "attributes.h", + "const_init.h", "macros.h", "optimization.h", "port.h", "thread_annotations.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", ], @@ -88,6 +125,10 @@ cc_library( "internal/low_level_alloc.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = select({ + "//absl:windows": [], + "//conditions:default": ["-pthread"], + }) + ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -110,9 +151,13 @@ cc_library( "internal/scheduling_mode.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], + deps = [ + "//absl/meta:type_traits", + ], ) cc_library( @@ -128,7 +173,6 @@ cc_library( hdrs = [ "call_once.h", "casts.h", - "internal/atomic_hook.h", "internal/cycleclock.h", "internal/low_level_scheduling.h", "internal/per_thread_tls.h", @@ -138,15 +182,21 @@ cc_library( "internal/thread_identity.h", "internal/tsan_mutex_interface.h", "internal/unscaledcycleclock.h", - "log_severity.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = select({ + "//absl:windows": [], + "//conditions:default": ["-pthread"], + }) + ABSL_DEFAULT_LINKOPTS, deps = [ + ":atomic_hook", ":base_internal", ":config", ":core_headers", ":dynamic_annotations", + ":log_severity", ":spinlock_wait", + "//absl/meta:type_traits", ], ) @@ -155,8 +205,9 @@ cc_test( size = "small", srcs = ["internal/atomic_hook_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":base", + ":atomic_hook", ":core_headers", "@com_google_googletest//:gtest_main", ], @@ -169,6 +220,7 @@ cc_test( "bit_cast_test.cc", ], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -181,7 +233,7 @@ cc_library( srcs = ["internal/throw_delegate.cc"], hdrs = ["internal/throw_delegate.h"], copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -195,7 +247,7 @@ cc_test( name = "throw_delegate_test", srcs = ["throw_delegate_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":throw_delegate", "@com_google_googletest//:gtest_main", @@ -207,6 +259,7 @@ cc_library( testonly = 1, hdrs = ["internal/exception_testing.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -219,6 +272,7 @@ cc_library( cc_library( name = "pretty_function", hdrs = ["internal/pretty_function.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//absl:__subpackages__"], ) @@ -228,9 +282,8 @@ cc_library( srcs = ["internal/exception_safety_testing.cc"], hdrs = ["internal/exception_safety_testing.h"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ - ":base", ":config", ":pretty_function", "//absl/memory", @@ -245,7 +298,7 @@ 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, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":exception_safety_testing", "//absl/memory", @@ -263,6 +316,7 @@ cc_test( "internal/inline_variable_testing.h", ], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base_internal", "@com_google_googletest//:gtest_main", @@ -274,6 +328,7 @@ cc_test( size = "small", srcs = ["invoke_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base_internal", "//absl/memory", @@ -289,6 +344,7 @@ cc_library( testonly = 1, srcs = ["spinlock_test_common.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -304,7 +360,7 @@ cc_test( size = "medium", srcs = ["spinlock_test_common.cc"], copts = ABSL_TEST_COPTS, - tags = ["no_test_wasm"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -318,7 +374,8 @@ cc_library( name = "spinlock_benchmark_common", testonly = 1, srcs = ["internal/spinlock_benchmark.cc"], - copts = ABSL_DEFAULT_COPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/base:__pkg__", ], @@ -335,6 +392,7 @@ cc_binary( name = "spinlock_benchmark", testonly = 1, copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ ":spinlock_benchmark_common", @@ -348,6 +406,7 @@ cc_library( "internal/unaligned_access.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", ":core_headers", @@ -359,7 +418,6 @@ cc_test( srcs = ["internal/endian_test.cc"], copts = ABSL_TEST_COPTS, deps = [ - ":base", ":config", ":endian", "@com_google_googletest//:gtest_main", @@ -370,9 +428,7 @@ cc_test( name = "config_test", srcs = ["config_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_wasm", - ], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", "//absl/synchronization:thread_pool", @@ -384,9 +440,7 @@ cc_test( name = "call_once_test", srcs = ["call_once_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_wasm", - ], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -399,6 +453,7 @@ cc_test( name = "raw_logging_test", srcs = ["raw_logging_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", "//absl/strings", @@ -411,6 +466,7 @@ cc_test( size = "small", srcs = ["internal/sysinfo_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", "//absl/synchronization", @@ -420,13 +476,10 @@ cc_test( cc_test( name = "low_level_alloc_test", - size = "small", + size = "medium", srcs = ["internal/low_level_alloc_test.cc"], copts = ABSL_TEST_COPTS, - linkopts = select({ - "//absl:windows": [], - "//conditions:default": ["-pthread"], - }), + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["no_test_ios_x86_64"], deps = [":malloc_internal"], ) @@ -436,13 +489,7 @@ cc_test( size = "small", srcs = ["internal/thread_identity_test.cc"], copts = ABSL_TEST_COPTS, - linkopts = select({ - "//absl:windows": [], - "//conditions:default": ["-pthread"], - }), - tags = [ - "no_test_wasm", - ], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -455,6 +502,7 @@ cc_test( name = "thread_identity_benchmark", srcs = ["internal/thread_identity_benchmark.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], visibility = ["//visibility:private"], deps = [ @@ -467,6 +515,7 @@ cc_test( cc_library( name = "bits", hdrs = ["internal/bits.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -478,8 +527,46 @@ cc_test( size = "small", srcs = ["internal/bits_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bits", "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "scoped_set_env", + testonly = 1, + srcs = ["internal/scoped_set_env.cc"], + hdrs = ["internal/scoped_set_env.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [":base"], +) + +cc_test( + name = "scoped_set_env_test", + size = "small", + srcs = ["internal/scoped_set_env_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":scoped_set_env", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "log_severity_test", + size = "small", + srcs = ["log_severity_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":base", + ":log_severity", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 212dd083..cc7960e3 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,6 +14,35 @@ # limitations under the License. # +absl_cc_library( + NAME + atomic_hook + HDRS + "internal/atomic_hook.h" + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_library( + NAME + log_severity + HDRS + "log_severity.h" + SRCS + "log_severity.cc" + DEPS + absl::core_headers + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_library( + NAME + raw_logging_internal + COPTS + ${ABSL_DEFAULT_COPTS} +) + absl_cc_library( NAME spinlock_wait @@ -26,6 +55,8 @@ absl_cc_library( "internal/spinlock_posix.inc" "internal/spinlock_wait.cc" "internal/spinlock_win32.inc" + COPTS + ${ABSL_DEFAULT_COPTS} DEPS absl::core_headers ) @@ -60,10 +91,12 @@ absl_cc_library( core_headers HDRS "attributes.h" + "const_init.h" "macros.h" "optimization.h" "port.h" "thread_annotations.h" + "internal/thread_annotations.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -87,6 +120,7 @@ absl_cc_library( absl::core_headers absl::dynamic_annotations absl::spinlock_wait + Threads::Threads ) absl_cc_library( @@ -99,6 +133,8 @@ absl_cc_library( "internal/invoke.h" COPTS ${ABSL_DEFAULT_COPTS} + DEPS + absl::type_traits ) absl_cc_library( @@ -107,7 +143,6 @@ absl_cc_library( HDRS "call_once.h" "casts.h" - "internal/atomic_hook.h" "internal/cycleclock.h" "internal/low_level_scheduling.h" "internal/per_thread_tls.h" @@ -125,14 +160,19 @@ absl_cc_library( "internal/sysinfo.cc" "internal/thread_identity.cc" "internal/unscaledcycleclock.cc" + "log_severity.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::atomic_hook absl::base_internal absl::config absl::core_headers absl::dynamic_annotations + absl::log_severity absl::spinlock_wait + absl::type_traits + Threads::Threads PUBLIC ) @@ -180,10 +220,11 @@ absl_cc_library( SRCS "internal/exception_safety_testing.cc" COPTS - ${ABSL_DEFAULT_COPTS} + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS - absl::base absl::config absl::pretty_function absl::memory @@ -200,6 +241,7 @@ absl_cc_test( SRCS "exception_safety_testing_test.cc" COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} LINKOPTS ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} @@ -214,8 +256,10 @@ absl_cc_test( atomic_hook_test SRCS "internal/atomic_hook_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS - absl::base + absl::atomic_hook absl::core_headers gtest_main ) @@ -225,6 +269,8 @@ absl_cc_test( bit_cast_test SRCS "bit_cast_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::core_headers @@ -236,9 +282,11 @@ absl_cc_test( throw_delegate_test SRCS "throw_delegate_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base - absl_internal_throw_delegate + absl::throw_delegate gtest_main ) @@ -250,6 +298,8 @@ absl_cc_test( "inline_variable_test.cc" "inline_variable_test_a.cc" "inline_variable_test_b.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base_internal gtest_main @@ -260,6 +310,8 @@ absl_cc_test( invoke_test SRCS "invoke_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base_internal absl::memory @@ -290,6 +342,8 @@ absl_cc_test( spinlock_test SRCS "spinlock_test_common.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::core_headers @@ -317,6 +371,8 @@ absl_cc_test( endian_test SRCS "internal/endian_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::config @@ -329,6 +385,8 @@ absl_cc_test( config_test SRCS "config_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::config absl::synchronization @@ -340,6 +398,8 @@ absl_cc_test( call_once_test SRCS "call_once_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::core_headers @@ -352,6 +412,8 @@ absl_cc_test( raw_logging_test SRCS "raw_logging_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::strings @@ -363,6 +425,8 @@ absl_cc_test( sysinfo_test SRCS "internal/sysinfo_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::synchronization @@ -374,6 +438,8 @@ absl_cc_test( low_level_alloc_test SRCS "internal/low_level_alloc_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::malloc_internal Threads::Threads @@ -384,6 +450,8 @@ absl_cc_test( thread_identity_test SRCS "internal/thread_identity_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::core_headers @@ -408,7 +476,57 @@ absl_cc_test( bits_test SRCS "internal/bits_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::bits gtest_main ) + +absl_cc_library( + NAME + scoped_set_env + SRCS + "internal/scoped_set_env.cc" + HDRS + "internal/scoped_set_env.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base +) + +absl_cc_test( + NAME + scoped_set_env_test + SRCS + "internal/scoped_set_env_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::scoped_set_env + gtest_main +) + +absl_cc_test( + NAME + cmake_thread_test + SRCS + "internal/cmake_thread_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base +) + +absl_cc_test( + NAME + log_severity_test + SRCS + "log_severity_test.cc" + DEPS + absl::base + absl::log_severity + gmock + gtest_main +) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 291ad89e..a7da62af 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -80,7 +80,7 @@ // // A function-like feature checking macro that accepts C++11 style attributes. // It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6 -// (http://en.cppreference.com/w/cpp/experimental/feature_test). If we don't +// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't // find `__has_cpp_attribute`, will evaluate to 0. #if defined(__cplusplus) && defined(__has_cpp_attribute) // NOTE: requiring __cplusplus above should not be necessary, but @@ -102,7 +102,7 @@ // // Tells the compiler to perform `printf` format string checking if the // compiler supports it; see the 'format' attribute in -// . +// . // // Note: As the GCC manual states, "[s]ince non-static C++ methods // have an implicit 'this' argument, the arguments of such methods @@ -232,7 +232,7 @@ // out of bounds or does other scary things with memory. // NOTE: GCC supports AddressSanitizer(asan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) && defined(ADDRESS_SANITIZER) +#if defined(__GNUC__) #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS @@ -246,7 +246,7 @@ // This attribute is similar to the ADDRESS_SANITIZER attribute above, but deals // with initialized-ness rather than addressability issues. // NOTE: MemorySanitizer(msan) is supported by Clang but not GCC. -#if defined(__GNUC__) && defined(MEMORY_SANITIZER) +#if defined(__clang__) #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY @@ -257,7 +257,7 @@ // Tells the ThreadSanitizer to not instrument a given function. // NOTE: GCC supports ThreadSanitizer(tsan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) && defined(THREAD_SANITIZER) +#if defined(__GNUC__) #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD @@ -287,6 +287,17 @@ #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI #endif +// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +// +// Tells the SafeStack to not instrument a given function. +// See https://clang.llvm.org/docs/SafeStack.html for details. +#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER) +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \ + __attribute__((no_sanitize("safe-stack"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +#endif + // ABSL_ATTRIBUTE_RETURNS_NONNULL // // Tells the compiler that a particular function never returns a null pointer. diff --git a/absl/base/bit_cast_test.cc b/absl/base/bit_cast_test.cc index 5af036df..be201856 100644 --- a/absl/base/bit_cast_test.cc +++ b/absl/base/bit_cast_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/base/macros.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { template @@ -105,5 +105,5 @@ TEST(BitCast, Double) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/call_once.h b/absl/base/call_once.h index aea9197b..373f015f 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -29,6 +29,7 @@ #include #include #include +#include #include "absl/base/internal/invoke.h" #include "absl/base/internal/low_level_scheduling.h" @@ -36,10 +37,11 @@ #include "absl/base/internal/scheduling_mode.h" #include "absl/base/internal/spinlock_wait.h" #include "absl/base/macros.h" +#include "absl/base/optimization.h" #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { class once_flag; @@ -208,7 +210,7 @@ void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { } } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 4d98a405..0e89bd24 100644 --- a/absl/base/call_once_test.cc +++ b/absl/base/call_once_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,15 +18,18 @@ #include #include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { absl::once_flag once; -Mutex counters_mu; + +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; @@ -100,5 +103,5 @@ TEST(CallOnceTest, ExecutionCount) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/casts.h b/absl/base/casts.h index bba623b4..b67b047c 100644 --- a/absl/base/casts.h +++ b/absl/base/casts.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,32 +27,25 @@ #include #include #include +#include #include "absl/base/internal/identity.h" #include "absl/base/macros.h" +#include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace internal_casts { -// NOTE: Not a fully compliant implementation of `std::is_trivially_copyable`. -// TODO(calabrese) Branch on implementations that directly provide -// `std::is_trivially_copyable`, create a more rigorous workaround, and publicly -// expose in meta/type_traits. -template -struct is_trivially_copyable - : std::integral_constant< - bool, std::is_destructible::value&& __has_trivial_destructor(T) && - __has_trivial_copy(T) && __has_trivial_assign(T)> {}; - template struct is_bitcastable - : std::integral_constant::value && - is_trivially_copyable::value && - std::is_default_constructible::value> {}; + : std::integral_constant< + bool, + sizeof(Dest) == sizeof(Source) && + type_traits_internal::is_trivially_copyable::value && + type_traits_internal::is_trivially_copyable::value && + std::is_default_constructible::value> {}; } // namespace internal_casts @@ -185,7 +178,7 @@ inline Dest bit_cast(const Source& source) { return dest; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_CASTS_H_ diff --git a/absl/base/config.h b/absl/base/config.h index db4c4539..1c3cb08e 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -118,7 +118,7 @@ // Checks whether `std::is_trivially_copy_assignable` is supported. // Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with -// either libc++ or libstdc++, and Visual Studio. +// either libc++ or libstdc++, and Visual Studio (but not NVCC). #if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) #error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set #elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) @@ -127,7 +127,7 @@ (!defined(__clang__) && defined(__GNUC__) && \ (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \ (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ - defined(_MSC_VER) + (defined(_MSC_VER) && !defined(__NVCC__)) #define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 #endif @@ -181,6 +181,13 @@ #endif #endif // defined(__ANDROID__) && defined(__clang__) +// Emscripten doesn't yet support `thread_local` or `__thread`. +// https://github.com/emscripten-core/emscripten/issues/3502 +#if defined(__EMSCRIPTEN__) +#undef ABSL_HAVE_TLS +#undef ABSL_HAVE_THREAD_LOCAL +#endif // defined(__EMSCRIPTEN__) + // ABSL_HAVE_INTRINSIC_INT128 // // Checks whether the __int128 compiler extension for a 128-bit integral type is @@ -191,15 +198,13 @@ // * On Clang: // * Building using Clang for Windows, where the Clang runtime library has // 128-bit support only on LP64 architectures, but Windows is LLP64. -// * Building for aarch64, where __int128 exists but has exhibits a sporadic -// compiler crashing bug. // * On Nvidia's nvcc: // * nvcc also defines __GNUC__ and __SIZEOF_INT128__, but not all versions // actually support __int128. #ifdef ABSL_HAVE_INTRINSIC_INT128 #error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set #elif defined(__SIZEOF_INT128__) -#if (defined(__clang__) && !defined(_WIN32) && !defined(__aarch64__)) || \ +#if (defined(__clang__) && !defined(_WIN32)) || \ (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \ (defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__)) #define ABSL_HAVE_INTRINSIC_INT128 1 @@ -255,7 +260,7 @@ // Linux and Linux-derived __linux__ // Android __ANDROID__ (implies __linux__) // Linux (non-Android) __linux__ && !__ANDROID__ -// Darwin (Mac OS X and iOS) __APPLE__ +// Darwin (macOS and iOS) __APPLE__ // Akaros (http://akaros.org) __ros__ // Windows _WIN32 // NaCL __native_client__ @@ -365,16 +370,25 @@ #error "absl endian detection needs to be set up for your compiler" #endif -// MacOS 10.13 doesn't let you use , , or even though -// the headers exist and are publicly noted to work. See +// macOS 10.13 and iOS 10.11 don't let you use , , or +// even though the headers exist and are publicly noted to work. See // https://github.com/abseil/abseil-cpp/issues/207 and // https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes +// libc++ spells out the availability requirements in the file +// llvm-project/libcxx/include/__config via the #define +// _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. #if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ - defined(__MAC_OS_X_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400 -#define ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES 1 + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 50000)) +#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1 #else -#define ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES 0 +#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0 #endif // ABSL_HAVE_STD_ANY @@ -386,7 +400,7 @@ #ifdef __has_include #if __has_include() && __cplusplus >= 201703L && \ - ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_ANY 1 #endif #endif @@ -400,7 +414,7 @@ #ifdef __has_include #if __has_include() && __cplusplus >= 201703L && \ - ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_OPTIONAL 1 #endif #endif @@ -414,7 +428,7 @@ #ifdef __has_include #if __has_include() && __cplusplus >= 201703L && \ - ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_VARIANT 1 #endif #endif diff --git a/absl/base/config_test.cc b/absl/base/config_test.cc index c839712a..7e0c033d 100644 --- a/absl/base/config_test.cc +++ b/absl/base/config_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/const_init.h b/absl/base/const_init.h new file mode 100644 index 00000000..30d6a3ca --- /dev/null +++ b/absl/base/const_init.h @@ -0,0 +1,74 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// kConstInit +// ----------------------------------------------------------------------------- +// +// A constructor tag used to mark an object as safe for use as a global +// variable, avoiding the usual lifetime issues that can affect globals. + +#ifndef ABSL_BASE_CONST_INIT_H_ +#define ABSL_BASE_CONST_INIT_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 +// undefined behavior, unless their constructors and destructors are designed +// with this issue in mind. +// +// The normal way to deal with this issue in C++11 is to use constant +// initialization and trivial destructors. +// +// Constant initialization is guaranteed to occur before any other code +// executes. Constructors that are declared 'constexpr' are eligible for +// constant initialization. You can annotate a variable declaration with the +// ABSL_CONST_INIT macro to express this intent. For compilers that support +// it, this annotation will cause a compilation error for declarations that +// aren't subject to constant initialization (perhaps because a runtime value +// was passed as a constructor argument). +// +// On program shutdown, lifetime issues can be avoided on global objects by +// ensuring that they contain trivial destructors. A class has a trivial +// destructor unless it has a user-defined destructor, a virtual method or base +// class, or a data member or base class with a non-trivial destructor of its +// own. Objects with static storage duration and a trivial destructor are not +// cleaned up on program shutdown, and are thus safe to access from other code +// running during shutdown. +// +// For a few core Abseil classes, we make a best effort to allow for safe global +// instances, even though these classes have non-trivial destructors. These +// objects can be created with the absl::kConstInit tag. For example: +// ABSL_CONST_INIT absl::Mutex global_mutex(absl::kConstInit); +// +// The line above declares a global variable of type absl::Mutex which can be +// accessed at any point during startup or shutdown. global_mutex's destructor +// will still run, but will not invalidate the object. Note that C++ specifies +// that accessing an object after its destructor has run results in undefined +// behavior, but this pattern works on the toolchains we support. +// +// The absl::kConstInit tag should only be used to define objects with static +// or thread_local storage duration. + +namespace absl { +inline namespace lts_2019_08_08 { + +enum ConstInitType { + kConstInit, +}; + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_BASE_CONST_INIT_H_ diff --git a/absl/base/dynamic_annotations.cc b/absl/base/dynamic_annotations.cc index 08c27e51..21e822e5 100644 --- a/absl/base/dynamic_annotations.cc +++ b/absl/base/dynamic_annotations.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/dynamic_annotations.h b/absl/base/dynamic_annotations.h index 7e328d96..65a54b44 100644 --- a/absl/base/dynamic_annotations.h +++ b/absl/base/dynamic_annotations.h @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -139,6 +139,7 @@ #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */ #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) /* empty */ #endif /* DYNAMIC_ANNOTATIONS_ENABLED || MEMORY_SANITIZER */ + /* TODO(delesley) -- Replace __CLANG_SUPPORT_DYN_ANNOTATION__ with the appropriate feature ID. */ #if defined(__clang__) && (!defined(SWIG)) \ @@ -376,7 +377,7 @@ inline T ANNOTATE_UNPROTECTED_READ(const volatile T &x) { /* NOLINT */ struct { char x[8] __attribute__ ((aligned (8))); } name #else #define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) -#define ADDRESS_SANITIZER_REDZONE(name) +#define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") #endif // ADDRESS_SANITIZER /* Undefine the macros intended only in this file. */ diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc index 7518264d..2ed38606 100644 --- a/absl/base/exception_safety_testing_test.cc +++ b/absl/base/exception_safety_testing_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/inline_variable_test.cc b/absl/base/inline_variable_test.cc index b968b10f..40e8b57b 100644 --- a/absl/base/inline_variable_test.cc +++ b/absl/base/inline_variable_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace inline_variable_testing_internal { namespace { @@ -60,5 +60,5 @@ TEST(InlineVariableTest, FunPtrType) { } // namespace } // namespace inline_variable_testing_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/inline_variable_test_a.cc b/absl/base/inline_variable_test_a.cc index a51b1d81..c19a6e55 100644 --- a/absl/base/inline_variable_test_a.cc +++ b/absl/base/inline_variable_test_a.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/inline_variable_test_b.cc b/absl/base/inline_variable_test_b.cc index 5041e20a..ae8da104 100644 --- a/absl/base/inline_variable_test_b.cc +++ b/absl/base/inline_variable_test_b.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h index 58ddf272..6757e75b 100644 --- a/absl/base/internal/atomic_hook.h +++ b/absl/base/internal/atomic_hook.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { template @@ -161,7 +161,7 @@ class AtomicHook { #undef ABSL_HAVE_WORKING_ATOMIC_POINTER } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 cf740757..ecc80406 100644 --- a/absl/base/internal/atomic_hook_test.cc +++ b/absl/base/internal/atomic_hook_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h index 29657426..ef978e9b 100644 --- a/absl/base/internal/bits.h +++ b/absl/base/internal/bits.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -46,7 +46,7 @@ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { @@ -189,7 +189,7 @@ ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { #undef ABSL_BASE_INTERNAL_FORCEINLINE } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_BITS_H_ diff --git a/absl/base/internal/bits_test.cc b/absl/base/internal/bits_test.cc index e5d991d6..7855fa62 100644 --- a/absl/base/internal/bits_test.cc +++ b/absl/base/internal/bits_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/cmake_thread_test.cc b/absl/base/internal/cmake_thread_test.cc new file mode 100644 index 00000000..f70bb24e --- /dev/null +++ b/absl/base/internal/cmake_thread_test.cc @@ -0,0 +1,22 @@ +// 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 +#include "absl/base/internal/thread_identity.h" + +int main() { + auto* tid = absl::base_internal::CurrentThreadIdentityIfPresent(); + // Make sure the above call can't be optimized out + std::cout << (void*)tid << std::endl; +} diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc index d99b63d3..9868e549 100644 --- a/absl/base/internal/cycleclock.cc +++ b/absl/base/internal/cycleclock.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,12 +22,13 @@ #include "absl/base/internal/cycleclock.h" +#include #include // NOLINT(build/c++11) #include "absl/base/internal/unscaledcycleclock.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { #if ABSL_USE_UNSCALED_CYCLECLOCK @@ -53,17 +54,40 @@ static constexpr int32_t kShift = 2; #endif static constexpr double kFrequencyScale = 1.0 / (1 << kShift); +static std::atomic cycle_clock_source; + +CycleClockSourceFunc LoadCycleClockSource() { + // Optimize for the common case (no callback) by first doing a relaxed load; + // this is significantly faster on non-x86 platforms. + if (cycle_clock_source.load(std::memory_order_relaxed) == nullptr) { + return nullptr; + } + // This corresponds to the store(std::memory_order_release) in + // CycleClockSource::Register, and makes sure that any updates made prior to + // registering the callback are visible to this thread before the callback is + // invoked. + return cycle_clock_source.load(std::memory_order_acquire); +} } // namespace int64_t CycleClock::Now() { - return base_internal::UnscaledCycleClock::Now() >> kShift; + auto fn = LoadCycleClockSource(); + if (fn == nullptr) { + return base_internal::UnscaledCycleClock::Now() >> kShift; + } + return fn() >> kShift; } double CycleClock::Frequency() { return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency(); } +void CycleClockSource::Register(CycleClockSourceFunc source) { + // Corresponds to the load(std::memory_order_acquire) in LoadCycleClockSource. + cycle_clock_source.store(source, std::memory_order_release); +} + #else int64_t CycleClock::Now() { @@ -79,5 +103,5 @@ double CycleClock::Frequency() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/cycleclock.h b/absl/base/internal/cycleclock.h index ae5ede3e..39bce06a 100644 --- a/absl/base/internal/cycleclock.h +++ b/absl/base/internal/cycleclock.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,6 @@ // not necessarily "CPU cycles" and code should not rely on that behavior, even // if experimentally observed. // -// // An arbitrary offset may have been added to the counter at power on. // // On some platforms, the rate and offset of the counter may differ @@ -46,7 +45,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // ----------------------------------------------------------------------------- @@ -72,8 +71,22 @@ class CycleClock { CycleClock& operator=(const CycleClock&) = delete; }; +using CycleClockSourceFunc = int64_t (*)(); + +class CycleClockSource { + private: + // CycleClockSource::Register() + // + // Register a function that provides an alternate source for the unscaled CPU + // cycle count value. The source function must be async signal safe, must not + // call CycleClock::Now(), and must have a frequency that matches that of the + // unscaled clock used by CycleClock. A nullptr value resets CycleClock to use + // the default source. + static void Register(CycleClockSourceFunc source); +}; + } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 f064e363..2e5e422c 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -62,7 +62,7 @@ extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); #endif // __BIONIC__ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // __linux__ diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index 52c09c5b..8643bffa 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #ifdef _MSC_VER #include // NOLINT(build/include) #elif defined(__APPLE__) -// Mac OS X / Darwin features +// macOS / Darwin features #include #elif defined(__FreeBSD__) #include @@ -34,7 +34,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // 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. @@ -76,7 +76,7 @@ inline uint64_t gbswap_64(uint64_t host_int) { if (__builtin_constant_p(host_int)) { return __bswap_constant_64(host_int); } else { - register uint64_t result; + uint64_t result; __asm__("bswap %0" : "=r"(result) : "0"(host_int)); return result; } @@ -268,7 +268,7 @@ inline void Store64(void *p, uint64_t v) { } // namespace big_endian -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 14ac4765..0c683286 100644 --- a/absl/base/internal/endian_test.cc +++ b/absl/base/internal/endian_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { const uint64_t kInitialNumber{0x0123456789abcdef}; @@ -261,5 +261,5 @@ TEST(EndianessTest, big_endian) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc index 8207b7d7..6ef4325c 100644 --- a/absl/base/internal/exception_safety_testing.cc +++ b/absl/base/internal/exception_safety_testing.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h index d4d41a8a..be38ba54 100644 --- a/absl/base/internal/exception_safety_testing.h +++ b/absl/base/internal/exception_safety_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -169,8 +169,10 @@ class ConstructorTracker { return current_tracker_instance_ != nullptr; } - static std::string ErrorMessage(void* address, const std::string& address_description, - int countdown, const std::string& error_description) { + static std::string ErrorMessage(void* address, + const std::string& address_description, + int countdown, + const std::string& error_description) { return absl::Substitute( "With coundtown at $0:\n" " $1\n" @@ -556,8 +558,8 @@ class ThrowingValue : private exceptions_internal::TrackedObject { // We provide both regular and templated operator delete because if only the // templated version is provided as we did with operator new, the compiler has // no way of knowing which overload of operator delete to call. See - // http://en.cppreference.com/w/cpp/memory/new/operator_delete and - // http://en.cppreference.com/w/cpp/language/delete for the gory details. + // https://en.cppreference.com/w/cpp/memory/new/operator_delete and + // https://en.cppreference.com/w/cpp/language/delete for the gory details. void operator delete(void* p) noexcept { ::operator delete(p); } template diff --git a/absl/base/internal/exception_testing.h b/absl/base/internal/exception_testing.h index 0cf7918e..01b54655 100644 --- a/absl/base/internal/exception_testing.h +++ b/absl/base/internal/exception_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/hide_ptr.h b/absl/base/internal/hide_ptr.h index ce390dc4..3720db18 100644 --- a/absl/base/internal/hide_ptr.h +++ b/absl/base/internal/hide_ptr.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // Arbitrary value with high bits set. Xor'ing with it is unlikely @@ -43,7 +43,7 @@ inline T* UnhidePtr(uintptr_t hidden) { } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_ diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h index d57c83f4..b9d0f621 100644 --- a/absl/base/internal/identity.h +++ b/absl/base/internal/identity.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #define ABSL_BASE_INTERNAL_IDENTITY_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace internal { template @@ -29,7 +29,7 @@ template using identity_t = typename identity::type; } // namespace internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_IDENTITY_H_ diff --git a/absl/base/internal/inline_variable.h b/absl/base/internal/inline_variable.h index f7bb8c56..130d8c24 100644 --- a/absl/base/internal/inline_variable.h +++ b/absl/base/internal/inline_variable.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/inline_variable_testing.h b/absl/base/internal/inline_variable_testing.h index be0b0b96..0ebdb9d8 100644 --- a/absl/base/internal/inline_variable_testing.h +++ b/absl/base/internal/inline_variable_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include "absl/base/internal/inline_variable.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_ diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index 1372ef50..030b98df 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -39,11 +39,13 @@ #include #include +#include "absl/meta/type_traits.h" + // The following code is internal implementation detail. See the comment at the // top of this file for the API documentation. namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // The five classes below each implement one of the clauses from the definition @@ -68,15 +70,11 @@ struct MemFunAndRef : StrippedAccept { template struct AcceptImpl : std::false_type {}; - template - struct AcceptImpl - : std::is_base_of {}; - - template - struct AcceptImpl - : std::is_base_of {}; + template + struct AcceptImpl + : std::integral_constant::value && + absl::is_function::value> { + }; template static decltype((std::declval().* @@ -93,15 +91,11 @@ struct MemFunAndPtr : StrippedAccept { template struct AcceptImpl : std::false_type {}; - template - struct AcceptImpl - : std::integral_constant::value> {}; - - template - struct AcceptImpl - : std::integral_constant::value> {}; + template + struct AcceptImpl + : std::integral_constant::value && + absl::is_function::value> { + }; template static decltype(((*std::declval()).* @@ -120,7 +114,9 @@ struct DataMemAndRef : StrippedAccept { struct AcceptImpl : std::false_type {}; template - struct AcceptImpl : std::is_base_of {}; + struct AcceptImpl + : std::integral_constant::value && + !absl::is_function::value> {}; template static decltype(std::declval().*std::declval()) Invoke( @@ -137,7 +133,8 @@ struct DataMemAndPtr : StrippedAccept { template struct AcceptImpl - : std::integral_constant::value> {}; + : std::integral_constant::value && + !absl::is_function::value> {}; template static decltype((*std::declval()).*std::declval()) Invoke( @@ -184,7 +181,7 @@ InvokeT Invoke(F&& f, Args&&... args) { std::forward(args)...); } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 10d805cc..419c0e45 100644 --- a/absl/base/internal/low_level_alloc.cc +++ b/absl/base/internal/low_level_alloc.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -63,7 +63,7 @@ #endif // __APPLE__ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // A first-fit allocator with amortized logarithmic free() time. @@ -295,7 +295,10 @@ class SCOPED_LOCKABLE ArenaLock { arena_->mu.Unlock(); #ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if (mask_valid_) { - pthread_sigmask(SIG_SETMASK, &mask_, nullptr); + const int err = pthread_sigmask(SIG_SETMASK, &mask_, nullptr); + if (err != 0) { + ABSL_RAW_LOG(FATAL, "pthread_sigmask failed: %d", err); + } } #endif left_ = true; @@ -612,7 +615,7 @@ void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) { } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 87cfc934..32b6ec17 100644 --- a/absl/base/internal/low_level_alloc.h +++ b/absl/base/internal/low_level_alloc.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,6 +25,7 @@ // IWYU pragma: private, include "base/low_level_alloc.h" #include + #include #include "absl/base/attributes.h" @@ -54,7 +55,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { class LowLevelAlloc { @@ -119,6 +120,7 @@ class LowLevelAlloc { }; } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 65bb519d..b6eb8b30 100644 --- a/absl/base/internal/low_level_alloc_test.cc +++ b/absl/base/internal/low_level_alloc_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { namespace { @@ -138,6 +138,7 @@ static void Test(bool use_new_arena, bool call_malloc_hook, int n) { TEST_ASSERT(LowLevelAlloc::DeleteArena(arena)); } } + // LowLevelAlloc is designed to be safe to call before main(). static struct BeforeMain { BeforeMain() { @@ -149,7 +150,7 @@ static struct BeforeMain { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 7cb6117e..b762279d 100644 --- a/absl/base/internal/low_level_scheduling.h +++ b/absl/base/internal/low_level_scheduling.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -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_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { class SchedulingHelper; // To allow use of SchedulingGuard. @@ -87,6 +87,7 @@ class SchedulingGuard { //------------------------------------------------------------------------------ // End of public interfaces. //------------------------------------------------------------------------------ + inline bool SchedulingGuard::ReschedulingIsAllowed() { return false; } @@ -99,8 +100,8 @@ inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { return; } - } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ diff --git a/absl/base/internal/per_thread_tls.h b/absl/base/internal/per_thread_tls.h index 2428bdc1..cf5e97a0 100644 --- a/absl/base/internal/per_thread_tls.h +++ b/absl/base/internal/per_thread_tls.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,13 +16,17 @@ #define ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ // This header defines two macros: +// // If the platform supports thread-local storage: -// ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a -// thread-local variable ABSL_PER_THREAD_TLS is 1 +// +// * ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a +// thread-local variable +// * ABSL_PER_THREAD_TLS is 1 // // Otherwise: -// ABSL_PER_THREAD_TLS_KEYWORD is empty -// ABSL_PER_THREAD_TLS is 0 +// +// * ABSL_PER_THREAD_TLS_KEYWORD is empty +// * ABSL_PER_THREAD_TLS is 0 // // Microsoft C supports thread-local storage. // GCC supports it if the appropriate version of glibc is available, diff --git a/absl/base/internal/pretty_function.h b/absl/base/internal/pretty_function.h index 01b0547b..35d51676 100644 --- a/absl/base/internal/pretty_function.h +++ b/absl/base/internal/pretty_function.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index ed8b8d7c..1a36d5b6 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -36,7 +36,8 @@ // This preprocessor token is also defined in raw_io.cc. If you need to copy // this, consider moving both to config.h instead. #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__Fuchsia__) || defined(__native_client__) + defined(__Fuchsia__) || defined(__native_client__) || \ + defined(__EMSCRIPTEN__) #include @@ -181,7 +182,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } // namespace namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace raw_logging_internal { void SafeWriteToStderr(const char *s, size_t len) { #if defined(ABSL_HAVE_SYSCALL_WRITE) @@ -232,5 +233,5 @@ void RegisterInternalLogFunction(InternalLogFunction func) { } } // namespace raw_logging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index 2786a3de..8e5a34ef 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,6 +38,7 @@ // ABSL_RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); // This will print an almost standard log line like this to stderr only: // E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file + #define ABSL_RAW_LOG(severity, ...) \ do { \ constexpr const char* absl_raw_logging_internal_basename = \ @@ -79,13 +80,13 @@ absl_raw_logging_internal_basename, __LINE__, message); \ } while (0) -#define ABSL_INTERNAL_CHECK(condition, message) \ - do { \ - if (ABSL_PREDICT_FALSE(!(condition))) { \ +#define ABSL_INTERNAL_CHECK(condition, message) \ + do { \ + if (ABSL_PREDICT_FALSE(!(condition))) { \ std::string death_message = "Check " #condition " failed: "; \ death_message += std::string(message); \ - ABSL_INTERNAL_LOG(FATAL, death_message); \ - } \ + ABSL_INTERNAL_LOG(FATAL, death_message); \ + } \ } while (0) #define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo @@ -96,7 +97,7 @@ ::absl::NormalizeLogSeverity(severity) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace raw_logging_internal { // Helper function to implement ABSL_RAW_LOG @@ -176,7 +177,7 @@ extern base_internal::AtomicHook internal_log_function; void RegisterInternalLogFunction(InternalLogFunction func); } // namespace raw_logging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 19a7514c..48767920 100644 --- a/absl/base/internal/scheduling_mode.h +++ b/absl/base/internal/scheduling_mode.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,7 +19,7 @@ #define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // Used to describe how a thread may be scheduled. Typically associated with @@ -50,7 +50,7 @@ enum SchedulingMode { }; } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 new file mode 100644 index 00000000..eb7a0116 --- /dev/null +++ b/absl/base/internal/scoped_set_env.cc @@ -0,0 +1,81 @@ +// 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/scoped_set_env.h" + +#ifdef _WIN32 +#include +#endif + +#include + +#include "absl/base/internal/raw_logging.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace base_internal { + +namespace { + +#ifdef _WIN32 +const int kMaxEnvVarValueSize = 1024; +#endif + +void SetEnvVar(const char* name, const char* value) { +#ifdef _WIN32 + SetEnvironmentVariableA(name, value); +#else + if (value == nullptr) { + ::unsetenv(name); + } else { + ::setenv(name, value, 1); + } +#endif +} + +} // namespace + +ScopedSetEnv::ScopedSetEnv(const char* var_name, const char* new_value) + : var_name_(var_name), was_unset_(false) { +#ifdef _WIN32 + char buf[kMaxEnvVarValueSize]; + auto get_res = GetEnvironmentVariableA(var_name_.c_str(), buf, sizeof(buf)); + ABSL_INTERNAL_CHECK(get_res < sizeof(buf), "value exceeds buffer size"); + + if (get_res == 0) { + was_unset_ = (GetLastError() == ERROR_ENVVAR_NOT_FOUND); + } else { + old_value_.assign(buf, get_res); + } + + SetEnvironmentVariableA(var_name_.c_str(), new_value); +#else + const char* val = ::getenv(var_name_.c_str()); + if (val == nullptr) { + was_unset_ = true; + } else { + old_value_ = val; + } +#endif + + SetEnvVar(var_name_.c_str(), new_value); +} + +ScopedSetEnv::~ScopedSetEnv() { + SetEnvVar(var_name_.c_str(), was_unset_ ? nullptr : old_value_.c_str()); +} + +} // namespace base_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/base/internal/scoped_set_env.h b/absl/base/internal/scoped_set_env.h new file mode 100644 index 00000000..709843bc --- /dev/null +++ b/absl/base/internal/scoped_set_env.h @@ -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. +// + +#ifndef ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ +#define ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ + +#include + +namespace absl { +inline namespace lts_2019_08_08 { +namespace base_internal { + +class ScopedSetEnv { + public: + ScopedSetEnv(const char* var_name, const char* new_value); + ~ScopedSetEnv(); + + private: + std::string var_name_; + std::string old_value_; + + // True if the environment variable was initially not set. + bool was_unset_; +}; + +} // namespace base_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ diff --git a/absl/base/internal/scoped_set_env_test.cc b/absl/base/internal/scoped_set_env_test.cc new file mode 100644 index 00000000..5cbad246 --- /dev/null +++ b/absl/base/internal/scoped_set_env_test.cc @@ -0,0 +1,99 @@ +// 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. + +#ifdef _WIN32 +#include +#endif + +#include "gtest/gtest.h" +#include "absl/base/internal/scoped_set_env.h" + +namespace { + +using absl::base_internal::ScopedSetEnv; + +std::string GetEnvVar(const char* name) { +#ifdef _WIN32 + char buf[1024]; + auto get_res = GetEnvironmentVariableA(name, buf, sizeof(buf)); + if (get_res >= sizeof(buf)) { + return "TOO_BIG"; + } + + if (get_res == 0) { + return "UNSET"; + } + + return std::string(buf, get_res); +#else + const char* val = ::getenv(name); + if (val == nullptr) { + return "UNSET"; + } + + return val; +#endif +} + +TEST(ScopedSetEnvTest, SetNonExistingVarToString) { + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); + + { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value"); + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); + } + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); +} + +TEST(ScopedSetEnvTest, SetNonExistingVarToNull) { + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); + + { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", nullptr); + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); + } + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); +} + +TEST(ScopedSetEnvTest, SetExistingVarToString) { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value"); + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); + + { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "new_value"); + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "new_value"); + } + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); +} + +TEST(ScopedSetEnvTest, SetExistingVarToNull) { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value"); + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); + + { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", nullptr); + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); + } + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); +} + +} // namespace diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc index 8f8eef82..5f443bfa 100644 --- a/absl/base/internal/spinlock.cc +++ b/absl/base/internal/spinlock.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -54,7 +54,7 @@ // holder to acquire the lock. There may be outstanding waiter(s). namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { ABSL_CONST_INIT static base_internal::AtomicHook #include + #include #include "absl/base/attributes.h" @@ -45,7 +46,7 @@ #include "absl/base/thread_annotations.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { class LOCKABLE SpinLock { @@ -58,8 +59,10 @@ class LOCKABLE SpinLock { // // static SpinLock lock(base_internal::kLinkerInitialized); // - // When intialized using this constructor, we depend on the fact - // that the linker has already initialized the memory appropriately. + // When initialized using this constructor, we depend on the fact + // that the linker has already initialized the memory appropriately. The lock + // is initialized in non-cooperative mode. + // // A SpinLock constructed like this can be freely used from global // initializers without worrying about the order in which global // initializers run. @@ -235,7 +238,7 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_SPINLOCK_H_ diff --git a/absl/base/internal/spinlock_akaros.inc b/absl/base/internal/spinlock_akaros.inc index 051c8cf8..bc468940 100644 --- a/absl/base/internal/spinlock_akaros.inc +++ b/absl/base/internal/spinlock_akaros.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/spinlock_benchmark.cc b/absl/base/internal/spinlock_benchmark.cc index 907d3e27..0451c65f 100644 --- a/absl/base/internal/spinlock_benchmark.cc +++ b/absl/base/internal/spinlock_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/spinlock_linux.inc b/absl/base/internal/spinlock_linux.inc index 94c861dc..28e29d19 100644 --- a/absl/base/internal/spinlock_linux.inc +++ b/absl/base/internal/spinlock_linux.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -51,17 +51,12 @@ extern "C" { ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( std::atomic *w, uint32_t value, int loop, absl::base_internal::SchedulingMode) { - if (loop != 0) { - int save_errno = errno; - struct timespec tm; - tm.tv_sec = 0; - // Increase the delay; we expect (but do not rely on) explicit wakeups. - // We don't rely on explicit wakeups because we intentionally allow for - // a race on the kSpinLockSleeper bit. - tm.tv_nsec = 16 * absl::base_internal::SpinLockSuggestedDelayNS(loop); - syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); - errno = save_errno; - } + int save_errno = errno; + 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 *w, diff --git a/absl/base/internal/spinlock_posix.inc b/absl/base/internal/spinlock_posix.inc index 0098c1c7..f025b5f8 100644 --- a/absl/base/internal/spinlock_posix.inc +++ b/absl/base/internal/spinlock_posix.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/spinlock_wait.cc b/absl/base/internal/spinlock_wait.cc index 3f8e43f0..60a85196 100644 --- a/absl/base/internal/spinlock_wait.cc +++ b/absl/base/internal/spinlock_wait.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -32,7 +32,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // See spinlock_wait.h for spec. @@ -66,19 +66,16 @@ int SpinLockSuggestedDelayNS(int loop) { r = 0x5deece66dLL * r + 0xb; // numbers from nrand48() delay_rand.store(r, std::memory_order_relaxed); - r <<= 16; // 48-bit random number now in top 48-bits. if (loop < 0 || loop > 32) { // limit loop to 0..32 loop = 32; } - // loop>>3 cannot exceed 4 because loop cannot exceed 32. - // Select top 20..24 bits of lower 48 bits, - // giving approximately 0ms to 16ms. - // Mean is exponential in loop for first 32 iterations, then 8ms. - // The futex path multiplies this by 16, since we expect explicit wakeups - // almost always on that path. - return static_cast(r >> (44 - (loop >> 3))); + const int kMinDelay = 128 << 10; // 128us + // Double delay every 8 iterations, up to 16x (2ms). + int delay = kMinDelay << (loop / 8); + // Randomize in delay..2*delay range, for resulting 128us..4ms range. + return delay | ((delay - 1) & static_cast(r)); } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h index 5eb727f1..7e139de0 100644 --- a/absl/base/internal/spinlock_wait.h +++ b/absl/base/internal/spinlock_wait.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include "absl/base/internal/scheduling_mode.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // SpinLockWait() waits until it can perform one of several transitions from @@ -63,7 +63,7 @@ void SpinLockDelay(std::atomic *w, uint32_t value, int loop, int SpinLockSuggestedDelayNS(int loop); } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // In some build configurations we pass --detect-odr-violations to the diff --git a/absl/base/internal/spinlock_win32.inc b/absl/base/internal/spinlock_win32.inc index 32c8fc0b..78654b5b 100644 --- a/absl/base/internal/spinlock_win32.inc +++ b/absl/base/internal/spinlock_win32.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index ce14fc0f..0d5cf821 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -56,7 +56,7 @@ #include "absl/base/internal/unscaledcycleclock.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { static once_flag init_system_info_once; @@ -402,5 +402,5 @@ pid_t GetTID() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/sysinfo.h b/absl/base/internal/sysinfo.h index 79100f61..22739aeb 100644 --- a/absl/base/internal/sysinfo.h +++ b/absl/base/internal/sysinfo.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,7 +33,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // Nominal core processor cycles per second of each processor. This is _not_ @@ -59,7 +59,7 @@ using pid_t = DWORD; pid_t GetTID(); } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 c072ebc2..c8323e52 100644 --- a/absl/base/internal/sysinfo_test.cc +++ b/absl/base/internal/sysinfo_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { namespace { @@ -38,12 +38,12 @@ TEST(SysinfoTest, NumCPUs) { } TEST(SysinfoTest, NominalCPUFrequency) { -#if !(defined(__aarch64__) && defined(__linux__)) +#if !(defined(__aarch64__) && defined(__linux__)) && !defined(__EMSCRIPTEN__) EXPECT_GE(NominalCPUFrequency(), 1000.0) << "NominalCPUFrequency() did not return a reasonable value"; #else - // TODO(absl-team): Aarch64 cannot read the CPU frequency from sysfs, so we - // get back 1.0. Fix once the value is available. + // Aarch64 cannot read the CPU frequency from sysfs, so we get back 1.0. + // Emscripten does not have a sysfs to read from at all. EXPECT_EQ(NominalCPUFrequency(), 1.0) << "CPU frequency detection was fixed! Please update unittest."; #endif @@ -96,5 +96,5 @@ TEST(SysinfoTest, LinuxGetTID) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/thread_annotations.h b/absl/base/internal/thread_annotations.h new file mode 100644 index 00000000..4dab6a9c --- /dev/null +++ b/absl/base/internal/thread_annotations.h @@ -0,0 +1,271 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: thread_annotations.h +// ----------------------------------------------------------------------------- +// +// WARNING: This is a backwards compatible header and it will be removed after +// the migration to prefixed thread annotations is finished; please include +// "absl/base/thread_annotations.h". +// +// This header file contains macro definitions for thread safety annotations +// that allow developers to document the locking policies of multi-threaded +// code. The annotations can also help program analysis tools to identify +// potential thread safety issues. +// +// These annotations are implemented using compiler attributes. Using the macros +// defined here instead of raw attributes allow for portability and future +// compatibility. +// +// When referring to mutexes in the arguments of the attributes, you should +// use variable names or more complex expressions (e.g. my_object->mutex_) +// that evaluate to a concrete mutex object whenever possible. If the mutex +// you want to refer to is not in scope, you may use a member pointer +// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object. + +#ifndef ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ +#define ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ + +#if defined(__clang__) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +// GUARDED_BY() +// +// Documents if a shared field or global variable needs to be protected by a +// mutex. GUARDED_BY() allows the user to specify a particular mutex that +// should be held when accessing the annotated variable. +// +// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to +// local variables, a local variable and its associated mutex can often be +// combined into a small class or struct, thereby allowing the annotation. +// +// Example: +// +// class Foo { +// Mutex mu_; +// int p1_ GUARDED_BY(mu_); +// ... +// }; +#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +// PT_GUARDED_BY() +// +// Documents if the memory location pointed to by a pointer should be guarded +// by a mutex when dereferencing the pointer. +// +// Example: +// class Foo { +// Mutex mu_; +// int *p1_ PT_GUARDED_BY(mu_); +// ... +// }; +// +// Note that a pointer variable to a shared memory location could itself be a +// shared variable. +// +// Example: +// +// // `q_`, guarded by `mu1_`, points to a shared memory location that is +// // guarded by `mu2_`: +// int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_); +#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +// ACQUIRED_AFTER() / ACQUIRED_BEFORE() +// +// Documents the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER +// and ACQUIRED_BEFORE.) +// +// As with GUARDED_BY, this is only applicable to mutexes that are shared +// fields or global variables. +// +// Example: +// +// Mutex m1_; +// Mutex m2_ ACQUIRED_AFTER(m1_); +#define ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED() +// +// Documents a function that expects a mutex to be held prior to entry. +// The mutex is expected to be held both on entry to, and exit from, the +// function. +// +// An exclusive lock allows read-write access to the guarded data member(s), and +// only one thread can acquire a lock exclusively at any one time. A shared lock +// allows read-only access, and any number of threads can acquire a shared lock +// concurrently. +// +// Generally, non-const methods should be annotated with +// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with +// SHARED_LOCKS_REQUIRED. +// +// Example: +// +// Mutex mu1, mu2; +// int a GUARDED_BY(mu1); +// int b GUARDED_BY(mu2); +// +// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } +// void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) + +#define SHARED_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) + +// LOCKS_EXCLUDED() +// +// Documents the locks acquired in the body of the function. These locks +// cannot be held when calling this function (as Abseil's `Mutex` locks are +// non-reentrant). +#define LOCKS_EXCLUDED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +// LOCK_RETURNED() +// +// Documents a function that returns a mutex without acquiring it. For example, +// a public getter method that returns a pointer to a private mutex should +// be annotated with LOCK_RETURNED. +#define LOCK_RETURNED(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +// LOCKABLE +// +// Documents if a class/type is a lockable type (such as the `Mutex` class). +#define LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(lockable) + +// SCOPED_LOCKABLE +// +// Documents if a class does RAII locking (such as the `MutexLock` class). +// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is +// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no +// arguments; the analysis will assume that the destructor unlocks whatever the +// constructor locked. +#define SCOPED_LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +// EXCLUSIVE_LOCK_FUNCTION() +// +// Documents functions that acquire a lock in the body of a function, and do +// not release it. +#define EXCLUSIVE_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) + +// SHARED_LOCK_FUNCTION() +// +// Documents functions that acquire a shared (reader) lock in the body of a +// function, and do not release it. +#define SHARED_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) + +// UNLOCK_FUNCTION() +// +// Documents functions that expect a lock to be held on entry to the function, +// and release it in the body of the function. +#define UNLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) + +// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION() +// +// Documents functions that try to acquire a lock, and return success or failure +// (or a non-boolean value that can be interpreted as a boolean). +// The first argument should be `true` for functions that return `true` on +// success, or `false` for functions that return `false` on success. The second +// argument specifies the mutex that is locked on success. If unspecified, this +// mutex is assumed to be `this`. +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) + +#define SHARED_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) + +// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK() +// +// Documents functions that dynamically check to see if a lock is held, and fail +// if it is not held. +#define ASSERT_EXCLUSIVE_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) + +#define ASSERT_SHARED_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) + +// NO_THREAD_SAFETY_ANALYSIS +// +// Turns off thread safety checking within the body of a particular function. +// This annotation is used to mark functions that are known to be correct, but +// the locking behavior is more complicated than the analyzer can handle. +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +//------------------------------------------------------------------------------ +// Tool-Supplied Annotations +//------------------------------------------------------------------------------ + +// TS_UNCHECKED should be placed around lock expressions that are not valid +// C++ syntax, but which are present for documentation purposes. These +// annotations will be ignored by the analysis. +#define TS_UNCHECKED(x) "" + +// TS_FIXME is used to mark lock expressions that are not valid C++ syntax. +// It is used by automated tools to mark and disable invalid expressions. +// The annotation should either be fixed, or changed to TS_UNCHECKED. +#define TS_FIXME(x) "" + +// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of +// a particular function. However, this attribute is used to mark functions +// that are incorrect and need to be fixed. It is used by automated tools to +// avoid breaking the build when the analysis is updated. +// Code owners are expected to eventually fix the routine. +#define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS + +// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY +// annotation that needs to be fixed, because it is producing thread safety +// warning. It disables the GUARDED_BY. +#define GUARDED_BY_FIXME(x) + +// Disables warnings for a single read operation. This can be used to avoid +// warnings when it is known that the read is not actually involved in a race, +// but the compiler cannot confirm that. +#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x) + + +namespace thread_safety_analysis { + +// Takes a reference to a guarded data member, and returns an unguarded +// reference. +template +inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +template +inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +} // namespace thread_safety_analysis + +#endif // ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc index b35bee34..dbec326a 100644 --- a/absl/base/internal/thread_identity.cc +++ b/absl/base/internal/thread_identity.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/base/internal/spinlock.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { #if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11 @@ -131,5 +131,5 @@ ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index 17ac2a7c..bb5aa074 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,7 +33,7 @@ #include "absl/base/internal/per_thread_tls.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { struct SynchLocksHeld; struct SynchWaitParams; @@ -43,9 +43,9 @@ namespace base_internal { class SpinLock; struct ThreadIdentity; -// Used by the implementation of base::Mutex and base::CondVar. +// Used by the implementation of absl::Mutex and absl::CondVar. struct PerThreadSynch { - // The internal representation of base::Mutex and base::CondVar rely + // The internal representation of absl::Mutex and absl::CondVar rely // on the alignment of PerThreadSynch. Both store the address of the // PerThreadSynch in the high-order bits of their internal state, // which means the low kLowZeroBits of the address of PerThreadSynch @@ -237,6 +237,7 @@ inline ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ diff --git a/absl/base/internal/thread_identity_benchmark.cc b/absl/base/internal/thread_identity_benchmark.cc index 242522b4..0ae10f2b 100644 --- a/absl/base/internal/thread_identity_benchmark.cc +++ b/absl/base/internal/thread_identity_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc index ec93fc27..8de6d9eb 100644 --- a/absl/base/internal/thread_identity_test.cc +++ b/absl/base/internal/thread_identity_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { namespace { @@ -124,5 +124,5 @@ TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/throw_delegate.cc b/absl/base/internal/throw_delegate.cc index 0f73c3eb..bee404d4 100644 --- a/absl/base/internal/throw_delegate.cc +++ b/absl/base/internal/throw_delegate.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/throw_delegate.h b/absl/base/internal/throw_delegate.h index 7e5510c0..60ed4f32 100644 --- a/absl/base/internal/throw_delegate.h +++ b/absl/base/internal/throw_delegate.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // Helper functions that allow throwing exceptions consistently from anywhere. @@ -67,7 +67,7 @@ namespace base_internal { // [[noreturn]] void ThrowStdBadArrayNewLength(); } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ diff --git a/absl/base/internal/tsan_mutex_interface.h b/absl/base/internal/tsan_mutex_interface.h index 6bb4faed..2a510603 100644 --- a/absl/base/internal/tsan_mutex_interface.h +++ b/absl/base/internal/tsan_mutex_interface.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h index 07a64bba..a709a446 100644 --- a/absl/base/internal/unaligned_access.h +++ b/absl/base/internal/unaligned_access.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,15 +25,6 @@ // unaligned APIs // Portable handling of unaligned loads, stores, and copies. -// On some platforms, like ARM, the copy functions can be more efficient -// then a load and a store. -// -// It is possible to implement all of these these using constant-length memcpy -// calls, which is portable and will usually be inlined into simple loads and -// stores if the architecture supports it. However, such inlining usually -// happens in a pass that's quite late in compilation, which means the resulting -// loads and stores cannot participate in many other optimizations, leading to -// overall worse code. // The unaligned API is C++ only. The declarations use C++ features // (namespaces, inline) which are absent or incompatible in C. @@ -65,7 +56,7 @@ void __sanitizer_unaligned_store64(void *p, uint64_t v); } // extern "C" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { inline uint16_t UnalignedLoad16(const void *p) { @@ -93,7 +84,7 @@ inline void UnalignedStore64(void *p, uint64_t v) { } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ @@ -110,172 +101,10 @@ inline void UnalignedStore64(void *p, uint64_t v) { #define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ (absl::base_internal::UnalignedStore64(_p, _val)) -#elif defined(UNDEFINED_BEHAVIOR_SANITIZER) - -namespace absl { -inline namespace lts_2018_12_18 { -namespace base_internal { - -inline uint16_t UnalignedLoad16(const void *p) { - uint16_t t; - memcpy(&t, p, sizeof t); - return t; -} - -inline uint32_t UnalignedLoad32(const void *p) { - uint32_t t; - memcpy(&t, p, sizeof t); - return t; -} - -inline uint64_t UnalignedLoad64(const void *p) { - uint64_t t; - memcpy(&t, p, sizeof t); - return t; -} - -inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); } - -inline void 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_2018_12_18 -} // namespace absl - -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ - (absl::base_internal::UnalignedLoad16(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ - (absl::base_internal::UnalignedLoad32(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ - (absl::base_internal::UnalignedLoad64(_p)) - -#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ - (absl::base_internal::UnalignedStore16(_p, _val)) -#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - (absl::base_internal::UnalignedStore32(_p, _val)) -#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (absl::base_internal::UnalignedStore64(_p, _val)) - -#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \ - defined(_M_IX86) || defined(__ppc__) || defined(__PPC__) || \ - defined(__ppc64__) || defined(__PPC64__) - -// x86 and x86-64 can perform unaligned loads/stores directly; -// modern PowerPC hardware can also do unaligned integer loads and stores; -// but note: the FPU still sends unaligned loads and stores to a trap handler! - -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ - (*reinterpret_cast(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ - (*reinterpret_cast(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ - (*reinterpret_cast(_p)) - -#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ - (*reinterpret_cast(_p) = (_val)) -#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - (*reinterpret_cast(_p) = (_val)) -#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (*reinterpret_cast(_p) = (_val)) - -#elif defined(__arm__) && \ - !defined(__ARM_ARCH_5__) && \ - !defined(__ARM_ARCH_5T__) && \ - !defined(__ARM_ARCH_5TE__) && \ - !defined(__ARM_ARCH_5TEJ__) && \ - !defined(__ARM_ARCH_6__) && \ - !defined(__ARM_ARCH_6J__) && \ - !defined(__ARM_ARCH_6K__) && \ - !defined(__ARM_ARCH_6Z__) && \ - !defined(__ARM_ARCH_6ZK__) && \ - !defined(__ARM_ARCH_6T2__) - - -// ARMv7 and newer support native unaligned accesses, but only of 16-bit -// and 32-bit values (not 64-bit); older versions either raise a fatal signal, -// do an unaligned read and rotate the words around a bit, or do the reads very -// slowly (trip through kernel mode). There's no simple #define that says just -// "ARMv7 or higher", so we have to filter away all ARMv5 and ARMv6 -// sub-architectures. Newer gcc (>= 4.6) set an __ARM_FEATURE_ALIGNED #define, -// so in time, maybe we can move on to that. -// -// This is a mess, but there's not much we can do about it. -// -// To further complicate matters, only LDR instructions (single reads) are -// allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we -// explicitly tell the compiler that these accesses can be unaligned, it can and -// will combine accesses. On armcc, the way to signal this is done by accessing -// through the type (uint32_t __packed *), but GCC has no such attribute -// (it ignores __attribute__((packed)) on individual variables). However, -// we can tell it that a _struct_ is unaligned, which has the same effect, -// so we do that. - -namespace absl { -inline namespace lts_2018_12_18 { -namespace base_internal { - -struct Unaligned16Struct { - uint16_t value; - uint8_t dummy; // To make the size non-power-of-two. -} ABSL_ATTRIBUTE_PACKED; - -struct Unaligned32Struct { - uint32_t value; - uint8_t dummy; // To make the size non-power-of-two. -} ABSL_ATTRIBUTE_PACKED; - -} // namespace base_internal -} // inline namespace lts_2018_12_18 -} // namespace absl - -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ - ((reinterpret_cast(_p)) \ - ->value) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ - ((reinterpret_cast(_p)) \ - ->value) - -#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ - ((reinterpret_cast< ::absl::base_internal::Unaligned16Struct *>(_p)) \ - ->value = (_val)) -#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - ((reinterpret_cast< ::absl::base_internal::Unaligned32Struct *>(_p)) \ - ->value = (_val)) - -namespace absl { -inline namespace lts_2018_12_18 { -namespace base_internal { - -inline uint64_t UnalignedLoad64(const void *p) { - uint64_t t; - memcpy(&t, p, sizeof t); - return t; -} - -inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } - -} // namespace base_internal -} // inline namespace lts_2018_12_18 -} // namespace absl - -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ - (absl::base_internal::UnalignedLoad64(_p)) -#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (absl::base_internal::UnalignedStore64(_p, _val)) - #else -// ABSL_INTERNAL_NEED_ALIGNED_LOADS is defined when the underlying platform -// doesn't support unaligned access. -#define ABSL_INTERNAL_NEED_ALIGNED_LOADS - -// These functions are provided for architectures that don't support -// unaligned loads and stores. - namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { inline uint16_t UnalignedLoad16(const void *p) { @@ -303,7 +132,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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index 888caf1e..29a927d3 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,7 @@ #include "absl/base/internal/sysinfo.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { #if defined(__i386__) @@ -97,7 +97,7 @@ double UnscaledCycleClock::Frequency() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_USE_UNSCALED_CYCLECLOCK diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index c71674f3..e6fc9103 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -59,7 +59,8 @@ // CycleClock that runs at atleast 1 MHz. We've found some Android // ARM64 devices where this is not the case, so we disable it by // default on Android ARM64. -#if defined(__native_client__) || TARGET_OS_IPHONE || \ +#if defined(__native_client__) || \ + (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \ (defined(__ANDROID__) && defined(__aarch64__)) #define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 #else @@ -83,8 +84,9 @@ defined(_M_IX86) || defined(_M_X64)) #define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY #endif + namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { class UnscaledCycleClockWrapperForGetCurrentTime; } // namespace time_internal @@ -114,8 +116,9 @@ class UnscaledCycleClock { }; } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_USE_UNSCALED_CYCLECLOCK #endif // ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc index 4df637ac..685501a4 100644 --- a/absl/base/invoke_test.cc +++ b/absl/base/invoke_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { namespace { @@ -71,6 +71,10 @@ struct OverloadedFunctor { struct Class { int Method(int a, int b) { return a - b; } int ConstMethod(int a, int b) const { return a - b; } + int RefMethod(int a, int b) & { return a - b; } + int RefRefMethod(int a, int b) && { return a - b; } + int NoExceptMethod(int a, int b) noexcept { return a - b; } + int VolatileMethod(int a, int b) volatile { return a - b; } int member; }; @@ -152,8 +156,18 @@ TEST(InvokeTest, ReferenceWrapper) { TEST(InvokeTest, MemberFunction) { std::unique_ptr p(new Class); std::unique_ptr cp(new Class); + std::unique_ptr vp(new Class); + EXPECT_EQ(1, Invoke(&Class::Method, p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::Method, p.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::Method, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefMethod, p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefMethod, p.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefMethod, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefRefMethod, std::move(*p), 3, 2)); // NOLINT + EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, *p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, p.get(), 3, 2)); @@ -163,6 +177,13 @@ TEST(InvokeTest, MemberFunction) { EXPECT_EQ(1, Invoke(&Class::ConstMethod, cp.get(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, *cp, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *vp, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::Method, make_unique(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, make_unique(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, make_unique(), 3, 2)); @@ -198,5 +219,5 @@ TEST(InvokeTest, SfinaeFriendly) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/log_severity.cc b/absl/base/log_severity.cc new file mode 100644 index 00000000..8109da19 --- /dev/null +++ b/absl/base/log_severity.cc @@ -0,0 +1,27 @@ +// 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/base/log_severity.h" + +#include + +namespace absl { +inline namespace lts_2019_08_08 { + +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(s) << ")"; +} +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h index c24fad79..45f79cca 100644 --- a/absl/base/log_severity.h +++ b/absl/base/log_severity.h @@ -4,24 +4,24 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT 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_LOG_SEVERITY_H_ #define ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ #include +#include #include "absl/base/attributes.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Four severity levels are defined. Logging APIs should terminate the program // when a message is logged at severity `kFatal`; the other levels have no @@ -63,7 +63,11 @@ constexpr absl::LogSeverity NormalizeLogSeverity(int s) { return NormalizeLogSeverity(static_cast(s)); } -} // inline namespace lts_2018_12_18 +// 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 } // 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 new file mode 100644 index 00000000..1de2d101 --- /dev/null +++ b/absl/base/log_severity_test.cc @@ -0,0 +1,43 @@ +// 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/log_severity.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace { +using testing::Eq; + +std::string StreamHelper(absl::LogSeverity value) { + std::ostringstream stream; + stream << value; + return stream.str(); +} + +TEST(StreamTest, Works) { + EXPECT_THAT(StreamHelper(static_cast(-100)), + Eq("absl::LogSeverity(-100)")); + EXPECT_THAT(StreamHelper(absl::LogSeverity::kInfo), Eq("INFO")); + EXPECT_THAT(StreamHelper(absl::LogSeverity::kWarning), Eq("WARNING")); + EXPECT_THAT(StreamHelper(absl::LogSeverity::kError), Eq("ERROR")); + EXPECT_THAT(StreamHelper(absl::LogSeverity::kFatal), Eq("FATAL")); + EXPECT_THAT(StreamHelper(static_cast(4)), + Eq("absl::LogSeverity(4)")); +} + +} // namespace diff --git a/absl/base/macros.h b/absl/base/macros.h index 14c4b0a3..3121088a 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,6 @@ // This code is compiled directly on many platforms, including client // platforms like Windows, Mac, and embedded systems. Before making // any changes here, make sure that you're not breaking any platforms. -// #ifndef ABSL_BASE_MACROS_H_ #define ABSL_BASE_MACROS_H_ @@ -32,6 +31,7 @@ #include #include +#include "absl/base/optimization.h" #include "absl/base/port.h" // ABSL_ARRAYSIZE() @@ -43,14 +43,14 @@ (sizeof(::absl::macros_internal::ArraySizeHelper(array))) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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 auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; } // namespace macros_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // kLinkerInitialized @@ -74,13 +74,13 @@ auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; // // Invocation // static MyClass my_global(absl::base_internal::kLinkerInitialized); namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { enum LinkerInitialized { kLinkerInitialized = 0, }; } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // ABSL_FALLTHROUGH_INTENDED @@ -196,10 +196,11 @@ enum LinkerInitialized { // This macro is inspired by // https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ #if defined(NDEBUG) -#define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0) +#define ABSL_ASSERT(expr) \ + (false ? static_cast(expr) : static_cast(0)) #else -#define ABSL_ASSERT(expr) \ - (ABSL_PREDICT_TRUE((expr)) ? (void)0 \ +#define ABSL_ASSERT(expr) \ + (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ : [] { assert(false && #expr); }()) // NOLINT #endif diff --git a/absl/base/optimization.h b/absl/base/optimization.h index 2fddfc80..0dcbef32 100644 --- a/absl/base/optimization.h +++ b/absl/base/optimization.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -111,9 +111,9 @@ // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html // for more information. // -// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to -// `__attribute__((aligned(ABSL_CACHELINE_SIZE)))`. For compilers where this is -// not known to work, the macro expands to nothing. +// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to an `__attribute__` +// or `__declspec` attribute. For compilers where this is not known to work, +// the macro expands to nothing. // // No further guarantees are made here. The result of applying the macro // to variables and types is always implementation-defined. @@ -122,6 +122,14 @@ // of causing bugs that are difficult to diagnose, crash, etc. It does not // of itself guarantee that objects are aligned to a cache line. // +// NOTE: Some compilers are picky about the locations of annotations such as +// this attribute, so prefer to put it at the beginning of your declaration. +// For example, +// +// ABSL_CACHELINE_ALIGNED static Foo* foo = ... +// +// class ABSL_CACHELINE_ALIGNED Bar { ... +// // Recommendations: // // 1) Consult compiler documentation; this comment is not kept in sync as @@ -131,8 +139,10 @@ // 3) Prefer applying this attribute to individual variables. Avoid // applying it to types. This tends to localize the effect. #define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE))) - -#else // not GCC +#elif defined(_MSC_VER) +#define ABSL_CACHELINE_SIZE 64 +#define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE)) +#else #define ABSL_CACHELINE_SIZE 64 #define ABSL_CACHELINE_ALIGNED #endif @@ -153,6 +163,12 @@ // Compilers can use the information that a certain branch is not likely to be // taken (for instance, a CHECK failure) to optimize for the common case in // the absence of better information (ie. compiling gcc with `-fprofile-arcs`). +// +// Recommendation: Modern CPUs dynamically predict branch execution paths, +// typically with accuracy greater than 97%. As a result, annotating every +// branch in a codebase is likely counterproductive; however, annotating +// specific branches that are both hot and consistently mispredicted is likely +// to yield performance improvements. #if ABSL_HAVE_BUILTIN(__builtin_expect) || \ (defined(__GNUC__) && !defined(__clang__)) #define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0)) diff --git a/absl/base/policy_checks.h b/absl/base/policy_checks.h index 0a07fc03..699fb1a2 100644 --- a/absl/base/policy_checks.h +++ b/absl/base/policy_checks.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/port.h b/absl/base/port.h index 1c67257f..6c28068d 100644 --- a/absl/base/port.h +++ b/absl/base/port.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/raw_logging_test.cc b/absl/base/raw_logging_test.cc index b21cf651..3d30bd38 100644 --- a/absl/base/raw_logging_test.cc +++ b/absl/base/raw_logging_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc index 95382977..06860e71 100644 --- a/absl/base/spinlock_test_common.cc +++ b/absl/base/spinlock_test_common.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -36,7 +36,7 @@ constexpr int32_t kNumThreads = 10; constexpr int32_t kIters = 1000; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // This is defined outside of anonymous namespace so that it can be @@ -55,6 +55,7 @@ namespace { static constexpr int kArrayLength = 10; static uint32_t values[kArrayLength]; + static SpinLock static_spinlock(base_internal::kLinkerInitialized); static SpinLock static_cooperative_spinlock( base_internal::kLinkerInitialized, @@ -62,7 +63,6 @@ static SpinLock static_cooperative_spinlock( static SpinLock static_noncooperative_spinlock( base_internal::kLinkerInitialized, base_internal::SCHEDULE_KERNEL_ONLY); - // Simple integer hash function based on the public domain lookup2 hash. // http://burtleburtle.net/bob/c/lookup2.c static uint32_t Hash32(uint32_t a, uint32_t c) { @@ -190,9 +190,11 @@ TEST(SpinLock, WaitCyclesEncoding) { SpinLockTest::DecodeWaitCycles(before_max_value); EXPECT_GT(expected_max_value_decoded, before_max_value_decoded); } + TEST(SpinLockWithThreads, StaticSpinLock) { ThreadedTest(&static_spinlock); } + TEST(SpinLockWithThreads, StackSpinLock) { SpinLock spinlock; ThreadedTest(&spinlock); @@ -265,5 +267,5 @@ TEST(SpinLockWithThreads, DoesNotDeadlock) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h index 2241ace4..f3e96589 100644 --- a/absl/base/thread_annotations.h +++ b/absl/base/thread_annotations.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,6 @@ // code. The annotations can also help program analysis tools to identify // potential thread safety issues. // -// // These annotations are implemented using compiler attributes. Using the macros // defined here instead of raw attributes allow for portability and future // compatibility. @@ -34,19 +33,23 @@ #ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_ #define ABSL_BASE_THREAD_ANNOTATIONS_H_ + +// TODO(mbonadei): Remove after the backward compatibility period. +#include "absl/base/internal/thread_annotations.h" // IWYU pragma: export + #if defined(__clang__) -#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#define ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(x) __attribute__((x)) #else -#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#define ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(x) // no-op #endif -// GUARDED_BY() +// ABSL_GUARDED_BY() // // Documents if a shared field or global variable needs to be protected by a -// mutex. GUARDED_BY() allows the user to specify a particular mutex that +// mutex. ABSL_GUARDED_BY() allows the user to specify a particular mutex that // should be held when accessing the annotated variable. // -// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to +// Although this annotation (and ABSL_PT_GUARDED_BY, below) cannot be applied to // local variables, a local variable and its associated mutex can often be // combined into a small class or struct, thereby allowing the annotation. // @@ -54,12 +57,13 @@ // // class Foo { // Mutex mu_; -// int p1_ GUARDED_BY(mu_); +// int p1_ ABSL_GUARDED_BY(mu_); // ... // }; -#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) +#define ABSL_GUARDED_BY(x) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(guarded_by(x)) -// PT_GUARDED_BY() +// ABSL_PT_GUARDED_BY() // // Documents if the memory location pointed to by a pointer should be guarded // by a mutex when dereferencing the pointer. @@ -67,7 +71,7 @@ // Example: // class Foo { // Mutex mu_; -// int *p1_ PT_GUARDED_BY(mu_); +// int *p1_ ABSL_PT_GUARDED_BY(mu_); // ... // }; // @@ -78,31 +82,32 @@ // // // `q_`, guarded by `mu1_`, points to a shared memory location that is // // guarded by `mu2_`: -// int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_); -#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) +// int *q_ ABSL_GUARDED_BY(mu1_) ABSL_PT_GUARDED_BY(mu2_); +#define ABSL_PT_GUARDED_BY(x) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(pt_guarded_by(x)) -// ACQUIRED_AFTER() / ACQUIRED_BEFORE() +// ABSL_ACQUIRED_AFTER() / ABSL_ACQUIRED_BEFORE() // // Documents the acquisition order between locks that can be held // simultaneously by a thread. For any two locks that need to be annotated // to establish an acquisition order, only one of them needs the annotation. -// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER -// and ACQUIRED_BEFORE.) +// (i.e. You don't have to annotate both locks with both ABSL_ACQUIRED_AFTER +// and ABSL_ACQUIRED_BEFORE.) // -// As with GUARDED_BY, this is only applicable to mutexes that are shared +// As with ABSL_GUARDED_BY, this is only applicable to mutexes that are shared // fields or global variables. // // Example: // // Mutex m1_; -// Mutex m2_ ACQUIRED_AFTER(m1_); -#define ACQUIRED_AFTER(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) +// Mutex m2_ ABSL_ACQUIRED_AFTER(m1_); +#define ABSL_ACQUIRED_AFTER(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(acquired_after(__VA_ARGS__)) -#define ACQUIRED_BEFORE(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) +#define ABSL_ACQUIRED_BEFORE(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(acquired_before(__VA_ARGS__)) -// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED() +// ABSL_EXCLUSIVE_LOCKS_REQUIRED() / ABSL_SHARED_LOCKS_REQUIRED() // // Documents a function that expects a mutex to be held prior to entry. // The mutex is expected to be held both on entry to, and exit from, the @@ -114,77 +119,78 @@ // concurrently. // // Generally, non-const methods should be annotated with -// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with -// SHARED_LOCKS_REQUIRED. +// ABSL_EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with +// ABSL_SHARED_LOCKS_REQUIRED. // // Example: // // Mutex mu1, mu2; -// int a GUARDED_BY(mu1); -// int b GUARDED_BY(mu2); -// -// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } -// void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } -#define EXCLUSIVE_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) +// int a ABSL_GUARDED_BY(mu1); +// int b ABSL_GUARDED_BY(mu2); +// +// void foo() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } +// void bar() const ABSL_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } +#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + exclusive_locks_required(__VA_ARGS__)) -#define SHARED_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) +#define ABSL_SHARED_LOCKS_REQUIRED(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(shared_locks_required(__VA_ARGS__)) -// LOCKS_EXCLUDED() +// ABSL_LOCKS_EXCLUDED() // // Documents the locks acquired in the body of the function. These locks // cannot be held when calling this function (as Abseil's `Mutex` locks are // non-reentrant). -#define LOCKS_EXCLUDED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) +#define ABSL_LOCKS_EXCLUDED(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(locks_excluded(__VA_ARGS__)) -// LOCK_RETURNED() +// ABSL_LOCK_RETURNED() // // Documents a function that returns a mutex without acquiring it. For example, // a public getter method that returns a pointer to a private mutex should -// be annotated with LOCK_RETURNED. -#define LOCK_RETURNED(x) \ - THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) +// be annotated with ABSL_LOCK_RETURNED. +#define ABSL_LOCK_RETURNED(x) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(lock_returned(x)) -// LOCKABLE +// ABSL_LOCKABLE // // Documents if a class/type is a lockable type (such as the `Mutex` class). -#define LOCKABLE \ - THREAD_ANNOTATION_ATTRIBUTE__(lockable) +#define ABSL_LOCKABLE ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(lockable) -// SCOPED_LOCKABLE +// ABSL_SCOPED_LOCKABLE // // Documents if a class does RAII locking (such as the `MutexLock` class). // The constructor should use `LOCK_FUNCTION()` to specify the mutex that is // acquired, and the destructor should use `UNLOCK_FUNCTION()` with no // arguments; the analysis will assume that the destructor unlocks whatever the // constructor locked. -#define SCOPED_LOCKABLE \ - THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) +#define ABSL_SCOPED_LOCKABLE \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(scoped_lockable) -// EXCLUSIVE_LOCK_FUNCTION() +// ABSL_EXCLUSIVE_LOCK_FUNCTION() // // Documents functions that acquire a lock in the body of a function, and do // not release it. -#define EXCLUSIVE_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) +#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + exclusive_lock_function(__VA_ARGS__)) -// SHARED_LOCK_FUNCTION() +// ABSL_SHARED_LOCK_FUNCTION() // // Documents functions that acquire a shared (reader) lock in the body of a // function, and do not release it. -#define SHARED_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) +#define ABSL_SHARED_LOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(shared_lock_function(__VA_ARGS__)) -// UNLOCK_FUNCTION() +// ABSL_UNLOCK_FUNCTION() // // Documents functions that expect a lock to be held on entry to the function, // and release it in the body of the function. -#define UNLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) +#define ABSL_UNLOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(unlock_function(__VA_ARGS__)) -// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION() +// ABSL_EXCLUSIVE_TRYLOCK_FUNCTION() / ABSL_SHARED_TRYLOCK_FUNCTION() // // Documents functions that try to acquire a lock, and return success or failure // (or a non-boolean value that can be interpreted as a boolean). @@ -192,76 +198,82 @@ // success, or `false` for functions that return `false` on success. The second // argument specifies the mutex that is locked on success. If unspecified, this // mutex is assumed to be `this`. -#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) +#define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + exclusive_trylock_function(__VA_ARGS__)) -#define SHARED_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) +#define ABSL_SHARED_TRYLOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + shared_trylock_function(__VA_ARGS__)) -// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK() +// ABSL_ASSERT_EXCLUSIVE_LOCK() / ABSL_ASSERT_SHARED_LOCK() // // Documents functions that dynamically check to see if a lock is held, and fail // if it is not held. -#define ASSERT_EXCLUSIVE_LOCK(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) +#define ABSL_ASSERT_EXCLUSIVE_LOCK(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(assert_exclusive_lock(__VA_ARGS__)) -#define ASSERT_SHARED_LOCK(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) +#define ABSL_ASSERT_SHARED_LOCK(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(assert_shared_lock(__VA_ARGS__)) -// NO_THREAD_SAFETY_ANALYSIS +// ABSL_NO_THREAD_SAFETY_ANALYSIS // // Turns off thread safety checking within the body of a particular function. // This annotation is used to mark functions that are known to be correct, but // the locking behavior is more complicated than the analyzer can handle. -#define NO_THREAD_SAFETY_ANALYSIS \ - THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) +#define ABSL_NO_THREAD_SAFETY_ANALYSIS \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) //------------------------------------------------------------------------------ // Tool-Supplied Annotations //------------------------------------------------------------------------------ -// TS_UNCHECKED should be placed around lock expressions that are not valid +// ABSL_TS_UNCHECKED should be placed around lock expressions that are not valid // C++ syntax, but which are present for documentation purposes. These // annotations will be ignored by the analysis. -#define TS_UNCHECKED(x) "" +#define ABSL_TS_UNCHECKED(x) "" -// TS_FIXME is used to mark lock expressions that are not valid C++ syntax. +// ABSL_TS_FIXME is used to mark lock expressions that are not valid C++ syntax. // It is used by automated tools to mark and disable invalid expressions. -// The annotation should either be fixed, or changed to TS_UNCHECKED. -#define TS_FIXME(x) "" +// The annotation should either be fixed, or changed to ABSL_TS_UNCHECKED. +#define ABSL_TS_FIXME(x) "" -// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of -// a particular function. However, this attribute is used to mark functions +// Like ABSL_NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body +// of a particular function. However, this attribute is used to mark functions // that are incorrect and need to be fixed. It is used by automated tools to // avoid breaking the build when the analysis is updated. // Code owners are expected to eventually fix the routine. -#define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS +#define ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME ABSL_NO_THREAD_SAFETY_ANALYSIS -// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY -// annotation that needs to be fixed, because it is producing thread safety -// warning. It disables the GUARDED_BY. -#define GUARDED_BY_FIXME(x) +// Similar to ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a +// ABSL_GUARDED_BY annotation that needs to be fixed, because it is producing +// thread safety warning. It disables the ABSL_GUARDED_BY. +#define ABSL_GUARDED_BY_FIXME(x) // Disables warnings for a single read operation. This can be used to avoid // warnings when it is known that the read is not actually involved in a race, // but the compiler cannot confirm that. -#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x) - +#define ABSL_TS_UNCHECKED_READ(x) absl::base_internal::ts_unchecked_read(x) -namespace thread_safety_analysis { +namespace absl { +inline namespace lts_2019_08_08 { +namespace base_internal { // Takes a reference to a guarded data member, and returns an unguarded // reference. +// Do not used this function directly, use ABSL_TS_UNCHECKED_READ instead. template -inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS { +inline const T& ts_unchecked_read(const T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { return v; } template -inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS { +inline T& ts_unchecked_read(T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { return v; } -} // namespace thread_safety_analysis +} // namespace base_internal +} // inline namespace lts_2019_08_08 +} // 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 0f15df04..a74dd3cd 100644 --- a/absl/base/throw_delegate_test.cc +++ b/absl/base/throw_delegate_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/compiler_config_setting.bzl b/absl/compiler_config_setting.bzl index b77c4f56..66962294 100644 --- a/absl/compiler_config_setting.bzl +++ b/absl/compiler_config_setting.bzl @@ -5,35 +5,34 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# """Creates config_setting that allows selecting based on 'compiler' value.""" def create_llvm_config(name, visibility): - # The "do_not_use_tools_cpp_compiler_present" attribute exists to - # distinguish between older versions of Bazel that do not support - # "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do. - # In the future, the only way to select on the compiler will be through - # flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can - # be removed. - if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"): - native.config_setting( - name = name, - flag_values = { - "@bazel_tools//tools/cpp:compiler": "llvm", - }, - visibility = visibility, - ) - else: - native.config_setting( - name = name, - values = {"compiler": "llvm"}, - visibility = visibility, - ) + # The "do_not_use_tools_cpp_compiler_present" attribute exists to + # distinguish between older versions of Bazel that do not support + # "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do. + # In the future, the only way to select on the compiler will be through + # flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can + # be removed. + if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"): + native.config_setting( + name = name, + flag_values = { + "@bazel_tools//tools/cpp:compiler": "llvm", + }, + visibility = visibility, + ) + else: + native.config_setting( + name = name, + values = {"compiler": "llvm"}, + visibility = visibility, + ) diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index afc869f4..9e2a5b1e 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,11 +15,12 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", - "ABSL_TEST_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_EXCEPTIONS_FLAG", "ABSL_EXCEPTIONS_FLAG_LINKOPTS", + "ABSL_TEST_COPTS", ) package(default_visibility = ["//visibility:public"]) @@ -30,6 +31,7 @@ cc_library( name = "compressed_tuple", hdrs = ["internal/compressed_tuple.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/utility", ], @@ -39,8 +41,14 @@ cc_test( name = "compressed_tuple_test", srcs = ["internal/compressed_tuple_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":compressed_tuple", + ":test_instance_tracker", + "//absl/memory", + "//absl/types:any", + "//absl/types:optional", + "//absl/utility", "@com_google_googletest//:gtest_main", ], ) @@ -49,6 +57,7 @@ cc_library( name = "fixed_array", hdrs = ["fixed_array.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":compressed_tuple", "//absl/algorithm", @@ -63,7 +72,7 @@ cc_test( name = "fixed_array_test", srcs = ["fixed_array_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":fixed_array", "//absl/base:exception_testing", @@ -77,6 +86,7 @@ cc_test( name = "fixed_array_test_noexceptions", srcs = ["fixed_array_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":fixed_array", "//absl/base:exception_testing", @@ -90,7 +100,7 @@ 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, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":fixed_array", "//absl/base:exception_safety_testing", @@ -102,6 +112,7 @@ cc_test( name = "fixed_array_benchmark", srcs = ["fixed_array_benchmark.cc"], copts = ABSL_TEST_COPTS + ["$(STACK_FRAME_UNLIMITED)"], + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], deps = [ ":fixed_array", @@ -109,11 +120,27 @@ cc_test( ], ) +cc_library( + name = "inlined_vector_internal", + hdrs = ["internal/inlined_vector.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":compressed_tuple", + "//absl/base:core_headers", + "//absl/memory", + "//absl/meta:type_traits", + "//absl/types:span", + ], +) + cc_library( name = "inlined_vector", hdrs = ["inlined_vector.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":inlined_vector_internal", "//absl/algorithm", "//absl/base:core_headers", "//absl/base:throw_delegate", @@ -121,12 +148,22 @@ cc_library( ], ) +cc_library( + name = "counting_allocator", + testonly = 1, + hdrs = ["internal/counting_allocator.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], +) + cc_test( name = "inlined_vector_test", srcs = ["inlined_vector_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ + ":counting_allocator", ":inlined_vector", ":test_instance_tracker", "//absl/base", @@ -143,7 +180,9 @@ 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", @@ -160,30 +199,46 @@ cc_test( name = "inlined_vector_benchmark", srcs = ["inlined_vector_benchmark.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], deps = [ ":inlined_vector", "//absl/base", + "//absl/base:core_headers", "//absl/strings", "@com_github_google_benchmark//:benchmark_main", ], ) +cc_test( + name = "inlined_vector_exception_safety_test", + srcs = ["inlined_vector_exception_safety_test.cc"], + copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + deps = [ + ":inlined_vector", + "//absl/base:exception_safety_testing", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "test_instance_tracker", testonly = 1, srcs = ["internal/test_instance_tracker.cc"], hdrs = ["internal/test_instance_tracker.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], + deps = ["//absl/types:compare"], ) cc_test( name = "test_instance_tracker_test", srcs = ["internal/test_instance_tracker_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":test_instance_tracker", "@com_google_googletest//:gtest_main", @@ -208,6 +263,7 @@ cc_library( name = "flat_hash_map", hdrs = ["flat_hash_map.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container_memory", ":hash_function_defaults", @@ -220,13 +276,15 @@ cc_library( cc_test( name = "flat_hash_map_test", srcs = ["flat_hash_map_test.cc"], - copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":flat_hash_map", ":hash_generator_testing", ":unordered_map_constructor_test", ":unordered_map_lookup_test", + ":unordered_map_members_test", ":unordered_map_modifiers_test", "//absl/types:any", "@com_google_googletest//:gtest_main", @@ -237,6 +295,7 @@ cc_library( name = "flat_hash_set", hdrs = ["flat_hash_set.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container_memory", ":hash_function_defaults", @@ -251,12 +310,14 @@ cc_test( name = "flat_hash_set_test", srcs = ["flat_hash_set_test.cc"], copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"], + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":flat_hash_set", ":hash_generator_testing", ":unordered_set_constructor_test", ":unordered_set_lookup_test", + ":unordered_set_members_test", ":unordered_set_modifiers_test", "//absl/memory", "//absl/strings", @@ -268,6 +329,7 @@ cc_library( name = "node_hash_map", hdrs = ["node_hash_map.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container_memory", ":hash_function_defaults", @@ -281,7 +343,8 @@ cc_library( cc_test( name = "node_hash_map_test", srcs = ["node_hash_map_test.cc"], - copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":hash_generator_testing", @@ -289,6 +352,7 @@ cc_test( ":tracked", ":unordered_map_constructor_test", ":unordered_map_lookup_test", + ":unordered_map_members_test", ":unordered_map_modifiers_test", "@com_google_googletest//:gtest_main", ], @@ -298,6 +362,7 @@ cc_library( name = "node_hash_set", hdrs = ["node_hash_set.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_function_defaults", ":node_hash_policy", @@ -311,12 +376,13 @@ cc_test( name = "node_hash_set_test", srcs = ["node_hash_set_test.cc"], copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"], + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ - ":hash_generator_testing", ":node_hash_set", ":unordered_set_constructor_test", ":unordered_set_lookup_test", + ":unordered_set_members_test", ":unordered_set_modifiers_test", "@com_google_googletest//:gtest_main", ], @@ -326,6 +392,7 @@ cc_library( name = "container_memory", hdrs = ["internal/container_memory.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/memory", "//absl/utility", @@ -336,6 +403,7 @@ cc_test( name = "container_memory_test", srcs = ["internal/container_memory_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":container_memory", @@ -348,6 +416,7 @@ cc_library( name = "hash_function_defaults", hdrs = ["internal/hash_function_defaults.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", "//absl/hash", @@ -359,6 +428,7 @@ cc_test( name = "hash_function_defaults_test", srcs = ["internal/hash_function_defaults_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS, deps = [ ":hash_function_defaults", @@ -374,6 +444,7 @@ cc_library( srcs = ["internal/hash_generator_testing.cc"], hdrs = ["internal/hash_generator_testing.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_testing", "//absl/meta:type_traits", @@ -386,6 +457,7 @@ cc_library( testonly = 1, hdrs = ["internal/hash_policy_testing.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/hash", "//absl/strings", @@ -396,6 +468,7 @@ cc_test( name = "hash_policy_testing_test", srcs = ["internal/hash_policy_testing_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_testing", "@com_google_googletest//:gtest_main", @@ -406,6 +479,7 @@ cc_library( name = "hash_policy_traits", hdrs = ["internal/hash_policy_traits.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = ["//absl/meta:type_traits"], ) @@ -413,6 +487,7 @@ cc_test( name = "hash_policy_traits_test", srcs = ["internal/hash_policy_traits_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_traits", "@com_google_googletest//:gtest_main", @@ -423,6 +498,7 @@ cc_library( name = "hashtable_debug", hdrs = ["internal/hashtable_debug.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hashtable_debug_hooks", ], @@ -432,18 +508,56 @@ cc_library( name = "hashtable_debug_hooks", hdrs = ["internal/hashtable_debug_hooks.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, +) + +cc_library( + name = "hashtablez_sampler", + srcs = [ + "internal/hashtablez_sampler.cc", + "internal/hashtablez_sampler_force_weak_definition.cc", + ], + hdrs = ["internal/hashtablez_sampler.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":have_sse", + "//absl/base", + "//absl/base:core_headers", + "//absl/debugging:stacktrace", + "//absl/memory", + "//absl/synchronization", + "//absl/utility", + ], +) + +cc_test( + name = "hashtablez_sampler_test", + srcs = ["internal/hashtablez_sampler_test.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":hashtablez_sampler", + ":have_sse", + "//absl/base:core_headers", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "//absl/time", + "@com_google_googletest//:gtest_main", + ], ) cc_library( name = "node_hash_policy", hdrs = ["internal/node_hash_policy.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_test( name = "node_hash_policy_test", srcs = ["internal/node_hash_policy_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_traits", ":node_hash_policy", @@ -455,9 +569,30 @@ cc_library( name = "raw_hash_map", hdrs = ["internal/raw_hash_map.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container_memory", ":raw_hash_set", + "//absl/base:throw_delegate", + ], +) + +cc_library( + name = "have_sse", + hdrs = ["internal/have_sse.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], +) + +cc_library( + name = "common", + hdrs = ["internal/common.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/meta:type_traits", + "//absl/types:optional", ], ) @@ -466,11 +601,15 @@ cc_library( srcs = ["internal/raw_hash_set.cc"], hdrs = ["internal/raw_hash_set.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":common", ":compressed_tuple", ":container_memory", ":hash_policy_traits", ":hashtable_debug_hooks", + ":hashtablez_sampler", + ":have_sse", ":layout", "//absl/base:bits", "//absl/base:config", @@ -478,7 +617,6 @@ cc_library( "//absl/base:endian", "//absl/memory", "//absl/meta:type_traits", - "//absl/types:optional", "//absl/utility", ], ) @@ -507,6 +645,7 @@ cc_test( size = "small", srcs = ["internal/raw_hash_set_allocator_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":raw_hash_set", ":tracked", @@ -519,6 +658,7 @@ cc_library( name = "layout", hdrs = ["internal/layout.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:core_headers", "//absl/meta:type_traits", @@ -533,6 +673,7 @@ cc_test( size = "small", srcs = ["internal/layout_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS, visibility = ["//visibility:private"], deps = [ @@ -549,6 +690,7 @@ cc_library( testonly = 1, hdrs = ["internal/tracked.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_library( @@ -556,6 +698,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_map_constructor_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -568,6 +711,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_map_lookup_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -580,6 +724,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_map_modifiers_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -592,9 +737,35 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_set_constructor_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", + "//absl/meta:type_traits", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "unordered_set_members_test", + testonly = 1, + hdrs = ["internal/unordered_set_members_test.h"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/meta:type_traits", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "unordered_map_members_test", + testonly = 1, + hdrs = ["internal/unordered_map_members_test.h"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/meta:type_traits", "@com_google_googletest//:gtest", ], ) @@ -604,6 +775,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_set_lookup_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -616,6 +788,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_set_modifiers_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -627,10 +800,12 @@ cc_test( name = "unordered_set_test", srcs = ["internal/unordered_set_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":unordered_set_constructor_test", ":unordered_set_lookup_test", + ":unordered_set_members_test", ":unordered_set_modifiers_test", "@com_google_googletest//:gtest_main", ], @@ -640,10 +815,12 @@ cc_test( name = "unordered_map_test", srcs = ["internal/unordered_map_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":unordered_map_constructor_test", ":unordered_map_lookup_test", + ":unordered_map_members_test", ":unordered_map_modifiers_test", "@com_google_googletest//:gtest_main", ], diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 8605facc..7988b12f 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -20,10 +20,6 @@ absl_cc_library( NAME container - SRCS - "internal/raw_hash_set.cc" - COPTS - ${ABSL_DEFAULT_COPTS} PUBLIC ) @@ -31,7 +27,9 @@ absl_cc_library( NAME compressed_tuple HDRS - "internal/compressed_tuple.h" + "internal/compressed_tuple.h" + COPTS + ${ABSL_DEFAULT_COPTS} DEPS absl::utility PUBLIC @@ -42,8 +40,15 @@ absl_cc_test( compressed_tuple_test SRCS "internal/compressed_tuple_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS + absl::any absl::compressed_tuple + absl::memory + absl::optional + absl::test_instance_tracker + absl::utility gmock_main ) @@ -70,6 +75,7 @@ absl_cc_test( SRCS "fixed_array_test.cc" COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} LINKOPTS ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} @@ -86,6 +92,8 @@ absl_cc_test( fixed_array_test_noexceptions SRCS "fixed_array_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::fixed_array absl::exception_testing @@ -100,6 +108,7 @@ absl_cc_test( SRCS "fixed_array_exception_safety_test.cc" COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} LINKOPTS ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} @@ -109,6 +118,22 @@ absl_cc_test( gmock_main ) +absl_cc_library( + NAME + inlined_vector_internal + HDRS + "internal/inlined_vector.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::compressed_tuple + absl::core_headers + absl::memory + absl::span + absl::type_traits + PUBLIC +) + absl_cc_library( NAME inlined_vector @@ -119,21 +144,33 @@ absl_cc_library( DEPS absl::algorithm absl::core_headers + absl::inlined_vector_internal absl::throw_delegate absl::memory PUBLIC ) +absl_cc_library( + NAME + counting_allocator + HDRS + "internal/counting_allocator.h" + COPTS + ${ABSL_DEFAULT_COPTS} +) + absl_cc_test( NAME inlined_vector_test SRCS "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 @@ -150,6 +187,8 @@ absl_cc_test( inlined_vector_test_noexceptions SRCS "inlined_vector_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::inlined_vector absl::test_instance_tracker @@ -162,6 +201,22 @@ absl_cc_test( gmock_main ) +absl_cc_test( + NAME + inlined_vector_exception_safety_test + SRCS + "inlined_vector_exception_safety_test.cc" + COPTS + ${ABSL_TEST_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::inlined_vector + absl::exception_safety_testing + gmock_main +) + absl_cc_library( NAME test_instance_tracker @@ -171,6 +226,8 @@ absl_cc_library( "internal/test_instance_tracker.cc" COPTS ${ABSL_DEFAULT_COPTS} + DEPS + absl::compare TESTONLY ) @@ -179,6 +236,8 @@ absl_cc_test( test_instance_tracker_test SRCS "internal/test_instance_tracker_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::test_instance_tracker gmock_main @@ -206,12 +265,13 @@ absl_cc_test( SRCS "flat_hash_map_test.cc" COPTS - "-DUNORDERED_MAP_CXX17" + ${ABSL_TEST_COPTS} DEPS absl::flat_hash_map absl::hash_generator_testing absl::unordered_map_constructor_test absl::unordered_map_lookup_test + absl::unordered_map_members_test absl::unordered_map_modifiers_test absl::any gmock_main @@ -240,12 +300,14 @@ absl_cc_test( SRCS "flat_hash_set_test.cc" COPTS + ${ABSL_TEST_COPTS} "-DUNORDERED_SET_CXX17" DEPS absl::flat_hash_set absl::hash_generator_testing absl::unordered_set_constructor_test absl::unordered_set_lookup_test + absl::unordered_set_members_test absl::unordered_set_modifiers_test absl::memory absl::strings @@ -275,13 +337,14 @@ absl_cc_test( SRCS "node_hash_map_test.cc" COPTS - "-DUNORDERED_MAP_CXX17" + ${ABSL_TEST_COPTS} DEPS absl::hash_generator_testing absl::node_hash_map absl::tracked absl::unordered_map_constructor_test absl::unordered_map_lookup_test + absl::unordered_map_members_test absl::unordered_map_modifiers_test gmock_main ) @@ -308,12 +371,14 @@ absl_cc_test( SRCS "node_hash_set_test.cc" COPTS + ${ABSL_TEST_COPTS} "-DUNORDERED_SET_CXX17" DEPS absl::hash_generator_testing absl::node_hash_set absl::unordered_set_constructor_test absl::unordered_set_lookup_test + absl::unordered_set_members_test absl::unordered_set_modifiers_test gmock_main ) @@ -336,6 +401,8 @@ absl_cc_test( container_memory_test SRCS "internal/container_memory_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::container_memory absl::strings @@ -361,6 +428,8 @@ absl_cc_test( hash_function_defaults_test SRCS "internal/hash_function_defaults_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash_function_defaults absl::hash @@ -402,6 +471,8 @@ absl_cc_test( hash_policy_testing_test SRCS "internal/hash_policy_testing_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash_policy_testing gmock_main @@ -424,11 +495,42 @@ absl_cc_test( hash_policy_traits_test SRCS "internal/hash_policy_traits_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash_policy_traits gmock_main ) +absl_cc_library( + NAME + hashtablez_sampler + HDRS + "internal/hashtablez_sampler.h" + SRCS + "internal/hashtablez_sampler.cc" + "internal/hashtablez_sampler_force_weak_definition.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::have_sse + absl::synchronization +) + +absl_cc_test( + NAME + hashtablez_sampler_test + SRCS + "internal/hashtablez_sampler_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hashtablez_sampler + absl::have_sse + gmock_main +) + absl_cc_library( NAME hashtable_debug @@ -450,6 +552,15 @@ absl_cc_library( PUBLIC ) +absl_cc_library( + NAME + have_sse + HDRS + "internal/have_sse.h" + COPTS + ${ABSL_DEFAULT_COPTS} +) + absl_cc_library( NAME node_hash_policy @@ -465,6 +576,8 @@ absl_cc_test( node_hash_policy_test SRCS "internal/node_hash_policy_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash_policy_traits absl::node_hash_policy @@ -481,9 +594,21 @@ absl_cc_library( DEPS absl::container_memory absl::raw_hash_set + absl::throw_delegate PUBLIC ) +absl_cc_library( + NAME + container_common + HDRS + "internal/commom.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::type_traits +) + absl_cc_library( NAME raw_hash_set @@ -494,19 +619,22 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::bits absl::compressed_tuple + absl::config + absl::container_common absl::container_memory + absl::core_headers + absl::endian absl::hash_policy_traits absl::hashtable_debug_hooks + absl::have_sse absl::layout - absl::bits - absl::config - absl::core_headers - absl::endian absl::memory absl::meta absl::optional absl::utility + absl::hashtablez_sampler PUBLIC ) @@ -515,6 +643,8 @@ absl_cc_test( raw_hash_set_test SRCS "internal/raw_hash_set_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::container_memory absl::hash_function_defaults @@ -532,6 +662,8 @@ absl_cc_test( raw_hash_set_allocator_test SRCS "internal/raw_hash_set_allocator_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::raw_hash_set absl::tracked @@ -560,6 +692,8 @@ absl_cc_test( layout_test SRCS "internal/layout_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::layout absl::base @@ -606,6 +740,19 @@ absl_cc_library( TESTONLY ) +absl_cc_library( + NAME + unordered_map_members_test + HDRS + "internal/unordered_map_members_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::type_traits + gmock + TESTONLY +) + absl_cc_library( NAME unordered_map_modifiers_test @@ -648,6 +795,19 @@ absl_cc_library( TESTONLY ) +absl_cc_library( + NAME + unordered_set_members_test + HDRS + "internal/unordered_set_members_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::type_traits + gmock + TESTONLY +) + absl_cc_library( NAME unordered_set_modifiers_test @@ -667,9 +827,12 @@ absl_cc_test( unordered_set_test SRCS "internal/unordered_set_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::unordered_set_constructor_test absl::unordered_set_lookup_test + absl::unordered_set_members_test absl::unordered_set_modifiers_test gmock_main ) @@ -679,9 +842,12 @@ absl_cc_test( unordered_map_test SRCS "internal/unordered_map_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::unordered_map_constructor_test absl::unordered_map_lookup_test + absl::unordered_map_members_test absl::unordered_map_modifiers_test gmock_main ) diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index 7f6a3afd..1e0da5eb 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -51,7 +51,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { constexpr static auto kFixedArrayUseDefault = static_cast(-1); @@ -515,6 +515,7 @@ void FixedArray::NonEmptyInlinedStorage::AnnotateDestruct( #endif // ADDRESS_SANITIZER static_cast(n); // Mark used when not in asan mode } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_CONTAINER_FIXED_ARRAY_H_ diff --git a/absl/container/fixed_array_benchmark.cc b/absl/container/fixed_array_benchmark.cc index b4f0cf2a..3c7a5a72 100644 --- a/absl/container/fixed_array_benchmark.cc +++ b/absl/container/fixed_array_benchmark.cc @@ -1,10 +1,10 @@ -// 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. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/container/fixed_array.h" - #include + #include #include "benchmark/benchmark.h" +#include "absl/container/fixed_array.h" namespace { @@ -25,8 +25,9 @@ namespace { // set an int to a constant.. class SimpleClass { public: - SimpleClass() : i(3) { } + SimpleClass() : i(3) {} ~SimpleClass() { i = 0; } + private: int i; }; diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc index 4d0430b3..4a67bb46 100644 --- a/absl/container/fixed_array_exception_safety_test.cc +++ b/absl/container/fixed_array_exception_safety_test.cc @@ -1,10 +1,10 @@ -// 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. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -14,13 +14,12 @@ #include -#include "absl/container/fixed_array.h" - #include "gtest/gtest.h" #include "absl/base/internal/exception_safety_testing.h" +#include "absl/container/fixed_array.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { @@ -115,5 +114,5 @@ TEST(FixedArrayExceptionSafety, Fill) { } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc index 205ff41f..2b1cf47e 100644 --- a/absl/container/fixed_array_test.cc +++ b/absl/container/fixed_array_test.cc @@ -1,10 +1,10 @@ -// 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. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,6 +15,7 @@ #include "absl/container/fixed_array.h" #include + #include #include #include @@ -42,11 +43,7 @@ static bool IsOnStack(const ArrayType& a) { class ConstructionTester { public: - ConstructionTester() - : self_ptr_(this), - value_(0) { - constructions++; - } + ConstructionTester() : self_ptr_(this), value_(0) { constructions++; } ~ConstructionTester() { assert(self_ptr_ == this); self_ptr_ = nullptr; @@ -58,9 +55,7 @@ class ConstructionTester { static int constructions; static int destructions; - void CheckConstructed() { - assert(self_ptr_ == this); - } + void CheckConstructed() { assert(self_ptr_ == this); } void set(int value) { value_ = value; } int get() { return value_; } @@ -150,7 +145,7 @@ TEST(FixedArrayTest, SmallObjects) { } { - // Arrays of > default size should be on the stack + // Arrays of > default size should be on the heap absl::FixedArray array(101); EXPECT_FALSE(IsOnStack(array)); } @@ -160,13 +155,13 @@ TEST(FixedArrayTest, SmallObjects) { // same amount of stack space absl::FixedArray array1(0); absl::FixedArray array2(0); - EXPECT_LE(sizeof(array1), sizeof(array2)+100); - EXPECT_LE(sizeof(array2), sizeof(array1)+100); + EXPECT_LE(sizeof(array1), sizeof(array2) + 100); + EXPECT_LE(sizeof(array2), sizeof(array1) + 100); } { // Ensure that vectors are properly constructed inside a fixed array. - absl::FixedArray > array(2); + absl::FixedArray> array(2); EXPECT_EQ(0, array[0].size()); EXPECT_EQ(0, array[1].size()); } @@ -270,8 +265,8 @@ static void TestArray(int n) { array.data()[i].set(i + 1); } for (int i = 0; i < n; i++) { - EXPECT_THAT(array[i].get(), i+1); - EXPECT_THAT(array.data()[i].get(), i+1); + EXPECT_THAT(array[i].get(), i + 1); + EXPECT_THAT(array.data()[i].get(), i + 1); } } // Close scope containing 'array'. @@ -296,7 +291,7 @@ static void TestArrayOfArrays(int n) { ASSERT_EQ(array.size(), n); ASSERT_EQ(array.memsize(), - sizeof(ConstructionTester) * elements_per_inner_array * n); + sizeof(ConstructionTester) * elements_per_inner_array * n); ASSERT_EQ(array.begin() + n, array.end()); // Check that all elements were constructed @@ -316,7 +311,7 @@ static void TestArrayOfArrays(int n) { } for (int i = 0; i < n; i++) { for (int j = 0; j < elements_per_inner_array; j++) { - ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j); + ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j); ASSERT_EQ((array.data()[i])[j].get(), i * elements_per_inner_array + j); } } @@ -329,8 +324,7 @@ static void TestArrayOfArrays(int n) { } for (int i = 0; i < n; i++) { for (int j = 0; j < elements_per_inner_array; j++) { - ASSERT_EQ((array[i])[j].get(), - (i + 1) * elements_per_inner_array + j); + ASSERT_EQ((array[i])[j].get(), (i + 1) * elements_per_inner_array + j); ASSERT_EQ((array.data()[i])[j].get(), (i + 1) * elements_per_inner_array + j); } @@ -343,7 +337,7 @@ static void TestArrayOfArrays(int n) { } TEST(IteratorConstructorTest, NonInline) { - int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; absl::FixedArray const fixed( kInput, kInput + ABSL_ARRAYSIZE(kInput)); ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size()); @@ -353,7 +347,7 @@ TEST(IteratorConstructorTest, NonInline) { } TEST(IteratorConstructorTest, Inline) { - int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; absl::FixedArray const fixed( kInput, kInput + ABSL_ARRAYSIZE(kInput)); ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size()); @@ -363,9 +357,10 @@ TEST(IteratorConstructorTest, Inline) { } TEST(IteratorConstructorTest, NonPod) { - char const* kInput[] = - { "red", "orange", "yellow", "green", "blue", "indigo", "violet" }; - absl::FixedArray const fixed(kInput, kInput + ABSL_ARRAYSIZE(kInput)); + char const* kInput[] = {"red", "orange", "yellow", "green", + "blue", "indigo", "violet"}; + absl::FixedArray const fixed(kInput, + kInput + ABSL_ARRAYSIZE(kInput)); ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size()); for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) { ASSERT_EQ(kInput[i], fixed[i]); @@ -380,7 +375,7 @@ TEST(IteratorConstructorTest, FromEmptyVector) { } TEST(IteratorConstructorTest, FromNonEmptyVector) { - int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; std::vector const items(kInput, kInput + ABSL_ARRAYSIZE(kInput)); absl::FixedArray const fixed(items.begin(), items.end()); ASSERT_EQ(items.size(), fixed.size()); @@ -390,7 +385,7 @@ TEST(IteratorConstructorTest, FromNonEmptyVector) { } TEST(IteratorConstructorTest, FromBidirectionalIteratorRange) { - int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; std::list const items(kInput, kInput + ABSL_ARRAYSIZE(kInput)); absl::FixedArray const fixed(items.begin(), items.end()); EXPECT_THAT(fixed, testing::ElementsAreArray(kInput)); @@ -507,9 +502,8 @@ struct PickyDelete { TEST(FixedArrayTest, UsesGlobalAlloc) { absl::FixedArray a(5); } - TEST(FixedArrayTest, Data) { - static const int kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + static const int kInput[] = {2, 3, 5, 7, 11, 13, 17}; absl::FixedArray fa(std::begin(kInput), std::end(kInput)); EXPECT_EQ(fa.data(), &*fa.begin()); EXPECT_EQ(fa.data(), &fa[0]); @@ -823,7 +817,7 @@ TEST(AllocatorSupportTest, SizeValAllocConstructor) { #ifdef ADDRESS_SANITIZER TEST(FixedArrayTest, AddressSanitizerAnnotations1) { absl::FixedArray a(10); - int *raw = a.data(); + int* raw = a.data(); raw[0] = 0; raw[9] = 0; EXPECT_DEATH(raw[-2] = 0, "container-overflow"); @@ -834,7 +828,7 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations1) { TEST(FixedArrayTest, AddressSanitizerAnnotations2) { absl::FixedArray a(12); - char *raw = a.data(); + char* raw = a.data(); raw[0] = 0; raw[11] = 0; EXPECT_DEATH(raw[-7] = 0, "container-overflow"); @@ -845,7 +839,7 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations2) { TEST(FixedArrayTest, AddressSanitizerAnnotations3) { absl::FixedArray a(20); - uint64_t *raw = a.data(); + uint64_t* raw = a.data(); raw[0] = 0; raw[19] = 0; EXPECT_DEATH(raw[-1] = 0, "container-overflow"); @@ -854,7 +848,7 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations3) { TEST(FixedArrayTest, AddressSanitizerAnnotations4) { absl::FixedArray a(10); - ThreeInts *raw = a.data(); + ThreeInts* raw = a.data(); raw[0] = ThreeInts(); raw[9] = ThreeInts(); // Note: raw[-1] is pointing to 12 bytes before the container range. However, @@ -869,4 +863,21 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations4) { } #endif // ADDRESS_SANITIZER +TEST(FixedArrayTest, AbslHashValueWorks) { + using V = absl::FixedArray; + std::vector cases; + + // Generate a variety of vectors some of these are small enough for the inline + // space but are stored out of line. + for (int i = 0; i < 10; ++i) { + V v(i); + for (int j = 0; j < i; ++j) { + v[j] = j; + } + cases.push_back(v); + } + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases)); +} + } // namespace diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index ed453348..a711398e 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -42,7 +42,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template struct FlatHashMapPolicy; @@ -78,7 +78,7 @@ struct FlatHashMapPolicy; // NOTE: A `flat_hash_map` stores its value types directly inside its // implementation array to avoid memory indirection. Because a `flat_hash_map` // is designed to move data when rehashed, map values will not retain pointer -// stability. If you require pointer stability, or your values are large, +// stability. If you require pointer stability, or if your values are large, // consider using `absl::flat_hash_map>` instead. // If your types are not moveable or you require pointer stability for keys, // consider `absl::node_hash_map`. @@ -220,8 +220,12 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< // Erases the element at `position` of the `flat_hash_map`, returning // `void`. // - // NOTE: this return behavior is different than that of STL containers in - // general and `std::unordered_map` in particular. + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_map` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // map.erase(it++); // // iterator erase(const_iterator first, const_iterator last): // @@ -528,25 +532,26 @@ namespace container_internal { template struct FlatHashMapPolicy { - using slot_type = container_internal::slot_type; + using slot_policy = container_internal::map_slot_policy; + using slot_type = typename slot_policy::slot_type; using key_type = K; using mapped_type = V; using init_type = std::pair; template static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { - slot_type::construct(alloc, slot, std::forward(args)...); + slot_policy::construct(alloc, slot, std::forward(args)...); } template static void destroy(Allocator* alloc, slot_type* slot) { - slot_type::destroy(alloc, slot); + slot_policy::destroy(alloc, slot); } template static void transfer(Allocator* alloc, slot_type* new_slot, slot_type* old_slot) { - slot_type::transfer(alloc, new_slot, old_slot); + slot_policy::transfer(alloc, new_slot, old_slot); } template @@ -576,7 +581,7 @@ struct IsUnorderedContainer< } // namespace container_algorithm_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 02d2fa81..3f11a52c 100644 --- a/absl/container/flat_hash_map_test.cc +++ b/absl/container/flat_hash_map_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,11 +17,12 @@ #include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/unordered_map_constructor_test.h" #include "absl/container/internal/unordered_map_lookup_test.h" +#include "absl/container/internal/unordered_map_members_test.h" #include "absl/container/internal/unordered_map_modifiers_test.h" #include "absl/types/any.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { using ::absl::container_internal::hash_internal::Enum; @@ -31,19 +32,20 @@ using ::testing::Pair; using ::testing::UnorderedElementsAre; template -using Map = - flat_hash_map>; +using Map = flat_hash_map>>; static_assert(!std::is_standard_layout(), ""); using MapTypes = - ::testing::Types, Map, Map, - Map, Map, - Map>; + ::testing::Types, Map, + Map, Map, + Map, Map>; -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ConstructorTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, LookupTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ModifiersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ConstructorTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, LookupTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, MembersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ModifiersTest, MapTypes); TEST(FlatHashMap, StandardLayout) { struct Int { @@ -140,6 +142,7 @@ TEST(FlatHashMap, LazyKeyPattern) { int conversions = 0; int hashes = 0; flat_hash_map m(0, Hash{&hashes}); + m.reserve(3); m[LazyInt(1, &conversions)] = 1; EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1))); @@ -204,7 +207,9 @@ TEST(FlatHashMap, MergeExtractInsert) { m.insert(std::move(node)); EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9))); } -#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__) + +#if (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) && \ + !defined(__EMSCRIPTEN__) TEST(FlatHashMap, Any) { absl::flat_hash_map m; m.emplace(1, 7); @@ -235,9 +240,10 @@ TEST(FlatHashMap, Any) { ASSERT_NE(it2, m2.end()); EXPECT_EQ(7, it2->second); } -#endif // __ANDROID__ +#endif // (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) && + // !defined(__EMSCRIPTEN__) } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index b175b1bf..8adbbcd5 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -40,7 +40,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template struct FlatHashSetPolicy; @@ -56,9 +56,9 @@ struct FlatHashSetPolicy; // following notable differences: // // * Requires keys that are CopyConstructible -// * Supports heterogeneous lookup, through `find()`, `operator[]()` and -// `insert()`, provided that the set is provided a compatible heterogeneous -// hashing function and equality operator. +// * Supports heterogeneous lookup, through `find()` and `insert()`, provided +// that the set is provided a compatible heterogeneous hashing function and +// equality operator. // * Invalidates any references and pointers to elements within the table after // `rehash()`. // * Contains a `capacity()` member function indicating the number of element @@ -213,8 +213,12 @@ class flat_hash_set // Erases the element at `position` of the `flat_hash_set`, returning // `void`. // - // NOTE: this return behavior is different than that of STL containers in - // general and `std::unordered_map` in particular. + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_set` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // set.erase(it++); // // iterator erase(const_iterator first, const_iterator last): // @@ -485,7 +489,7 @@ struct IsUnorderedContainer> } // namespace container_algorithm_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 cabc2b59..56140bbe 100644 --- a/absl/container/flat_hash_set_test.cc +++ b/absl/container/flat_hash_set_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,12 +19,13 @@ #include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/unordered_set_constructor_test.h" #include "absl/container/internal/unordered_set_lookup_test.h" +#include "absl/container/internal/unordered_set_members_test.h" #include "absl/container/internal/unordered_set_modifiers_test.h" #include "absl/memory/memory.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -41,9 +42,10 @@ using Set = using SetTypes = ::testing::Types, Set, Set, Set>; -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ConstructorTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, LookupTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ModifiersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, ConstructorTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, LookupTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, MembersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, ModifiersTest, SetTypes); TEST(FlatHashSet, EmplaceString) { std::vector v = {"a", "b"}; @@ -124,5 +126,5 @@ TEST(FlatHashSet, MergeExtractInsert) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 37714baf..27186b15 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -1,10 +1,10 @@ -// 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. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -50,11 +50,11 @@ #include "absl/base/internal/throw_delegate.h" #include "absl/base/optimization.h" #include "absl/base/port.h" +#include "absl/container/internal/inlined_vector.h" #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { - +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // InlinedVector // ----------------------------------------------------------------------------- @@ -67,119 +67,181 @@ inline namespace lts_2018_12_18 { // designed to cover the same API footprint as covered by `std::vector`. template > class InlinedVector { - static_assert(N > 0, "InlinedVector requires inline capacity greater than 0"); - constexpr static typename A::size_type inlined_capacity() { - return static_cast(N); - } + static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity."); - template - using DisableIfIntegral = - absl::enable_if_t::value>; + using Storage = inlined_vector_internal::Storage; + using rvalue_reference = typename Storage::rvalue_reference; + using MoveIterator = typename Storage::MoveIterator; + using AllocatorTraits = typename Storage::AllocatorTraits; + using IsMemcpyOk = typename Storage::IsMemcpyOk; template - using EnableIfInputIterator = absl::enable_if_t::iterator_category, - std::input_iterator_tag>::value>; + using IteratorValueAdapter = + typename Storage::template IteratorValueAdapter; + using CopyValueAdapter = typename Storage::CopyValueAdapter; + using DefaultValueAdapter = typename Storage::DefaultValueAdapter; template - using IteratorCategory = - typename std::iterator_traits::iterator_category; - - using rvalue_reference = typename A::value_type&&; + using EnableIfAtLeastForwardIterator = absl::enable_if_t< + inlined_vector_internal::IsAtLeastForwardIterator::value>; + template + using DisableIfAtLeastForwardIterator = absl::enable_if_t< + !inlined_vector_internal::IsAtLeastForwardIterator::value>; 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 size_type = typename allocator_type::size_type; - using difference_type = typename allocator_type::difference_type; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; + using allocator_type = typename Storage::allocator_type; + using value_type = typename Storage::value_type; + using pointer = typename Storage::pointer; + using const_pointer = typename Storage::const_pointer; + using 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 iterator = typename Storage::iterator; + using const_iterator = typename Storage::const_iterator; + using reverse_iterator = typename Storage::reverse_iterator; + using const_reverse_iterator = typename Storage::const_reverse_iterator; // --------------------------------------------------------------------------- // InlinedVector Constructors and Destructor // --------------------------------------------------------------------------- - // Creates an empty inlined vector with a default initialized allocator. - InlinedVector() noexcept(noexcept(allocator_type())) - : allocator_and_tag_(allocator_type()) {} + // Creates an empty inlined vector with a value-initialized allocator. + InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {} - // Creates an empty inlined vector with a specified allocator. + // Creates an empty inlined vector with a copy of `alloc`. explicit InlinedVector(const allocator_type& alloc) noexcept - : allocator_and_tag_(alloc) {} + : storage_(alloc) {} // Creates an inlined vector with `n` copies of `value_type()`. explicit InlinedVector(size_type n, const allocator_type& alloc = allocator_type()) - : allocator_and_tag_(alloc) { - InitAssign(n); + : storage_(alloc) { + storage_.Initialize(DefaultValueAdapter(), n); } // Creates an inlined vector with `n` copies of `v`. InlinedVector(size_type n, const_reference v, const allocator_type& alloc = allocator_type()) - : allocator_and_tag_(alloc) { - InitAssign(n, v); + : storage_(alloc) { + storage_.Initialize(CopyValueAdapter(v), n); } - // Creates an inlined vector of copies of the values in `init_list`. - InlinedVector(std::initializer_list init_list, + // Creates an inlined vector with copies of the elements of `list`. + InlinedVector(std::initializer_list list, const allocator_type& alloc = allocator_type()) - : allocator_and_tag_(alloc) { - AppendRange(init_list.begin(), init_list.end(), - IteratorCategory{}); - } + : InlinedVector(list.begin(), list.end(), alloc) {} // Creates an inlined vector with elements constructed from the provided - // Iterator range [`first`, `last`). + // forward iterator range [`first`, `last`). // - // NOTE: The `enable_if` prevents ambiguous interpretation between a call to + // NOTE: the `enable_if` prevents ambiguous interpretation between a call to // this constructor with two integral arguments and a call to the above // `InlinedVector(size_type, const_reference)` constructor. - template * = nullptr> + template * = nullptr> + InlinedVector(ForwardIterator first, ForwardIterator last, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + storage_.Initialize(IteratorValueAdapter(first), + std::distance(first, last)); + } + + // Creates an inlined vector with elements constructed from the provided input + // iterator range [`first`, `last`). + template * = nullptr> InlinedVector(InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()) - : allocator_and_tag_(alloc) { - AppendRange(first, last, IteratorCategory{}); + : storage_(alloc) { + std::copy(first, last, std::back_inserter(*this)); } - // Creates a copy of `other` using `other`'s allocator. - InlinedVector(const InlinedVector& other); + // Creates an inlined vector by copying the contents of `other` using + // `other`'s allocator. + InlinedVector(const InlinedVector& other) + : InlinedVector(other, *other.storage_.GetAllocPtr()) {} - // Creates a copy of `other` but with a specified allocator. - InlinedVector(const InlinedVector& other, const allocator_type& alloc); + // Creates an inlined vector by copying the contents of `other` using `alloc`. + InlinedVector(const InlinedVector& other, const allocator_type& alloc) + : storage_(alloc) { + if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) { + storage_.MemcpyFrom(other.storage_); + } else { + storage_.Initialize(IteratorValueAdapter(other.data()), + other.size()); + } + } - // Creates an inlined vector by moving in the contents of `other`. + // Creates an inlined vector by moving in the contents of `other` without + // allocating. If `other` contains allocated memory, the newly-created inlined + // vector will take ownership of that memory. However, if `other` does not + // contain allocated memory, the newly-created inlined vector will perform + // element-wise move construction of the contents of `other`. // - // NOTE: This move constructor does not allocate and only moves the underlying - // objects, so its `noexcept` specification depends on whether moving the - // underlying objects can throw or not. We assume: - // a) move constructors should only throw due to allocation failure and + // NOTE: since no allocation is performed for the inlined vector in either + // case, the `noexcept(...)` specification depends on whether moving the + // underlying objects can throw. It is assumed assumed that... + // a) move constructors should only throw due to allocation failure. // b) if `value_type`'s move constructor allocates, it uses the same - // allocation function as the `InlinedVector`'s allocator, so the move - // constructor is non-throwing if the allocator is non-throwing or - // `value_type`'s move constructor is specified as `noexcept`. - InlinedVector(InlinedVector&& v) noexcept( + // allocation function as the inlined vector's allocator. + // Thus, the move constructor is non-throwing if the allocator is non-throwing + // or `value_type`'s move constructor is specified as `noexcept`. + InlinedVector(InlinedVector&& other) noexcept( absl::allocator_is_nothrow::value || - std::is_nothrow_move_constructible::value); + std::is_nothrow_move_constructible::value) + : storage_(*other.storage_.GetAllocPtr()) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if (other.storage_.GetIsAllocated()) { + storage_.SetAllocatedData(other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + } else { + IteratorValueAdapter other_values( + MoveIterator(other.storage_.GetInlinedData())); + + inlined_vector_internal::ConstructElements( + storage_.GetAllocPtr(), storage_.GetInlinedData(), &other_values, + other.storage_.GetSize()); - // Creates an inlined vector by moving in the contents of `other`. + storage_.SetInlinedSize(other.storage_.GetSize()); + } + } + + // Creates an inlined vector by moving in the contents of `other` with a copy + // of `alloc`. // - // NOTE: This move constructor allocates and subsequently moves the underlying - // objects, so its `noexcept` specification depends on whether the allocation - // can throw and whether moving the underlying objects can throw. Based on the - // same assumptions as above, the `noexcept` specification is dominated by - // whether the allocation can throw regardless of whether `value_type`'s move - // constructor is specified as `noexcept`. - InlinedVector(InlinedVector&& v, const allocator_type& alloc) noexcept( - absl::allocator_is_nothrow::value); + // NOTE: if `other`'s allocator is not equal to `alloc`, even if `other` + // contains allocated memory, this move constructor will still allocate. Since + // allocation is performed, this constructor can only be `noexcept` if the + // specified allocator is also `noexcept`. + InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept( + absl::allocator_is_nothrow::value) + : storage_(alloc) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) && + other.storage_.GetIsAllocated()) { + storage_.SetAllocatedData(other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + } else { + storage_.Initialize( + IteratorValueAdapter(MoveIterator(other.data())), + other.size()); + } + } - ~InlinedVector() { clear(); } + ~InlinedVector() {} // --------------------------------------------------------------------------- // InlinedVector Member Accessors @@ -187,87 +249,102 @@ class InlinedVector { // `InlinedVector::empty()` // - // Checks if the inlined vector has no elements. + // Returns whether the inlined vector contains no elements. bool empty() const noexcept { return !size(); } // `InlinedVector::size()` // // Returns the number of elements in the inlined vector. - size_type size() const noexcept { return tag().size(); } + size_type size() const noexcept { return storage_.GetSize(); } // `InlinedVector::max_size()` // - // Returns the maximum number of elements the vector can hold. + // Returns the maximum number of elements the inlined vector can hold. size_type max_size() const noexcept { // One bit of the size storage is used to indicate whether the inlined - // vector is allocated. As a result, the maximum size of the container that - // we can express is half of the max for `size_type`. + // vector contains allocated memory. As a result, the maximum size that the + // inlined vector can express is half of the max for `size_type`. return (std::numeric_limits::max)() / 2; } // `InlinedVector::capacity()` // - // Returns the number of elements that can be stored in the inlined vector - // without requiring a reallocation of underlying memory. + // Returns the number of elements that could be stored in the inlined vector + // without requiring a reallocation. // - // NOTE: For most inlined vectors, `capacity()` should equal - // `inlined_capacity()`. For inlined vectors which exceed this capacity, they - // will no longer be inlined and `capacity()` will equal its capacity on the - // allocated heap. + // NOTE: for most inlined vectors, `capacity()` should be equal to the + // template parameter `N`. For inlined vectors which exceed this capacity, + // they will no longer be inlined and `capacity()` will equal the capactity of + // the allocated memory. size_type capacity() const noexcept { - return allocated() ? allocation().capacity() : inlined_capacity(); + return storage_.GetIsAllocated() ? storage_.GetAllocatedCapacity() + : storage_.GetInlinedCapacity(); } // `InlinedVector::data()` // - // Returns a `pointer` to elements of the inlined vector. This pointer can be - // used to access and modify the contained elements. - // Only results within the range [`0`, `size()`) are defined. + // Returns a `pointer` to the elements of the inlined vector. This pointer + // can be used to access and modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. pointer data() noexcept { - return allocated() ? allocated_space() : inlined_space(); + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); } - // Overload of `InlinedVector::data()` to return a `const_pointer` to elements - // of the inlined vector. This pointer can be used to access (but not modify) - // the contained elements. + // Overload of `InlinedVector::data()` that returns a `const_pointer` to the + // elements of the inlined vector. This pointer can be used to access but not + // modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. const_pointer data() const noexcept { - return allocated() ? allocated_space() : inlined_space(); + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); } - // `InlinedVector::operator[]()` + // `InlinedVector::operator[](...)` // - // Returns a `reference` to the `i`th element of the inlined vector using the - // array operator. + // Returns a `reference` to the `i`th element of the inlined vector. reference operator[](size_type i) { assert(i < size()); + return data()[i]; } - // Overload of `InlinedVector::operator[]()` to return a `const_reference` to - // the `i`th element of the inlined vector. + // Overload of `InlinedVector::operator[](...)` that returns a + // `const_reference` to the `i`th element of the inlined vector. const_reference operator[](size_type i) const { assert(i < size()); + return data()[i]; } - // `InlinedVector::at()` + // `InlinedVector::at(...)` // // Returns a `reference` to the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. reference at(size_type i) { if (ABSL_PREDICT_FALSE(i >= size())) { base_internal::ThrowStdOutOfRange( - "InlinedVector::at() failed bounds check"); + "`InlinedVector::at(size_type)` failed bounds check"); } + return data()[i]; } - // Overload of `InlinedVector::at()` to return a `const_reference` to the - // `i`th element of the inlined vector. + // Overload of `InlinedVector::at(...)` that returns a `const_reference` to + // the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. const_reference at(size_type i) const { if (ABSL_PREDICT_FALSE(i >= size())) { base_internal::ThrowStdOutOfRange( - "InlinedVector::at() failed bounds check"); + "`InlinedVector::at(size_type) const` failed bounds check"); } + return data()[i]; } @@ -276,13 +353,15 @@ class InlinedVector { // Returns a `reference` to the first element of the inlined vector. reference front() { assert(!empty()); + return at(0); } - // Overload of `InlinedVector::front()` returns a `const_reference` to the - // first element of the inlined vector. + // Overload of `InlinedVector::front()` that returns a `const_reference` to + // the first element of the inlined vector. const_reference front() const { assert(!empty()); + return at(0); } @@ -291,13 +370,15 @@ class InlinedVector { // Returns a `reference` to the last element of the inlined vector. reference back() { assert(!empty()); + return at(size() - 1); } - // Overload of `InlinedVector::back()` to return a `const_reference` to the + // Overload of `InlinedVector::back()` that returns a `const_reference` to the // last element of the inlined vector. const_reference back() const { assert(!empty()); + return at(size() - 1); } @@ -306,7 +387,7 @@ class InlinedVector { // Returns an `iterator` to the beginning of the inlined vector. iterator begin() noexcept { return data(); } - // Overload of `InlinedVector::begin()` to return a `const_iterator` to + // Overload of `InlinedVector::begin()` that returns a `const_iterator` to // the beginning of the inlined vector. const_iterator begin() const noexcept { return data(); } @@ -315,7 +396,7 @@ class InlinedVector { // Returns an `iterator` to the end of the inlined vector. iterator end() noexcept { return data() + size(); } - // Overload of `InlinedVector::end()` to return a `const_iterator` to the + // Overload of `InlinedVector::end()` that returns a `const_iterator` to the // end of the inlined vector. const_iterator end() const noexcept { return data() + size(); } @@ -334,7 +415,7 @@ class InlinedVector { // Returns a `reverse_iterator` from the end of the inlined vector. reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - // Overload of `InlinedVector::rbegin()` to return a + // Overload of `InlinedVector::rbegin()` that returns a // `const_reverse_iterator` from the end of the inlined vector. const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); @@ -345,7 +426,7 @@ class InlinedVector { // Returns a `reverse_iterator` from the beginning of the inlined vector. reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - // Overload of `InlinedVector::rend()` to return a `const_reverse_iterator` + // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator` // from the beginning of the inlined vector. const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); @@ -364,1086 +445,403 @@ class InlinedVector { // `InlinedVector::get_allocator()` // - // Returns a copy of the allocator of the inlined vector. - allocator_type get_allocator() const { return allocator(); } + // Returns a copy of the inlined vector's allocator. + allocator_type get_allocator() const { return *storage_.GetAllocPtr(); } // --------------------------------------------------------------------------- // InlinedVector Member Mutators // --------------------------------------------------------------------------- - // `InlinedVector::operator=()` + // `InlinedVector::operator=(...)` // - // Replaces the contents of the inlined vector with copies of the elements in - // the provided `std::initializer_list`. - InlinedVector& operator=(std::initializer_list init_list) { - AssignRange(init_list.begin(), init_list.end(), - IteratorCategory{}); + // Replaces the elements of the inlined vector with copies of the elements of + // `list`. + InlinedVector& operator=(std::initializer_list list) { + assign(list.begin(), list.end()); + return *this; } - // Overload of `InlinedVector::operator=()` to replace the contents of the - // inlined vector with the contents of `other`. + // Overload of `InlinedVector::operator=(...)` that replaces the elements of + // the inlined vector with copies of the elements of `other`. InlinedVector& operator=(const InlinedVector& other) { - if (ABSL_PREDICT_FALSE(this == &other)) return *this; - - // Optimized to avoid reallocation. - // Prefer reassignment to copy construction for elements. - if (size() < other.size()) { // grow - reserve(other.size()); - std::copy(other.begin(), other.begin() + size(), begin()); - std::copy(other.begin() + size(), other.end(), std::back_inserter(*this)); - } else { // maybe shrink - erase(begin() + other.size(), end()); - std::copy(other.begin(), other.end(), begin()); + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + const_pointer other_data = other.data(); + assign(other_data, other_data + other.size()); } + return *this; } - // Overload of `InlinedVector::operator=()` to replace the contents of the - // inlined vector with the contents of `other`. + // Overload of `InlinedVector::operator=(...)` that moves the elements of + // `other` into the inlined vector. // - // NOTE: As a result of calling this overload, `other` may be empty or it's - // contents may be left in a moved-from state. + // NOTE: as a result of calling this overload, `other` is left in a valid but + // unspecified state. InlinedVector& operator=(InlinedVector&& other) { - if (ABSL_PREDICT_FALSE(this == &other)) return *this; - - if (other.allocated()) { - clear(); - tag().set_allocated_size(other.size()); - init_allocation(other.allocation()); - other.tag() = Tag(); - } else { - if (allocated()) clear(); - // Both are inlined now. - if (size() < other.size()) { - auto mid = std::make_move_iterator(other.begin() + size()); - std::copy(std::make_move_iterator(other.begin()), mid, begin()); - UninitializedCopy(mid, std::make_move_iterator(other.end()), end()); + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) { + inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), + size()); + storage_.DeallocateIfAllocated(); + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); } else { - auto new_end = std::copy(std::make_move_iterator(other.begin()), - std::make_move_iterator(other.end()), begin()); - Destroy(new_end, end()); + storage_.Assign(IteratorValueAdapter( + MoveIterator(other.storage_.GetInlinedData())), + other.size()); } - tag().set_inline_size(other.size()); } + return *this; } - // `InlinedVector::assign()` + // `InlinedVector::assign(...)` // // Replaces the contents of the inlined vector with `n` copies of `v`. void assign(size_type n, const_reference v) { - if (n <= size()) { // Possibly shrink - std::fill_n(begin(), n, v); - erase(begin() + n, end()); - return; - } - // Grow - reserve(n); - std::fill_n(begin(), size(), v); - if (allocated()) { - UninitializedFill(allocated_space() + size(), allocated_space() + n, v); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space() + size(), inlined_space() + n, v); - tag().set_inline_size(n); - } + storage_.Assign(CopyValueAdapter(v), n); + } + + // Overload of `InlinedVector::assign(...)` that replaces the contents of the + // inlined vector with copies of the elements of `list`. + void assign(std::initializer_list list) { + assign(list.begin(), list.end()); } - // Overload of `InlinedVector::assign()` to replace the contents of the - // inlined vector with copies of the values in the provided - // `std::initializer_list`. - void assign(std::initializer_list init_list) { - AssignRange(init_list.begin(), init_list.end(), - IteratorCategory{}); + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "forward" category or better. + template * = nullptr> + void assign(ForwardIterator first, ForwardIterator last) { + storage_.Assign(IteratorValueAdapter(first), + std::distance(first, last)); } - // Overload of `InlinedVector::assign()` to replace the contents of the - // inlined vector with values constructed from the range [`first`, `last`). - template * = nullptr> + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "input" category. + template * = nullptr> void assign(InputIterator first, InputIterator last) { - AssignRange(first, last, IteratorCategory{}); + size_type i = 0; + for (; i < size() && first != last; ++i, static_cast(++first)) { + at(i) = *first; + } + + erase(data() + i, data() + size()); + + std::copy(first, last, std::back_inserter(*this)); } - // `InlinedVector::resize()` + // `InlinedVector::resize(...)` + // + // Resizes the inlined vector to contain `n` elements. // - // Resizes the inlined vector to contain `n` elements. If `n` is smaller than - // the inlined vector's current size, extra elements are destroyed. If `n` is - // larger than the initial size, new elements are value-initialized. - void resize(size_type n); + // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are value-initialized. + void resize(size_type n) { storage_.Resize(DefaultValueAdapter(), n); } - // Overload of `InlinedVector::resize()` to resize the inlined vector to - // contain `n` elements where, if `n` is larger than `size()`, the new values - // will be copy-constructed from `v`. - void resize(size_type n, const_reference v); + // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to + // contain `n` elements. + // + // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are copied-constructed from `v`. + void resize(size_type n, const_reference v) { + storage_.Resize(CopyValueAdapter(v), n); + } - // `InlinedVector::insert()` + // `InlinedVector::insert(...)` // - // Copies `v` into `position`, returning an `iterator` pointing to the newly + // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly // inserted element. - iterator insert(const_iterator position, const_reference v) { - return emplace(position, v); + iterator insert(const_iterator pos, const_reference v) { + return emplace(pos, v); } - // Overload of `InlinedVector::insert()` for moving `v` into `position`, - // returning an iterator pointing to the newly inserted element. - iterator insert(const_iterator position, rvalue_reference v) { - return emplace(position, std::move(v)); + // 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) { + return emplace(pos, std::move(v)); } - // Overload of `InlinedVector::insert()` for inserting `n` contiguous copies - // of `v` starting at `position`. Returns an `iterator` pointing to the first - // of the newly inserted elements. - iterator insert(const_iterator position, size_type n, const_reference v) { - return InsertWithCount(position, n, v); + // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies + // of `v` starting at `pos`, returning an `iterator` pointing to the first of + // the newly inserted elements. + iterator insert(const_iterator pos, size_type n, const_reference v) { + assert(pos >= begin()); + assert(pos <= end()); + + if (ABSL_PREDICT_TRUE(n != 0)) { + value_type dealias = v; + return storage_.Insert(pos, CopyValueAdapter(dealias), n); + } else { + return const_cast(pos); + } } - // Overload of `InlinedVector::insert()` for copying the contents of the - // `std::initializer_list` into the vector starting at `position`. Returns an - // `iterator` pointing to the first of the newly inserted elements. - iterator insert(const_iterator position, - std::initializer_list init_list) { - return insert(position, init_list.begin(), init_list.end()); + // Overload of `InlinedVector::insert(...)` that inserts copies of the + // elements of `list` starting at `pos`, returning an `iterator` pointing to + // the first of the newly inserted elements. + iterator insert(const_iterator pos, std::initializer_list list) { + return insert(pos, list.begin(), list.end()); + } + + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. + // + // NOTE: this overload is for iterators that are "forward" category or better. + template * = nullptr> + iterator insert(const_iterator pos, ForwardIterator first, + ForwardIterator last) { + assert(pos >= begin()); + assert(pos <= end()); + + if (ABSL_PREDICT_TRUE(first != last)) { + return storage_.Insert(pos, IteratorValueAdapter(first), + std::distance(first, last)); + } else { + return const_cast(pos); + } } - // Overload of `InlinedVector::insert()` for inserting elements constructed - // from the range [`first`, `last`). Returns an `iterator` pointing to the - // first of the newly inserted elements. + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. // - // NOTE: The `enable_if` is intended to disambiguate the two three-argument - // overloads of `insert()`. + // NOTE: this overload is for iterators that are "input" category. template > - iterator insert(const_iterator position, InputIterator first, - InputIterator last) { - return InsertWithRange(position, first, last, - IteratorCategory()); + DisableIfAtLeastForwardIterator* = nullptr> + iterator insert(const_iterator pos, InputIterator first, InputIterator last) { + assert(pos >= begin()); + assert(pos <= end()); + + size_type index = std::distance(cbegin(), pos); + for (size_type i = index; first != last; ++i, static_cast(++first)) { + insert(data() + i, *first); + } + + return iterator(data() + index); } - // `InlinedVector::emplace()` + // `InlinedVector::emplace(...)` // - // Constructs and inserts an object in the inlined vector at the given - // `position`, returning an `iterator` pointing to the newly emplaced element. + // Constructs and inserts an element using `args...` in the inlined vector at + // `pos`, returning an `iterator` pointing to the newly emplaced element. template - iterator emplace(const_iterator position, Args&&... args); + iterator emplace(const_iterator pos, Args&&... args) { + assert(pos >= begin()); + assert(pos <= end()); - // `InlinedVector::emplace_back()` + value_type dealias(std::forward(args)...); + return storage_.Insert(pos, + IteratorValueAdapter( + MoveIterator(std::addressof(dealias))), + 1); + } + + // `InlinedVector::emplace_back(...)` // - // Constructs and appends a new element to the end of the inlined vector, - // returning a `reference` to the emplaced element. + // Constructs and inserts an element using `args...` in the inlined vector at + // `end()`, returning a `reference` to the newly emplaced element. template reference emplace_back(Args&&... args) { - size_type s = size(); - assert(s <= capacity()); - if (ABSL_PREDICT_FALSE(s == capacity())) { - return GrowAndEmplaceBack(std::forward(args)...); - } - assert(s < capacity()); - - pointer space; - if (allocated()) { - tag().set_allocated_size(s + 1); - space = allocated_space(); - } else { - tag().set_inline_size(s + 1); - space = inlined_space(); - } - return Construct(space + s, std::forward(args)...); + return storage_.EmplaceBack(std::forward(args)...); } - // `InlinedVector::push_back()` + // `InlinedVector::push_back(...)` // - // Appends a copy of `v` to the end of the inlined vector. + // Inserts a copy of `v` in the inlined vector at `end()`. void push_back(const_reference v) { static_cast(emplace_back(v)); } - // Overload of `InlinedVector::push_back()` for moving `v` into a newly - // appended element. + // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` + // using move semantics. void push_back(rvalue_reference v) { static_cast(emplace_back(std::move(v))); } // `InlinedVector::pop_back()` // - // Destroys the element at the end of the inlined vector and shrinks the size - // by `1` (unless the inlined vector is empty, in which case this is a no-op). + // Destroys the element at `back()`, reducing the size by `1`. void pop_back() noexcept { assert(!empty()); - size_type s = size(); - if (allocated()) { - Destroy(allocated_space() + s - 1, allocated_space() + s); - tag().set_allocated_size(s - 1); - } else { - Destroy(inlined_space() + s - 1, inlined_space() + s); - tag().set_inline_size(s - 1); - } + + AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1)); + storage_.SubtractSize(1); } - // `InlinedVector::erase()` + // `InlinedVector::erase(...)` // - // Erases the element at `position` of the inlined vector, returning an - // `iterator` pointing to the first element following the erased element. + // Erases the element at `pos`, returning an `iterator` pointing to where the + // erased element was located. // - // NOTE: May return the end iterator, which is not dereferencable. - iterator erase(const_iterator position) { - assert(position >= begin()); - assert(position < end()); - - iterator pos = const_cast(position); - std::move(pos + 1, end(), pos); - pop_back(); - return pos; + // NOTE: may return `end()`, which is not dereferencable. + iterator erase(const_iterator pos) { + assert(pos >= begin()); + assert(pos < end()); + + return storage_.Erase(pos, pos + 1); } - // Overload of `InlinedVector::erase()` for erasing all elements in the - // range [`from`, `to`) in the inlined vector. Returns an `iterator` pointing - // to the first element following the range erased or the end iterator if `to` - // was the end iterator. - iterator erase(const_iterator from, const_iterator to); + // Overload of `InlinedVector::erase(...)` that erases every element in the + // range [`from`, `to`), returning an `iterator` pointing to where the first + // erased element was located. + // + // NOTE: may return `end()`, which is not dereferencable. + iterator erase(const_iterator from, const_iterator to) { + assert(from >= begin()); + assert(from <= to); + assert(to <= end()); + + if (ABSL_PREDICT_TRUE(from != to)) { + return storage_.Erase(from, to); + } else { + return const_cast(from); + } + } // `InlinedVector::clear()` // - // Destroys all elements in the inlined vector, sets the size of `0` and - // deallocates the heap allocation if the inlined vector was allocated. + // Destroys all elements in the inlined vector, setting the size to `0` and + // deallocating any held memory. void clear() noexcept { - size_type s = size(); - if (allocated()) { - Destroy(allocated_space(), allocated_space() + s); - allocation().Dealloc(allocator()); - } else if (s != 0) { // do nothing for empty vectors - Destroy(inlined_space(), inlined_space() + s); - } - tag() = Tag(); + inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), + size()); + storage_.DeallocateIfAllocated(); + storage_.SetInlinedSize(0); } - // `InlinedVector::reserve()` + // `InlinedVector::reserve(...)` // - // Enlarges the underlying representation of the inlined vector so it can hold - // at least `n` elements. This method does not change `size()` or the actual - // contents of the vector. - // - // NOTE: If `n` does not exceed `capacity()`, `reserve()` will have no - // effects. Otherwise, `reserve()` will reallocate, performing an n-time - // element-wise move of everything contained. - void reserve(size_type n) { - if (n > capacity()) { - // Make room for new elements - EnlargeBy(n - size()); - } - } + // Ensures that there is enough room for at least `n` elements. + void reserve(size_type n) { storage_.Reserve(n); } // `InlinedVector::shrink_to_fit()` // - // Reduces memory usage by freeing unused memory. After this call, calls to - // `capacity()` will be equal to `(std::max)(inlined_capacity(), size())`. + // Reduces memory usage by freeing unused memory. After being called, calls to + // `capacity()` will be equal to `max(N, size())`. // - // If `size() <= inlined_capacity()` and the elements are currently stored on - // the heap, they will be moved to the inlined storage and the heap memory + // If `size() <= N` and the inlined vector contains allocated memory, the + // elements will all be moved to the inlined space and the allocated memory // will be deallocated. // - // If `size() > inlined_capacity()` and `size() < capacity()` the elements - // will be moved to a smaller heap allocation. + // If `size() > N` and `size() < capacity()`, the elements will be moved to a + // smaller allocation. void shrink_to_fit() { - const auto s = size(); - if (ABSL_PREDICT_FALSE(!allocated() || s == capacity())) return; - - if (s <= inlined_capacity()) { - // Move the elements to the inlined storage. - // We have to do this using a temporary, because `inlined_storage` and - // `allocation_storage` are in a union field. - auto temp = std::move(*this); - assign(std::make_move_iterator(temp.begin()), - std::make_move_iterator(temp.end())); - return; + if (storage_.GetIsAllocated()) { + storage_.ShrinkToFit(); } - - // Reallocate storage and move elements. - // We can't simply use the same approach as above, because `assign()` would - // call into `reserve()` internally and reserve larger capacity than we need - Allocation new_allocation(allocator(), s); - UninitializedCopy(std::make_move_iterator(allocated_space()), - std::make_move_iterator(allocated_space() + s), - new_allocation.buffer()); - ResetAllocation(new_allocation, s); } - // `InlinedVector::swap()` + // `InlinedVector::swap(...)` // - // Swaps the contents of this inlined vector with the contents of `other`. - void swap(InlinedVector& other); - - template - friend Hash AbslHashValue(Hash hash, const InlinedVector& inlined_vector) { - const_pointer p = inlined_vector.data(); - size_type n = inlined_vector.size(); - return Hash::combine(Hash::combine_contiguous(std::move(hash), p, n), n); - } - - private: - // Holds whether the vector is allocated or not in the lowest bit and the size - // in the high bits: - // `size_ = (size << 1) | is_allocated;` - class Tag { - public: - Tag() : size_(0) {} - size_type size() const { return size_ / 2; } - void add_size(size_type n) { size_ += n * 2; } - void set_inline_size(size_type n) { size_ = n * 2; } - void set_allocated_size(size_type n) { size_ = (n * 2) + 1; } - bool allocated() const { return size_ % 2; } - - private: - size_type size_; - }; - - // Derives from `allocator_type` to use the empty base class optimization. - // If the `allocator_type` is stateless, we can store our instance for free. - class AllocatorAndTag : private allocator_type { - public: - explicit AllocatorAndTag(const allocator_type& a) : allocator_type(a) {} - - Tag& tag() { return tag_; } - const Tag& tag() const { return tag_; } - - allocator_type& allocator() { return *this; } - const allocator_type& allocator() const { return *this; } - - private: - Tag tag_; - }; - - class Allocation { - public: - Allocation(allocator_type& a, size_type capacity) - : capacity_(capacity), buffer_(Create(a, capacity)) {} - - void Dealloc(allocator_type& a) { - std::allocator_traits::deallocate(a, buffer_, capacity_); - } - - size_type capacity() const { return capacity_; } - - const_pointer buffer() const { return buffer_; } - - pointer buffer() { return buffer_; } - - private: - static pointer Create(allocator_type& a, size_type n) { - return std::allocator_traits::allocate(a, n); + // Swaps the contents of the inlined vector with `other`. + void swap(InlinedVector& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + storage_.Swap(std::addressof(other.storage_)); } - - size_type capacity_; - pointer buffer_; - }; - - const Tag& tag() const { return allocator_and_tag_.tag(); } - - Tag& tag() { return allocator_and_tag_.tag(); } - - Allocation& allocation() { - return reinterpret_cast(rep_.allocation_storage.allocation); - } - - const Allocation& allocation() const { - return reinterpret_cast( - rep_.allocation_storage.allocation); - } - - void init_allocation(const Allocation& allocation) { - new (&rep_.allocation_storage.allocation) Allocation(allocation); - } - - // TODO(absl-team): investigate whether the reinterpret_cast is appropriate. - pointer inlined_space() { - return reinterpret_cast( - std::addressof(rep_.inlined_storage.inlined[0])); - } - - const_pointer inlined_space() const { - return reinterpret_cast( - std::addressof(rep_.inlined_storage.inlined[0])); - } - - pointer allocated_space() { return allocation().buffer(); } - - const_pointer allocated_space() const { return allocation().buffer(); } - - const allocator_type& allocator() const { - return allocator_and_tag_.allocator(); } - allocator_type& allocator() { return allocator_and_tag_.allocator(); } - - bool allocated() const { return tag().allocated(); } - - // Enlarge the underlying representation so we can store `size_ + delta` elems - // in allocated space. The size is not changed, and any newly added memory is - // not initialized. - void EnlargeBy(size_type delta); - - // Shift all elements from `position` to `end()` by `n` places to the right. - // If the vector needs to be enlarged, memory will be allocated. - // Returns `iterator`s pointing to the start of the previously-initialized - // portion and the start of the uninitialized portion of the created gap. - // The number of initialized spots is `pair.second - pair.first`. The number - // of raw spots is `n - (pair.second - pair.first)`. - // - // Updates the size of the InlinedVector internally. - std::pair ShiftRight(const_iterator position, - size_type n); - - void ResetAllocation(Allocation new_allocation, size_type new_size) { - if (allocated()) { - Destroy(allocated_space(), allocated_space() + size()); - assert(begin() == allocated_space()); - allocation().Dealloc(allocator()); - allocation() = new_allocation; - } else { - Destroy(inlined_space(), inlined_space() + size()); - init_allocation(new_allocation); // bug: only init once - } - tag().set_allocated_size(new_size); - } - - template - reference GrowAndEmplaceBack(Args&&... args) { - assert(size() == capacity()); - const size_type s = size(); - - Allocation new_allocation(allocator(), 2 * capacity()); - - reference new_element = - Construct(new_allocation.buffer() + s, std::forward(args)...); - UninitializedCopy(std::make_move_iterator(data()), - std::make_move_iterator(data() + s), - new_allocation.buffer()); - - ResetAllocation(new_allocation, s + 1); - - return new_element; - } - - void InitAssign(size_type n); - - void InitAssign(size_type n, const_reference v); - - template - reference Construct(pointer p, Args&&... args) { - std::allocator_traits::construct( - allocator(), p, std::forward(args)...); - return *p; - } - - template - void UninitializedCopy(Iterator src, Iterator src_last, pointer dst) { - for (; src != src_last; ++dst, ++src) Construct(dst, *src); - } - - template - void UninitializedFill(pointer dst, pointer dst_last, const Args&... args) { - for (; dst != dst_last; ++dst) Construct(dst, args...); - } - - // Destroy [`from`, `to`) in place. - void Destroy(pointer from, pointer to); - - template - void AppendRange(Iterator first, Iterator last, std::forward_iterator_tag); - - template - void AppendRange(Iterator first, Iterator last, std::input_iterator_tag); - - template - void AssignRange(Iterator first, Iterator last, std::forward_iterator_tag); + private: + template + friend H AbslHashValue(H h, const absl::InlinedVector& a); - template - void AssignRange(Iterator first, Iterator last, std::input_iterator_tag); - - iterator InsertWithCount(const_iterator position, size_type n, - const_reference v); - - template - iterator InsertWithRange(const_iterator position, ForwardIterator first, - ForwardIterator last, std::forward_iterator_tag); - - template - iterator InsertWithRange(const_iterator position, InputIterator first, - InputIterator last, std::input_iterator_tag); - - // Stores either the inlined or allocated representation - union Rep { - using ValueTypeBuffer = - absl::aligned_storage_t; - using AllocationBuffer = - absl::aligned_storage_t; - - // Structs wrap the buffers to perform indirection that solves a bizarre - // compilation error on Visual Studio (all known versions). - struct InlinedRep { - ValueTypeBuffer inlined[N]; - }; - struct AllocatedRep { - AllocationBuffer allocation; - }; - - InlinedRep inlined_storage; - AllocatedRep allocation_storage; - }; - - AllocatorAndTag allocator_and_tag_; - Rep rep_; + Storage storage_; }; // ----------------------------------------------------------------------------- // InlinedVector Non-Member Functions // ----------------------------------------------------------------------------- -// `swap()` +// `swap(...)` // -// Swaps the contents of two inlined vectors. This convenience function -// simply calls `InlinedVector::swap()`. +// Swaps the contents of two inlined vectors. template -void swap(InlinedVector& a, - InlinedVector& b) noexcept(noexcept(a.swap(b))) { +void swap(absl::InlinedVector& a, + absl::InlinedVector& b) noexcept(noexcept(a.swap(b))) { a.swap(b); } -// `operator==()` +// `operator==(...)` // -// Tests the equivalency of the contents of two inlined vectors. +// Tests for value-equality of two inlined vectors. template -bool operator==(const InlinedVector& a, - const InlinedVector& b) { - return absl::equal(a.begin(), a.end(), b.begin(), b.end()); +bool operator==(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return absl::equal(a_data, a_data + a.size(), b_data, b_data + b.size()); } -// `operator!=()` +// `operator!=(...)` // -// Tests the inequality of the contents of two inlined vectors. +// Tests for value-inequality of two inlined vectors. template -bool operator!=(const InlinedVector& a, - const InlinedVector& b) { +bool operator!=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { return !(a == b); } -// `operator<()` +// `operator<(...)` // -// Tests whether the contents of one inlined vector are less than the contents -// of another through a lexicographical comparison operation. +// Tests whether the value of an inlined vector is less than the value of +// another inlined vector using a lexicographical comparison algorithm. template -bool operator<(const InlinedVector& a, - const InlinedVector& b) { - return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); +bool operator<(const absl::InlinedVector& a, + const absl::InlinedVector& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return std::lexicographical_compare(a_data, a_data + a.size(), b_data, + b_data + b.size()); } -// `operator>()` +// `operator>(...)` // -// Tests whether the contents of one inlined vector are greater than the -// contents of another through a lexicographical comparison operation. +// Tests whether the value of an inlined vector is greater than the value of +// another inlined vector using a lexicographical comparison algorithm. template -bool operator>(const InlinedVector& a, - const InlinedVector& b) { +bool operator>(const absl::InlinedVector& a, + const absl::InlinedVector& b) { return b < a; } -// `operator<=()` +// `operator<=(...)` // -// Tests whether the contents of one inlined vector are less than or equal to -// the contents of another through a lexicographical comparison operation. +// Tests whether the value of an inlined vector is less than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. template -bool operator<=(const InlinedVector& a, - const InlinedVector& b) { +bool operator<=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { return !(b < a); } -// `operator>=()` +// `operator>=(...)` // -// Tests whether the contents of one inlined vector are greater than or equal to -// the contents of another through a lexicographical comparison operation. +// Tests whether the value of an inlined vector is greater than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. template -bool operator>=(const InlinedVector& a, - const InlinedVector& b) { +bool operator>=(const absl::InlinedVector& a, + const absl::InlinedVector& b) { return !(a < b); } -// ----------------------------------------------------------------------------- -// Implementation of InlinedVector +// `AbslHashValue(...)` // -// Do not depend on any below implementation details! -// ----------------------------------------------------------------------------- - -template -InlinedVector::InlinedVector(const InlinedVector& other) - : allocator_and_tag_(other.allocator()) { - reserve(other.size()); - if (allocated()) { - UninitializedCopy(other.begin(), other.end(), allocated_space()); - tag().set_allocated_size(other.size()); - } else { - UninitializedCopy(other.begin(), other.end(), inlined_space()); - tag().set_inline_size(other.size()); - } -} - -template -InlinedVector::InlinedVector(const InlinedVector& other, - const allocator_type& alloc) - : allocator_and_tag_(alloc) { - reserve(other.size()); - if (allocated()) { - UninitializedCopy(other.begin(), other.end(), allocated_space()); - tag().set_allocated_size(other.size()); - } else { - UninitializedCopy(other.begin(), other.end(), inlined_space()); - tag().set_inline_size(other.size()); - } -} - -template -InlinedVector::InlinedVector(InlinedVector&& other) noexcept( - absl::allocator_is_nothrow::value || - std::is_nothrow_move_constructible::value) - : allocator_and_tag_(other.allocator_and_tag_) { - if (other.allocated()) { - // We can just steal the underlying buffer from the source. - // That leaves the source empty, so we clear its size. - init_allocation(other.allocation()); - other.tag() = Tag(); - } else { - UninitializedCopy( - std::make_move_iterator(other.inlined_space()), - std::make_move_iterator(other.inlined_space() + other.size()), - inlined_space()); - } -} - -template -InlinedVector::InlinedVector(InlinedVector&& other, - const allocator_type& alloc) noexcept( // - absl::allocator_is_nothrow::value) - : allocator_and_tag_(alloc) { - if (other.allocated()) { - if (alloc == other.allocator()) { - // We can just steal the allocation from the source. - tag() = other.tag(); - init_allocation(other.allocation()); - other.tag() = Tag(); - } else { - // We need to use our own allocator - reserve(other.size()); - UninitializedCopy(std::make_move_iterator(other.begin()), - std::make_move_iterator(other.end()), - allocated_space()); - tag().set_allocated_size(other.size()); - } - } else { - UninitializedCopy( - std::make_move_iterator(other.inlined_space()), - std::make_move_iterator(other.inlined_space() + other.size()), - inlined_space()); - tag().set_inline_size(other.size()); - } -} - -template -void InlinedVector::InitAssign(size_type n, const_reference v) { - if (n > inlined_capacity()) { - Allocation new_allocation(allocator(), n); - init_allocation(new_allocation); - UninitializedFill(allocated_space(), allocated_space() + n, v); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space(), inlined_space() + n, v); - tag().set_inline_size(n); - } -} - -template -void InlinedVector::InitAssign(size_type n) { - if (n > inlined_capacity()) { - Allocation new_allocation(allocator(), n); - init_allocation(new_allocation); - UninitializedFill(allocated_space(), allocated_space() + n); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space(), inlined_space() + n); - tag().set_inline_size(n); - } -} - -template -void InlinedVector::resize(size_type n) { - size_type s = size(); - if (n < s) { - erase(begin() + n, end()); - return; - } - reserve(n); - assert(capacity() >= n); - - // Fill new space with elements constructed in-place. - if (allocated()) { - UninitializedFill(allocated_space() + s, allocated_space() + n); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space() + s, inlined_space() + n); - tag().set_inline_size(n); - } -} - -template -void InlinedVector::resize(size_type n, const_reference v) { - size_type s = size(); - if (n < s) { - erase(begin() + n, end()); - return; - } - reserve(n); - assert(capacity() >= n); - - // Fill new space with copies of 'v'. - if (allocated()) { - UninitializedFill(allocated_space() + s, allocated_space() + n, v); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space() + s, inlined_space() + n, v); - tag().set_inline_size(n); - } -} - -template -template -auto InlinedVector::emplace(const_iterator position, Args&&... args) - -> iterator { - assert(position >= begin()); - assert(position <= end()); - if (ABSL_PREDICT_FALSE(position == end())) { - emplace_back(std::forward(args)...); - return end() - 1; - } - - T new_t = T(std::forward(args)...); - - auto range = ShiftRight(position, 1); - if (range.first == range.second) { - // constructing into uninitialized memory - Construct(range.first, std::move(new_t)); - } else { - // assigning into moved-from object - *range.first = T(std::move(new_t)); - } - - return range.first; -} - -template -auto InlinedVector::erase(const_iterator from, const_iterator to) - -> iterator { - assert(begin() <= from); - assert(from <= to); - assert(to <= end()); - - iterator range_start = const_cast(from); - iterator range_end = const_cast(to); - - size_type s = size(); - ptrdiff_t erase_gap = std::distance(range_start, range_end); - if (erase_gap > 0) { - pointer space; - if (allocated()) { - space = allocated_space(); - tag().set_allocated_size(s - erase_gap); - } else { - space = inlined_space(); - tag().set_inline_size(s - erase_gap); - } - std::move(range_end, space + s, range_start); - Destroy(space + s - erase_gap, space + s); - } - return range_start; -} - -template -void InlinedVector::swap(InlinedVector& other) { - using std::swap; // Augment ADL with `std::swap`. - if (ABSL_PREDICT_FALSE(this == &other)) return; - - if (allocated() && other.allocated()) { - // Both out of line, so just swap the tag, allocation, and allocator. - swap(tag(), other.tag()); - swap(allocation(), other.allocation()); - swap(allocator(), other.allocator()); - return; - } - if (!allocated() && !other.allocated()) { - // Both inlined: swap up to smaller size, then move remaining elements. - InlinedVector* a = this; - InlinedVector* b = &other; - if (size() < other.size()) { - swap(a, b); - } - - const size_type a_size = a->size(); - const size_type b_size = b->size(); - assert(a_size >= b_size); - // `a` is larger. Swap the elements up to the smaller array size. - std::swap_ranges(a->inlined_space(), a->inlined_space() + b_size, - b->inlined_space()); - - // Move the remaining elements: - // [`b_size`, `a_size`) from `a` -> [`b_size`, `a_size`) from `b` - b->UninitializedCopy(a->inlined_space() + b_size, - a->inlined_space() + a_size, - b->inlined_space() + b_size); - a->Destroy(a->inlined_space() + b_size, a->inlined_space() + a_size); - - swap(a->tag(), b->tag()); - swap(a->allocator(), b->allocator()); - assert(b->size() == a_size); - assert(a->size() == b_size); - return; - } - - // One is out of line, one is inline. - // We first move the elements from the inlined vector into the - // inlined space in the other vector. We then put the other vector's - // pointer/capacity into the originally inlined vector and swap - // the tags. - InlinedVector* a = this; - InlinedVector* b = &other; - if (a->allocated()) { - swap(a, b); - } - assert(!a->allocated()); - assert(b->allocated()); - const size_type a_size = a->size(); - const size_type b_size = b->size(); - // In an optimized build, `b_size` would be unused. - static_cast(b_size); - - // Made Local copies of `size()`, don't need `tag()` accurate anymore - swap(a->tag(), b->tag()); - - // Copy `b_allocation` out before `b`'s union gets clobbered by `inline_space` - Allocation b_allocation = b->allocation(); - - b->UninitializedCopy(a->inlined_space(), a->inlined_space() + a_size, - b->inlined_space()); - a->Destroy(a->inlined_space(), a->inlined_space() + a_size); - - a->allocation() = b_allocation; - - if (a->allocator() != b->allocator()) { - swap(a->allocator(), b->allocator()); - } - - assert(b->size() == a_size); - assert(a->size() == b_size); -} - -template -void InlinedVector::EnlargeBy(size_type delta) { - const size_type s = size(); - assert(s <= capacity()); - - size_type target = std::max(inlined_capacity(), s + delta); - - // Compute new capacity by repeatedly doubling current capacity - // TODO(psrc): Check and avoid overflow? - size_type new_capacity = capacity(); - while (new_capacity < target) { - new_capacity <<= 1; - } - - Allocation new_allocation(allocator(), new_capacity); - - UninitializedCopy(std::make_move_iterator(data()), - std::make_move_iterator(data() + s), - new_allocation.buffer()); - - ResetAllocation(new_allocation, s); -} - -template -auto InlinedVector::ShiftRight(const_iterator position, size_type n) - -> std::pair { - iterator start_used = const_cast(position); - iterator start_raw = const_cast(position); - size_type s = size(); - size_type required_size = s + n; - - if (required_size > capacity()) { - // Compute new capacity by repeatedly doubling current capacity - size_type new_capacity = capacity(); - while (new_capacity < required_size) { - new_capacity <<= 1; - } - // Move everyone into the new allocation, leaving a gap of `n` for the - // requested shift. - Allocation new_allocation(allocator(), new_capacity); - size_type index = position - begin(); - UninitializedCopy(std::make_move_iterator(data()), - std::make_move_iterator(data() + index), - new_allocation.buffer()); - UninitializedCopy(std::make_move_iterator(data() + index), - std::make_move_iterator(data() + s), - new_allocation.buffer() + index + n); - ResetAllocation(new_allocation, s); - - // New allocation means our iterator is invalid, so we'll recalculate. - // Since the entire gap is in new space, there's no used space to reuse. - start_raw = begin() + index; - start_used = start_raw; - } else { - // If we had enough space, it's a two-part move. Elements going into - // previously-unoccupied space need an `UninitializedCopy()`. Elements - // going into a previously-occupied space are just a `std::move()`. - iterator pos = const_cast(position); - iterator raw_space = end(); - size_type slots_in_used_space = raw_space - pos; - size_type new_elements_in_used_space = std::min(n, slots_in_used_space); - size_type new_elements_in_raw_space = n - new_elements_in_used_space; - size_type old_elements_in_used_space = - slots_in_used_space - new_elements_in_used_space; - - UninitializedCopy(std::make_move_iterator(pos + old_elements_in_used_space), - std::make_move_iterator(raw_space), - raw_space + new_elements_in_raw_space); - std::move_backward(pos, pos + old_elements_in_used_space, raw_space); - - // If the gap is entirely in raw space, the used space starts where the raw - // space starts, leaving no elements in used space. If the gap is entirely - // in used space, the raw space starts at the end of the gap, leaving all - // elements accounted for within the used space. - start_used = pos; - start_raw = pos + new_elements_in_used_space; - } - tag().add_size(n); - return std::make_pair(start_used, start_raw); -} - -template -void InlinedVector::Destroy(pointer from, pointer to) { - for (pointer cur = from; cur != to; ++cur) { - std::allocator_traits::destroy(allocator(), cur); - } -#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. - if (from != to) { - auto len = sizeof(value_type) * std::distance(from, to); - std::memset(reinterpret_cast(from), 0xab, len); - } -#endif -} - -template -template -void InlinedVector::AppendRange(Iterator first, Iterator last, - std::forward_iterator_tag) { - auto length = std::distance(first, last); - reserve(size() + length); - if (allocated()) { - UninitializedCopy(first, last, allocated_space() + size()); - tag().set_allocated_size(size() + length); - } else { - UninitializedCopy(first, last, inlined_space() + size()); - tag().set_inline_size(size() + length); - } -} - -template -template -void InlinedVector::AppendRange(Iterator first, Iterator last, - std::input_iterator_tag) { - std::copy(first, last, std::back_inserter(*this)); -} - -template -template -void InlinedVector::AssignRange(Iterator first, Iterator last, - std::forward_iterator_tag) { - auto length = std::distance(first, last); - // Prefer reassignment to copy construction for elements. - if (static_cast(length) <= size()) { - erase(std::copy(first, last, begin()), end()); - return; - } - reserve(length); - iterator out = begin(); - for (; out != end(); ++first, ++out) *out = *first; - if (allocated()) { - UninitializedCopy(first, last, out); - tag().set_allocated_size(length); - } else { - UninitializedCopy(first, last, out); - tag().set_inline_size(length); - } -} - -template -template -void InlinedVector::AssignRange(Iterator first, Iterator last, - std::input_iterator_tag) { - // Optimized to avoid reallocation. - // Prefer reassignment to copy construction for elements. - iterator out = begin(); - for (; first != last && out != end(); ++first, ++out) { - *out = *first; - } - erase(out, end()); - std::copy(first, last, std::back_inserter(*this)); -} - -template -auto InlinedVector::InsertWithCount(const_iterator position, - size_type n, const_reference v) - -> iterator { - assert(position >= begin() && position <= end()); - if (ABSL_PREDICT_FALSE(n == 0)) return const_cast(position); - - value_type copy = v; - std::pair it_pair = ShiftRight(position, n); - std::fill(it_pair.first, it_pair.second, copy); - UninitializedFill(it_pair.second, it_pair.first + n, copy); - - return it_pair.first; -} - -template -template -auto InlinedVector::InsertWithRange(const_iterator position, - ForwardIterator first, - ForwardIterator last, - std::forward_iterator_tag) - -> iterator { - assert(position >= begin() && position <= end()); - if (ABSL_PREDICT_FALSE(first == last)) return const_cast(position); - - auto n = std::distance(first, last); - std::pair it_pair = ShiftRight(position, n); - size_type used_spots = it_pair.second - it_pair.first; - ForwardIterator open_spot = std::next(first, used_spots); - std::copy(first, open_spot, it_pair.first); - UninitializedCopy(open_spot, last, it_pair.second); - return it_pair.first; -} - -template -template -auto InlinedVector::InsertWithRange(const_iterator position, - InputIterator first, - InputIterator last, - std::input_iterator_tag) - -> iterator { - assert(position >= begin() && position <= end()); - size_type index = position - cbegin(); - size_type i = index; - while (first != last) insert(begin() + i++, *first++); - return begin() + index; +// Provides `absl::Hash` support for `absl::InlinedVector`. It is uncommon to +// call this directly. +template +H AbslHashValue(H h, const absl::InlinedVector& a) { + auto size = a.size(); + return H::combine(H::combine_contiguous(std::move(h), a.data(), size), size); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 a3ad0f8a..b99bbd62 100644 --- a/absl/container/inlined_vector_benchmark.cc +++ b/absl/container/inlined_vector_benchmark.cc @@ -1,10 +1,10 @@ -// 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. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,28 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/container/inlined_vector.h" - +#include #include #include #include "benchmark/benchmark.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/container/inlined_vector.h" #include "absl/strings/str_cat.h" namespace { -using IntVec = absl::InlinedVector; - void BM_InlinedVectorFill(benchmark::State& state) { - const int len = state.range(0); + absl::InlinedVector v; + int val = 10; for (auto _ : state) { - IntVec v; - for (int i = 0; i < len; i++) { - v.push_back(i); - } + benchmark::DoNotOptimize(v); + v.push_back(val); } - state.SetItemsProcessed(static_cast(state.iterations()) * len); } BENCHMARK(BM_InlinedVectorFill)->Range(0, 1024); @@ -43,23 +40,25 @@ void BM_InlinedVectorFillRange(benchmark::State& state) { for (int i = 0; i < len; i++) { ia[i] = i; } + auto* from = ia.get(); + auto* to = from + len; for (auto _ : state) { - IntVec v(ia.get(), ia.get() + len); + benchmark::DoNotOptimize(from); + benchmark::DoNotOptimize(to); + absl::InlinedVector v(from, to); benchmark::DoNotOptimize(v); } - state.SetItemsProcessed(static_cast(state.iterations()) * len); } BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024); void BM_StdVectorFill(benchmark::State& state) { - const int len = state.range(0); + std::vector v; + int val = 10; for (auto _ : state) { - std::vector v; - for (int i = 0; i < len; i++) { - v.push_back(i); - } + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(val); + v.push_back(val); } - state.SetItemsProcessed(static_cast(state.iterations()) * len); } BENCHMARK(BM_StdVectorFill)->Range(0, 1024); @@ -89,7 +88,7 @@ void BM_InlinedVectorFillString(benchmark::State& state) { const int len = state.range(0); const int no_sso = GetNonShortStringOptimizationSize(); std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'), - std::string(no_sso, 'C'), std::string(no_sso, 'D')}; + std::string(no_sso, 'C'), std::string(no_sso, 'D')}; for (auto _ : state) { absl::InlinedVector v; @@ -105,7 +104,7 @@ void BM_StdVectorFillString(benchmark::State& state) { const int len = state.range(0); const int no_sso = GetNonShortStringOptimizationSize(); std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'), - std::string(no_sso, 'C'), std::string(no_sso, 'D')}; + std::string(no_sso, 'C'), std::string(no_sso, 'D')}; for (auto _ : state) { std::vector v; @@ -124,7 +123,7 @@ struct Buffer { // some arbitrary structure for benchmarking. void* user_data; }; -void BM_InlinedVectorTenAssignments(benchmark::State& state) { +void BM_InlinedVectorAssignments(benchmark::State& state) { const int len = state.range(0); using BufferVec = absl::InlinedVector; @@ -133,18 +132,25 @@ void BM_InlinedVectorTenAssignments(benchmark::State& state) { BufferVec dst; for (auto _ : state) { - for (int i = 0; i < 10; ++i) { - dst = src; - } + benchmark::DoNotOptimize(dst); + benchmark::DoNotOptimize(src); + dst = src; } } -BENCHMARK(BM_InlinedVectorTenAssignments) - ->Arg(0)->Arg(1)->Arg(2)->Arg(3)->Arg(4)->Arg(20); +BENCHMARK(BM_InlinedVectorAssignments) + ->Arg(0) + ->Arg(1) + ->Arg(2) + ->Arg(3) + ->Arg(4) + ->Arg(20); void BM_CreateFromContainer(benchmark::State& state) { for (auto _ : state) { - absl::InlinedVector x(absl::InlinedVector{1, 2, 3}); - benchmark::DoNotOptimize(x); + absl::InlinedVector src{1, 2, 3}; + benchmark::DoNotOptimize(src); + absl::InlinedVector dst(std::move(src)); + benchmark::DoNotOptimize(dst); } } BENCHMARK(BM_CreateFromContainer); @@ -159,15 +165,14 @@ struct LargeCopyableOnly { struct LargeCopyableSwappable { LargeCopyableSwappable() : d(1024, 17) {} + LargeCopyableSwappable(const LargeCopyableSwappable& o) = default; - LargeCopyableSwappable(LargeCopyableSwappable&& o) = delete; LargeCopyableSwappable& operator=(LargeCopyableSwappable o) { using std::swap; swap(*this, o); return *this; } - LargeCopyableSwappable& operator=(LargeCopyableSwappable&& o) = delete; friend void swap(LargeCopyableSwappable& a, LargeCopyableSwappable& b) { using std::swap; @@ -215,6 +220,8 @@ void BM_SwapElements(benchmark::State& state) { Vec b; for (auto _ : state) { using std::swap; + benchmark::DoNotOptimize(a); + benchmark::DoNotOptimize(b); swap(a, b); } } @@ -260,60 +267,44 @@ BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector); void BM_InlinedVectorIndexInlined(benchmark::State& state) { absl::InlinedVector v = {1, 2, 3, 4, 5, 6, 7}; for (auto _ : state) { - for (int i = 0; i < 1000; ++i) { - benchmark::DoNotOptimize(v); - benchmark::DoNotOptimize(v[4]); - } + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v[4]); } - state.SetItemsProcessed(1000 * static_cast(state.iterations())); } BENCHMARK(BM_InlinedVectorIndexInlined); void BM_InlinedVectorIndexExternal(benchmark::State& state) { absl::InlinedVector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - for (int i = 0; i < 1000; ++i) { - benchmark::DoNotOptimize(v); - benchmark::DoNotOptimize(v[4]); - } + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v[4]); } - state.SetItemsProcessed(1000 * static_cast(state.iterations())); } BENCHMARK(BM_InlinedVectorIndexExternal); void BM_StdVectorIndex(benchmark::State& state) { std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - for (int i = 0; i < 1000; ++i) { - benchmark::DoNotOptimize(v); - benchmark::DoNotOptimize(v[4]); - } + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v[4]); } - state.SetItemsProcessed(1000 * static_cast(state.iterations())); } BENCHMARK(BM_StdVectorIndex); -#define UNROLL_2(x) \ - benchmark::DoNotOptimize(x); \ - benchmark::DoNotOptimize(x); - -#define UNROLL_4(x) UNROLL_2(x) UNROLL_2(x) -#define UNROLL_8(x) UNROLL_4(x) UNROLL_4(x) -#define UNROLL_16(x) UNROLL_8(x) UNROLL_8(x); - void BM_InlinedVectorDataInlined(benchmark::State& state) { absl::InlinedVector v = {1, 2, 3, 4, 5, 6, 7}; for (auto _ : state) { - UNROLL_16(v.data()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.data()); } - state.SetItemsProcessed(16 * static_cast(state.iterations())); } BENCHMARK(BM_InlinedVectorDataInlined); void BM_InlinedVectorDataExternal(benchmark::State& state) { absl::InlinedVector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.data()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.data()); } state.SetItemsProcessed(16 * static_cast(state.iterations())); } @@ -322,7 +313,8 @@ BENCHMARK(BM_InlinedVectorDataExternal); void BM_StdVectorData(benchmark::State& state) { std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.data()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.data()); } state.SetItemsProcessed(16 * static_cast(state.iterations())); } @@ -331,55 +323,482 @@ BENCHMARK(BM_StdVectorData); void BM_InlinedVectorSizeInlined(benchmark::State& state) { absl::InlinedVector v = {1, 2, 3, 4, 5, 6, 7}; for (auto _ : state) { - UNROLL_16(v.size()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.size()); } - state.SetItemsProcessed(16 * static_cast(state.iterations())); } BENCHMARK(BM_InlinedVectorSizeInlined); void BM_InlinedVectorSizeExternal(benchmark::State& state) { absl::InlinedVector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.size()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.size()); } - state.SetItemsProcessed(16 * static_cast(state.iterations())); } BENCHMARK(BM_InlinedVectorSizeExternal); void BM_StdVectorSize(benchmark::State& state) { std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.size()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.size()); } - state.SetItemsProcessed(16 * static_cast(state.iterations())); } BENCHMARK(BM_StdVectorSize); void BM_InlinedVectorEmptyInlined(benchmark::State& state) { absl::InlinedVector v = {1, 2, 3, 4, 5, 6, 7}; for (auto _ : state) { - UNROLL_16(v.empty()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.empty()); } - state.SetItemsProcessed(16 * static_cast(state.iterations())); } BENCHMARK(BM_InlinedVectorEmptyInlined); void BM_InlinedVectorEmptyExternal(benchmark::State& state) { absl::InlinedVector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.empty()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.empty()); } - state.SetItemsProcessed(16 * static_cast(state.iterations())); } BENCHMARK(BM_InlinedVectorEmptyExternal); void BM_StdVectorEmpty(benchmark::State& state) { std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.empty()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.empty()); } - state.SetItemsProcessed(16 * static_cast(state.iterations())); } BENCHMARK(BM_StdVectorEmpty); +constexpr size_t kInlinedCapacity = 4; +constexpr size_t kLargeSize = kInlinedCapacity * 2; +constexpr size_t kSmallSize = kInlinedCapacity / 2; +constexpr size_t kBatchSize = 100; + +#define ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_FunctionTemplate, T) \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize); \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize) + +#define ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_FunctionTemplate, T) \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize, kLargeSize); \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize, kSmallSize); \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize, kLargeSize); \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize, kSmallSize) + +template +using InlVec = absl::InlinedVector; + +struct TrivialType { + size_t val; +}; + +class NontrivialType { + public: + ABSL_ATTRIBUTE_NOINLINE NontrivialType() : val_() { + benchmark::DoNotOptimize(*this); + } + + ABSL_ATTRIBUTE_NOINLINE NontrivialType(const NontrivialType& other) + : val_(other.val_) { + benchmark::DoNotOptimize(*this); + } + + ABSL_ATTRIBUTE_NOINLINE NontrivialType& operator=( + const NontrivialType& other) { + val_ = other.val_; + benchmark::DoNotOptimize(*this); + return *this; + } + + ABSL_ATTRIBUTE_NOINLINE ~NontrivialType() noexcept { + benchmark::DoNotOptimize(*this); + } + + private: + size_t val_; +}; + +template +void BatchedBenchmark(benchmark::State& state, PrepareVecFn prepare_vec, + TestVecFn test_vec) { + std::array, kBatchSize> vector_batch{}; + + while (state.KeepRunningBatch(kBatchSize)) { + // Prepare batch + state.PauseTiming(); + for (size_t i = 0; i < kBatchSize; ++i) { + prepare_vec(vector_batch.data() + i, i); + } + benchmark::DoNotOptimize(vector_batch); + state.ResumeTiming(); + + // Test batch + for (size_t i = 0; i < kBatchSize; ++i) { + test_vec(vector_batch.data() + i, i); + } + } +} + +template +void BM_ConstructFromSize(benchmark::State& state) { + using VecT = InlVec; + auto size = ToSize; + BatchedBenchmark( + state, + /* prepare_vec = */ [](InlVec* vec, size_t) { vec->~VecT(); }, + /* test_vec = */ + [&](void* ptr, size_t) { + benchmark::DoNotOptimize(size); + ::new (ptr) VecT(size); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSize, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSize, NontrivialType); + +template +void BM_ConstructFromSizeRef(benchmark::State& state) { + using VecT = InlVec; + auto size = ToSize; + auto ref = T(); + BatchedBenchmark( + state, + /* prepare_vec = */ [](InlVec* vec, size_t) { vec->~VecT(); }, + /* test_vec = */ + [&](void* ptr, size_t) { + benchmark::DoNotOptimize(size); + benchmark::DoNotOptimize(ref); + ::new (ptr) VecT(size, ref); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSizeRef, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSizeRef, NontrivialType); + +template +void BM_ConstructFromRange(benchmark::State& state) { + using VecT = InlVec; + std::array arr{}; + BatchedBenchmark( + state, + /* prepare_vec = */ [](InlVec* vec, size_t) { vec->~VecT(); }, + /* test_vec = */ + [&](void* ptr, size_t) { + benchmark::DoNotOptimize(arr); + ::new (ptr) VecT(arr.begin(), arr.end()); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromRange, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromRange, NontrivialType); + +template +void BM_ConstructFromCopy(benchmark::State& state) { + using VecT = InlVec; + VecT other_vec(ToSize); + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { vec->~VecT(); }, + /* test_vec = */ + [&](void* ptr, size_t) { + benchmark::DoNotOptimize(other_vec); + ::new (ptr) VecT(other_vec); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromCopy, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromCopy, NontrivialType); + +template +void BM_ConstructFromMove(benchmark::State& state) { + using VecT = InlVec; + std::array vector_batch{}; + BatchedBenchmark( + state, + /* prepare_vec = */ + [&](InlVec* vec, size_t i) { + vector_batch[i].clear(); + vector_batch[i].resize(ToSize); + vec->~VecT(); + }, + /* test_vec = */ + [&](void* ptr, size_t i) { + benchmark::DoNotOptimize(vector_batch[i]); + ::new (ptr) VecT(std::move(vector_batch[i])); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, NontrivialType); + +template +void BM_AssignSizeRef(benchmark::State& state) { + auto size = ToSize; + auto ref = T(); + BatchedBenchmark( + state, + /* prepare_vec = */ [](InlVec* vec, size_t) { vec->resize(FromSize); }, + /* test_vec = */ + [&](InlVec* vec, size_t) { + benchmark::DoNotOptimize(size); + benchmark::DoNotOptimize(ref); + vec->assign(size, ref); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignSizeRef, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignSizeRef, NontrivialType); + +template +void BM_AssignRange(benchmark::State& state) { + std::array arr{}; + BatchedBenchmark( + state, + /* prepare_vec = */ [](InlVec* vec, size_t) { vec->resize(FromSize); }, + /* test_vec = */ + [&](InlVec* vec, size_t) { + benchmark::DoNotOptimize(arr); + vec->assign(arr.begin(), arr.end()); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignRange, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignRange, NontrivialType); + +template +void BM_AssignFromCopy(benchmark::State& state) { + InlVec other_vec(ToSize); + BatchedBenchmark( + state, + /* prepare_vec = */ [](InlVec* vec, size_t) { vec->resize(FromSize); }, + /* test_vec = */ + [&](InlVec* vec, size_t) { + benchmark::DoNotOptimize(other_vec); + *vec = other_vec; + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromCopy, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromCopy, NontrivialType); + +template +void BM_AssignFromMove(benchmark::State& state) { + using VecT = InlVec; + std::array vector_batch{}; + BatchedBenchmark( + state, + /* prepare_vec = */ + [&](InlVec* vec, size_t i) { + vector_batch[i].clear(); + vector_batch[i].resize(ToSize); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec* vec, size_t i) { + benchmark::DoNotOptimize(vector_batch[i]); + *vec = std::move(vector_batch[i]); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromMove, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromMove, NontrivialType); + +template +void BM_ResizeSize(benchmark::State& state) { + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec* vec, size_t) { vec->resize(ToSize); }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSize, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSize, NontrivialType); + +template +void BM_ResizeSizeRef(benchmark::State& state) { + auto t = T(); + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec* vec, size_t) { + benchmark::DoNotOptimize(t); + vec->resize(ToSize, t); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSizeRef, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSizeRef, NontrivialType); + +template +void BM_InsertSizeRef(benchmark::State& state) { + auto t = T(); + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec* vec, size_t) { + benchmark::DoNotOptimize(t); + auto* pos = vec->data() + (vec->size() / 2); + vec->insert(pos, t); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertSizeRef, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertSizeRef, NontrivialType); + +template +void BM_InsertRange(benchmark::State& state) { + InlVec other_vec(ToSize); + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec* vec, size_t) { + benchmark::DoNotOptimize(other_vec); + auto* pos = vec->data() + (vec->size() / 2); + vec->insert(pos, other_vec.begin(), other_vec.end()); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertRange, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertRange, NontrivialType); + +template +void BM_EmplaceBack(benchmark::State& state) { + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec* vec, size_t) { vec->emplace_back(); }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EmplaceBack, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EmplaceBack, NontrivialType); + +template +void BM_PopBack(benchmark::State& state) { + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec* vec, size_t) { vec->pop_back(); }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_PopBack, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_PopBack, NontrivialType); + +template +void BM_EraseOne(benchmark::State& state) { + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec* vec, size_t) { + auto* pos = vec->data() + (vec->size() / 2); + vec->erase(pos); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseOne, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseOne, NontrivialType); + +template +void BM_EraseRange(benchmark::State& state) { + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec* vec, size_t) { + auto* pos = vec->data() + (vec->size() / 2); + vec->erase(pos, pos + 1); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseRange, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseRange, NontrivialType); + +template +void BM_Clear(benchmark::State& state) { + BatchedBenchmark( + state, + /* prepare_vec = */ [](InlVec* vec, size_t) { vec->resize(FromSize); }, + /* test_vec = */ [](InlVec* vec, size_t) { vec->clear(); }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, NontrivialType); + +template +void BM_Reserve(benchmark::State& state) { + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec* vec, size_t) { vec->reserve(ToCapacity); }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Reserve, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Reserve, NontrivialType); + +template +void BM_ShrinkToFit(benchmark::State& state) { + BatchedBenchmark( + state, + /* prepare_vec = */ + [](InlVec* vec, size_t) { + vec->clear(); + vec->resize(ToCapacity); + vec->reserve(FromCapacity); + }, + /* test_vec = */ [](InlVec* vec, size_t) { vec->shrink_to_fit(); }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ShrinkToFit, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ShrinkToFit, NontrivialType); + +template +void BM_Swap(benchmark::State& state) { + using VecT = InlVec; + std::array vector_batch{}; + BatchedBenchmark( + state, + /* prepare_vec = */ + [&](InlVec* vec, size_t i) { + vector_batch[i].clear(); + vector_batch[i].resize(ToSize); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec* vec, size_t i) { + using std::swap; + benchmark::DoNotOptimize(vector_batch[i]); + swap(*vec, vector_batch[i]); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Swap, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Swap, NontrivialType); + } // namespace diff --git a/absl/container/inlined_vector_exception_safety_test.cc b/absl/container/inlined_vector_exception_safety_test.cc new file mode 100644 index 00000000..ff0da75b --- /dev/null +++ b/absl/container/inlined_vector_exception_safety_test.cc @@ -0,0 +1,489 @@ +// 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 +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/base/internal/exception_safety_testing.h" +#include "absl/container/inlined_vector.h" + +namespace { + +constexpr size_t kInlinedCapacity = 4; +constexpr size_t kLargeSize = kInlinedCapacity * 2; +constexpr size_t kSmallSize = kInlinedCapacity / 2; + +using Thrower = testing::ThrowingValue<>; +using MovableThrower = testing::ThrowingValue; +using ThrowAlloc = testing::ThrowingAllocator; + +using ThrowerVec = absl::InlinedVector; +using MovableThrowerVec = absl::InlinedVector; + +using ThrowAllocThrowerVec = + absl::InlinedVector; +using ThrowAllocMovableThrowerVec = + absl::InlinedVector; + +// In GCC, if an element of a `std::initializer_list` throws during construction +// the elements that were constructed before it are not destroyed. This causes +// incorrect exception safety test failures. Thus, `testing::nothrow_ctor` is +// required. See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66139 +#define ABSL_INTERNAL_MAKE_INIT_LIST(T, N) \ + (N > kInlinedCapacity \ + ? std::initializer_list{T(0, testing::nothrow_ctor), \ + T(1, testing::nothrow_ctor), \ + T(2, testing::nothrow_ctor), \ + T(3, testing::nothrow_ctor), \ + T(4, testing::nothrow_ctor), \ + T(5, testing::nothrow_ctor), \ + T(6, testing::nothrow_ctor), \ + T(7, testing::nothrow_ctor)} \ + \ + : std::initializer_list{T(0, testing::nothrow_ctor), \ + T(1, testing::nothrow_ctor)}) +static_assert((kLargeSize == 8 || kSmallSize == 2), + "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)."); + +template +class TestParams { + public: + using VecT = TheVecT; + constexpr static size_t GetSizeAt(size_t i) { return kSizes[1 + i]; } + + private: + constexpr static size_t kSizes[1 + sizeof...(TheSizes)] = {1, TheSizes...}; +}; + +using NoSizeTestParams = + ::testing::Types, TestParams, + TestParams, + TestParams>; + +using OneSizeTestParams = + ::testing::Types, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams>; + +using TwoSizeTestParams = ::testing::Types< + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams, + TestParams>; + +template +struct NoSizeTest : ::testing::Test {}; +TYPED_TEST_SUITE(NoSizeTest, NoSizeTestParams); + +template +struct OneSizeTest : ::testing::Test {}; +TYPED_TEST_SUITE(OneSizeTest, OneSizeTestParams); + +template +struct TwoSizeTest : ::testing::Test {}; +TYPED_TEST_SUITE(TwoSizeTest, TwoSizeTestParams); + +template +bool InlinedVectorInvariants(VecT* vec) { + if (*vec != *vec) return false; + if (vec->size() > vec->capacity()) return false; + if (vec->size() > vec->max_size()) return false; + if (vec->capacity() > vec->max_size()) return false; + if (vec->data() != std::addressof(vec->at(0))) return false; + if (vec->data() != vec->begin()) return false; + if (*vec->data() != *vec->begin()) return false; + if (vec->begin() > vec->end()) return false; + if ((vec->end() - vec->begin()) != vec->size()) return false; + if (std::distance(vec->begin(), vec->end()) != vec->size()) return false; + return true; +} + +// Function that always returns false is correct, but refactoring is required +// for clarity. It's needed to express that, as a contract, certain operations +// should not throw at all. Execution of this function means an exception was +// thrown and thus the test should fail. +// TODO(johnsoncj): Add `testing::NoThrowGuarantee` to the framework +template +bool NoThrowGuarantee(VecT* /* vec */) { + return false; +} + +TYPED_TEST(NoSizeTest, DefaultConstructor) { + using VecT = typename TypeParam::VecT; + using allocator_type = typename VecT::allocator_type; + + testing::TestThrowingCtor(); + + testing::TestThrowingCtor(allocator_type{}); +} + +TYPED_TEST(OneSizeTest, SizeConstructor) { + using VecT = typename TypeParam::VecT; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + testing::TestThrowingCtor(size); + + testing::TestThrowingCtor(size, allocator_type{}); +} + +TYPED_TEST(OneSizeTest, SizeRefConstructor) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + testing::TestThrowingCtor(size, value_type{}); + + testing::TestThrowingCtor(size, value_type{}, allocator_type{}); +} + +TYPED_TEST(OneSizeTest, InitializerListConstructor) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + testing::TestThrowingCtor( + ABSL_INTERNAL_MAKE_INIT_LIST(value_type, size)); + + testing::TestThrowingCtor( + ABSL_INTERNAL_MAKE_INIT_LIST(value_type, size), allocator_type{}); +} + +TYPED_TEST(OneSizeTest, RangeConstructor) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + std::array arr{}; + + testing::TestThrowingCtor(arr.begin(), arr.end()); + + testing::TestThrowingCtor(arr.begin(), arr.end(), allocator_type{}); +} + +TYPED_TEST(OneSizeTest, CopyConstructor) { + using VecT = typename TypeParam::VecT; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + VecT other_vec{size}; + + testing::TestThrowingCtor(other_vec); + + testing::TestThrowingCtor(other_vec, allocator_type{}); +} + +TYPED_TEST(OneSizeTest, MoveConstructor) { + using VecT = typename TypeParam::VecT; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + if (!absl::allocator_is_nothrow::value) { + testing::TestThrowingCtor(VecT{size}); + + testing::TestThrowingCtor(VecT{size}, allocator_type{}); + } +} + +TYPED_TEST(TwoSizeTest, Assign) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto to_size = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + *vec = ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + VecT other_vec{to_size}; + *vec = other_vec; + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + VecT other_vec{to_size}; + *vec = std::move(other_vec); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + value_type val{}; + vec->assign(to_size, val); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->assign(ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size)); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + std::array arr{}; + vec->assign(arr.begin(), arr.end()); + })); +} + +TYPED_TEST(TwoSizeTest, Resize) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto to_size = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants, + testing::strong_guarantee); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->resize(to_size); // + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->resize(to_size, value_type{}); // + })); +} + +TYPED_TEST(OneSizeTest, Insert) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->insert(it, value_type{}); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->insert(it, value_type{}); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->end(); + vec->insert(it, value_type{}); + })); +} + +TYPED_TEST(TwoSizeTest, Insert) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto count = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->insert(it, count, value_type{}); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->insert(it, count, value_type{}); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->end(); + vec->insert(it, count, value_type{}); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count)); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count)); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->end(); + vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count)); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + std::array arr{}; + vec->insert(it, arr.begin(), arr.end()); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + std::array arr{}; + vec->insert(it, arr.begin(), arr.end()); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->end(); + std::array arr{}; + vec->insert(it, arr.begin(), arr.end()); + })); +} + +TYPED_TEST(OneSizeTest, EmplaceBack) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + VecT full_vec{size}; + full_vec.resize(full_vec.capacity()); + + VecT nonfull_vec{size}; + nonfull_vec.reserve(size + 1); + + auto tester = testing::MakeExceptionSafetyTester().WithContracts( + InlinedVectorInvariants); + + EXPECT_TRUE(tester.WithInitialValue(nonfull_vec).Test([](VecT* vec) { + vec->emplace_back(); // + })); + + EXPECT_TRUE(tester.WithInitialValue(full_vec).Test([](VecT* vec) { + vec->emplace_back(); // + })); +} + +TYPED_TEST(OneSizeTest, PopBack) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{size}) + .WithContracts(NoThrowGuarantee); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->pop_back(); // + })); +} + +TYPED_TEST(OneSizeTest, Erase) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{size}) + .WithContracts(InlinedVectorInvariants); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->erase(it); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->erase(it); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() - 1); + vec->erase(it); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->erase(it, it + 1); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->erase(it, it + 1); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() - 1); + vec->erase(it, it + 1); + })); +} + +TYPED_TEST(OneSizeTest, Clear) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{size}) + .WithContracts(NoThrowGuarantee); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->clear(); // + })); +} + +TYPED_TEST(TwoSizeTest, Reserve) { + using VecT = typename TypeParam::VecT; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto to_capacity = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->reserve(to_capacity); // + })); +} + +TYPED_TEST(OneSizeTest, ShrinkToFit) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{size}) + .WithContracts(InlinedVectorInvariants); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->shrink_to_fit(); // + })); +} + +TYPED_TEST(TwoSizeTest, Swap) { + using VecT = typename TypeParam::VecT; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto to_size = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + VecT other_vec{to_size}; + vec->swap(other_vec); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + using std::swap; + VecT other_vec{to_size}; + swap(*vec, other_vec); + })); +} + +} // namespace diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index 3a1ea8ac..bada4fec 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc @@ -1,10 +1,10 @@ -// 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. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,6 +30,7 @@ #include "absl/base/internal/exception_testing.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" +#include "absl/container/internal/counting_allocator.h" #include "absl/container/internal/test_instance_tracker.h" #include "absl/hash/hash_testing.h" #include "absl/memory/memory.h" @@ -37,6 +38,7 @@ namespace { +using absl::container_internal::CountingAllocator; using absl::test_internal::CopyableMovableInstance; using absl::test_internal::CopyableOnlyInstance; using absl::test_internal::InstanceTracker; @@ -68,18 +70,15 @@ MATCHER_P(ValueIs, e, "") { // test_instance_tracker.h. template class InstanceTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(InstanceTest); +TYPED_TEST_SUITE_P(InstanceTest); // A simple reference counted class to make sure that the proper elements are // destroyed in the erase(begin, end) test. class RefCounted { public: - RefCounted(int value, int* count) : value_(value), count_(count) { - Ref(); - } + RefCounted(int value, int* count) : value_(value), count_(count) { Ref(); } - RefCounted(const RefCounted& v) - : value_(v.value_), count_(v.count_) { + RefCounted(const RefCounted& v) : value_(v.value_), count_(v.count_) { Ref(); } @@ -138,57 +137,6 @@ static IntVec Fill(int len, int offset = 0) { return v; } -// This is a stateful allocator, but the state lives outside of the -// allocator (in whatever test is using the allocator). This is odd -// but helps in tests where the allocator is propagated into nested -// containers - that chain of allocators uses the same state and is -// thus easier to query for aggregate allocation information. -template -class CountingAllocator : public std::allocator { - public: - using Alloc = std::allocator; - using pointer = typename Alloc::pointer; - using size_type = typename Alloc::size_type; - - CountingAllocator() : bytes_used_(nullptr) {} - explicit CountingAllocator(int64_t* b) : bytes_used_(b) {} - - template - CountingAllocator(const CountingAllocator& x) - : Alloc(x), bytes_used_(x.bytes_used_) {} - - pointer allocate(size_type n, - std::allocator::const_pointer hint = nullptr) { - assert(bytes_used_ != nullptr); - *bytes_used_ += n * sizeof(T); - return Alloc::allocate(n, hint); - } - - void deallocate(pointer p, size_type n) { - Alloc::deallocate(p, n); - assert(bytes_used_ != nullptr); - *bytes_used_ -= n * sizeof(T); - } - - template - class rebind { - public: - using other = CountingAllocator; - }; - - friend bool operator==(const CountingAllocator& a, - const CountingAllocator& b) { - return a.bytes_used_ == b.bytes_used_; - } - - friend bool operator!=(const CountingAllocator& a, - const CountingAllocator& b) { - return !(a == b); - } - - int64_t* bytes_used_; -}; - TEST(IntVec, SimpleOps) { for (int len = 0; len < 20; len++) { IntVec v; @@ -239,6 +187,12 @@ TEST(IntVec, SimpleOps) { } } +TEST(IntVec, PopBackNoOverflow) { + IntVec v = {1}; + v.pop_back(); + EXPECT_EQ(v.size(), 0); +} + TEST(IntVec, AtThrows) { IntVec v = {1, 2, 3}; EXPECT_EQ(v.at(2), 3); @@ -333,7 +287,7 @@ TEST(RefCountedVec, EraseBeginEnd) { } // Check that the elements at the end are preserved. - for (int i = erase_end; i< len; ++i) { + for (int i = erase_end; i < len; ++i) { EXPECT_EQ(1, counts[i]); } } @@ -595,10 +549,10 @@ TEST(IntVec, Resize) { static const int kResizeElem = 1000000; for (int k = 0; k < 10; k++) { // Enlarging resize - v.resize(len+k, kResizeElem); - EXPECT_EQ(len+k, v.size()); - EXPECT_LE(len+k, v.capacity()); - for (int i = 0; i < len+k; i++) { + v.resize(len + k, kResizeElem); + EXPECT_EQ(len + k, v.size()); + EXPECT_LE(len + k, v.capacity()); + for (int i = 0; i < len + k; i++) { if (i < len) { EXPECT_EQ(i, v[i]); } else { @@ -909,7 +863,7 @@ TYPED_TEST_P(InstanceTest, Swap) { auto min_len = std::min(l1, l2); auto max_len = std::max(l1, l2); for (int i = 0; i < l1; i++) a.push_back(Instance(i)); - for (int i = 0; i < l2; i++) b.push_back(Instance(100+i)); + for (int i = 0; i < l2; i++) b.push_back(Instance(100 + i)); EXPECT_EQ(tracker.instances(), l1 + l2); tracker.ResetCopiesMovesSwaps(); { @@ -977,7 +931,7 @@ TEST(IntVec, EqualAndNotEqual) { EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); - b[i] = b[i] - 1; // Back to before + b[i] = b[i] - 1; // Back to before EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); } @@ -1044,7 +998,7 @@ TYPED_TEST_P(InstanceTest, CountConstructorsDestructors) { // reserve() must not increase the number of initialized objects SCOPED_TRACE("reserve"); - v.reserve(len+1000); + v.reserve(len + 1000); EXPECT_EQ(tracker.instances(), len); EXPECT_EQ(tracker.copies() + tracker.moves(), len); @@ -1290,9 +1244,8 @@ void InstanceCountElemAssignWithAllocationTest() { absl::InlinedVector v(original_contents.begin(), original_contents.end()); v.assign(3, Instance(123)); - EXPECT_THAT(v, - AllOf(SizeIs(3), - ElementsAre(ValueIs(123), ValueIs(123), ValueIs(123)))); + EXPECT_THAT(v, AllOf(SizeIs(3), ElementsAre(ValueIs(123), ValueIs(123), + ValueIs(123)))); EXPECT_LE(v.size(), v.capacity()); } } @@ -1571,8 +1524,8 @@ TYPED_TEST_P(InstanceTest, InitializerListAssign) { SCOPED_TRACE(original_size); absl::InlinedVector v(original_size, Instance(12345)); v.assign({Instance(3), Instance(4), Instance(5)}); - EXPECT_THAT(v, AllOf(SizeIs(3), - ElementsAre(ValueIs(3), ValueIs(4), ValueIs(5)))); + EXPECT_THAT( + v, AllOf(SizeIs(3), ElementsAre(ValueIs(3), ValueIs(4), ValueIs(5)))); EXPECT_LE(3, v.capacity()); } } @@ -1597,7 +1550,7 @@ TEST(DynamicVec, DynamicVecCompiles) { TEST(AllocatorSupportTest, Constructors) { using MyAlloc = CountingAllocator; using AllocVec = absl::InlinedVector; - const int ia[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7}; int64_t allocated = 0; MyAlloc alloc(&allocated); { AllocVec ABSL_ATTRIBUTE_UNUSED v; } @@ -1613,7 +1566,7 @@ TEST(AllocatorSupportTest, Constructors) { TEST(AllocatorSupportTest, CountAllocations) { using MyAlloc = CountingAllocator; using AllocVec = absl::InlinedVector; - const int ia[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7}; int64_t allocated = 0; MyAlloc alloc(&allocated); { @@ -1677,8 +1630,8 @@ TEST(AllocatorSupportTest, SwapBothAllocated) { int64_t allocated1 = 0; int64_t allocated2 = 0; { - const int ia1[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; - const int ia2[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + const int ia1[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const int ia2[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; MyAlloc a1(&allocated1); MyAlloc a2(&allocated2); AllocVec v1(ia1, ia1 + ABSL_ARRAYSIZE(ia1), a1); @@ -1702,8 +1655,8 @@ TEST(AllocatorSupportTest, SwapOneAllocated) { int64_t allocated1 = 0; int64_t allocated2 = 0; { - const int ia1[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; - const int ia2[] = { 0, 1, 2, 3 }; + const int ia1[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const int ia2[] = {0, 1, 2, 3}; MyAlloc a1(&allocated1); MyAlloc a2(&allocated2); AllocVec v1(ia1, ia1 + ABSL_ARRAYSIZE(ia1), a1); @@ -1722,67 +1675,53 @@ TEST(AllocatorSupportTest, SwapOneAllocated) { EXPECT_THAT(allocated2, 0); } -TEST(AllocatorSupportTest, ScopedAllocatorWorks) { +TEST(AllocatorSupportTest, ScopedAllocatorWorksInlined) { using StdVector = std::vector>; - using MyAlloc = - std::scoped_allocator_adaptor>; - using AllocVec = absl::InlinedVector; - - // MSVC 2017's std::vector allocates different amounts of memory in debug - // versus opt mode. - int64_t test_allocated = 0; - StdVector v(CountingAllocator{&test_allocated}); - // The amount of memory allocated by a default constructed vector - auto default_std_vec_allocated = test_allocated; - v.push_back(1); - // The amound of memory allocated by a copy-constructed vector with one - // element. - int64_t one_element_std_vec_copy_allocated = test_allocated; + using Alloc = CountingAllocator; + using ScopedAlloc = std::scoped_allocator_adaptor; + using AllocVec = absl::InlinedVector; - int64_t allocated = 0; - AllocVec vec(MyAlloc{CountingAllocator{&allocated}}); - EXPECT_EQ(allocated, 0); + int64_t total_allocated_byte_count = 0; - // This default constructs a vector, but the allocator should pass itself - // into the vector, so check allocation compared to that. - // The absl::InlinedVector does not allocate any memory. - // The vector may allocate any memory. - auto expected = default_std_vec_allocated; - vec.resize(1); - EXPECT_EQ(allocated, expected); - - // We make vector allocate memory. - // It must go through the allocator even though we didn't construct the - // vector directly. This assumes that vec[0] doesn't need to grow its - // allocation. - expected += sizeof(int); - vec[0].push_back(1); - EXPECT_EQ(allocated, expected); - - // Another allocating vector. - expected += one_element_std_vec_copy_allocated; - vec.push_back(vec[0]); - EXPECT_EQ(allocated, expected); - - // Overflow the inlined memory. - // The absl::InlinedVector will now allocate. - expected += sizeof(StdVector) * 8 + default_std_vec_allocated * 3; - vec.resize(5); - EXPECT_EQ(allocated, expected); - - // Adding one more in external mode should also work. - expected += one_element_std_vec_copy_allocated; - vec.push_back(vec[0]); - EXPECT_EQ(allocated, expected); - - // And extending these should still work. This assumes that vec[0] does not - // need to grow its allocation. - expected += sizeof(int); - vec[0].push_back(1); - EXPECT_EQ(allocated, expected); - - vec.clear(); - EXPECT_EQ(allocated, 0); + AllocVec inlined_case(ScopedAlloc(Alloc(+&total_allocated_byte_count))); + + // Called only once to remain inlined + inlined_case.emplace_back(); + + int64_t absl_responsible_for_count = total_allocated_byte_count; + EXPECT_EQ(absl_responsible_for_count, 0); + + inlined_case[0].emplace_back(); + EXPECT_GT(total_allocated_byte_count, absl_responsible_for_count); + + inlined_case.clear(); + inlined_case.shrink_to_fit(); + EXPECT_EQ(total_allocated_byte_count, 0); +} + +TEST(AllocatorSupportTest, ScopedAllocatorWorksAllocated) { + using StdVector = std::vector>; + using Alloc = CountingAllocator; + using ScopedAlloc = std::scoped_allocator_adaptor; + using AllocVec = absl::InlinedVector; + + int64_t total_allocated_byte_count = 0; + + AllocVec allocated_case(ScopedAlloc(Alloc(+&total_allocated_byte_count))); + + // Called twice to force into being allocated + allocated_case.emplace_back(); + allocated_case.emplace_back(); + + int64_t absl_responsible_for_count = total_allocated_byte_count; + EXPECT_GT(absl_responsible_for_count, 0); + + allocated_case[1].emplace_back(); + EXPECT_GT(total_allocated_byte_count, absl_responsible_for_count); + + allocated_case.clear(); + allocated_case.shrink_to_fit(); + EXPECT_EQ(total_allocated_byte_count, 0); } TEST(AllocatorSupportTest, SizeAllocConstructor) { @@ -1811,4 +1750,23 @@ TEST(AllocatorSupportTest, SizeAllocConstructor) { } } +TEST(InlinedVectorTest, AbslHashValueWorks) { + using V = absl::InlinedVector; + std::vector cases; + + // Generate a variety of vectors some of these are small enough for the inline + // space but are stored out of line. + for (int i = 0; i < 10; ++i) { + V v; + for (int j = 0; j < i; ++j) { + v.push_back(j); + } + cases.push_back(v); + v.resize(i % 4); + cases.push_back(v); + } + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases)); +} + } // anonymous namespace diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h new file mode 100644 index 00000000..a02cd5c3 --- /dev/null +++ b/absl/container/internal/common.h @@ -0,0 +1,198 @@ +// 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_CONTAINER_H_ +#define ABSL_CONTAINER_INTERNAL_CONTAINER_H_ + +#include +#include + +#include "absl/meta/type_traits.h" +#include "absl/types/optional.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +template +struct IsTransparent : std::false_type {}; +template +struct IsTransparent> + : std::true_type {}; + +template +struct KeyArg { + // Transparent. Forward `K`. + template + using type = K; +}; + +template <> +struct KeyArg { + // Not transparent. Always use `key_type`. + template + using type = key_type; +}; + +// The node_handle concept from C++17. +// We specialize node_handle for sets and maps. node_handle_base holds the +// common API of both. +template +class node_handle_base { + protected: + using slot_type = typename PolicyTraits::slot_type; + + public: + using allocator_type = Alloc; + + constexpr node_handle_base() {} + node_handle_base(node_handle_base&& other) noexcept { + *this = std::move(other); + } + ~node_handle_base() { destroy(); } + node_handle_base& operator=(node_handle_base&& other) noexcept { + destroy(); + if (!other.empty()) { + alloc_ = other.alloc_; + PolicyTraits::transfer(alloc(), slot(), other.slot()); + other.reset(); + } + return *this; + } + + bool empty() const noexcept { return !alloc_; } + explicit operator bool() const noexcept { return !empty(); } + allocator_type get_allocator() const { return *alloc_; } + + protected: + friend struct CommonAccess; + + struct transfer_tag_t {}; + node_handle_base(transfer_tag_t, const allocator_type& a, slot_type* s) + : alloc_(a) { + PolicyTraits::transfer(alloc(), slot(), s); + } + + struct move_tag_t {}; + node_handle_base(move_tag_t, const allocator_type& a, slot_type* s) + : alloc_(a) { + PolicyTraits::construct(alloc(), slot(), s); + } + + void destroy() { + if (!empty()) { + PolicyTraits::destroy(alloc(), slot()); + reset(); + } + } + + void reset() { + assert(alloc_.has_value()); + alloc_ = absl::nullopt; + } + + slot_type* slot() const { + assert(!empty()); + return reinterpret_cast(std::addressof(slot_space_)); + } + allocator_type* alloc() { return std::addressof(*alloc_); } + + private: + absl::optional alloc_; + mutable absl::aligned_storage_t + slot_space_; +}; + +// For sets. +template +class node_handle : public node_handle_base { + using Base = typename node_handle::node_handle_base; + + public: + using value_type = typename PolicyTraits::value_type; + + constexpr node_handle() {} + + value_type& value() const { return PolicyTraits::element(this->slot()); } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// For maps. +template +class node_handle> + : public node_handle_base { + using Base = typename node_handle::node_handle_base; + + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + + constexpr node_handle() {} + + auto key() const -> decltype(PolicyTraits::key(this->slot())) { + return PolicyTraits::key(this->slot()); + } + + mapped_type& mapped() const { + return PolicyTraits::value(&PolicyTraits::element(this->slot())); + } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// Provide access to non-public node-handle functions. +struct CommonAccess { + template + static auto GetSlot(const Node& node) -> decltype(node.slot()) { + return node.slot(); + } + + template + static void Reset(Node* node) { + node->reset(); + } + + template + static T Transfer(Args&&... args) { + return T(typename T::transfer_tag_t{}, std::forward(args)...); + } + + template + static T Move(Args&&... args) { + return T(typename T::move_tag_t{}, std::forward(args)...); + } +}; + +// Implement the insert_return_type<> concept of C++17. +template +struct InsertReturnType { + Iterator position; + bool inserted; + NodeType node; +}; + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // 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 29fe7c12..fbace496 100644 --- a/absl/container/internal/compressed_tuple.h +++ b/absl/container/internal/compressed_tuple.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,27 +27,28 @@ // const T2& t2 = value.get<2>(); // ... // -// http://en.cppreference.com/w/cpp/language/ebo +// https://en.cppreference.com/w/cpp/language/ebo #ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ #define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ +#include #include #include #include #include "absl/utility/utility.h" -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__NVCC__) // We need to mark these classes with this declspec to ensure that // CompressedTuple happens. #define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases) -#else // _MSC_VER +#else #define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC -#endif // _MSC_VER +#endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template @@ -76,57 +77,110 @@ constexpr bool IsFinal() { #endif } +// We can't use EBCO on other CompressedTuples because that would mean that we +// derive from multiple Storage<> instantiations with the same I parameter, +// and potentially from multiple identical Storage<> instantiations. So anytime +// we use type inheritance rather than encapsulation, we mark +// CompressedTupleImpl, to make this easy to detect. +struct uses_inheritance {}; + template constexpr bool ShouldUseBase() { - return std::is_class::value && std::is_empty::value && !IsFinal(); + return std::is_class::value && std::is_empty::value && !IsFinal() && + !std::is_base_of::value; } // The storage class provides two specializations: // - For empty classes, it stores T as a base class. // - For everything else, it stores T as a member. -template >()> +template ::type>()> +#else + bool UseBase = ShouldUseBase()> +#endif struct Storage { - using T = ElemT; T value; constexpr Storage() = default; - explicit constexpr Storage(T&& v) : value(absl::forward(v)) {} - constexpr const T& get() const { return value; } - T& get() { return value; } + template + explicit constexpr Storage(absl::in_place_t, V&& v) + : value(absl::forward(v)) {} + constexpr const T& get() const& { return value; } + T& get() & { return value; } + constexpr const T&& get() const&& { return absl::move(*this).value; } + T&& get() && { return std::move(*this).value; } }; -template -struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage - : ElemT { - using T = internal_compressed_tuple::ElemT; +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage : T { constexpr Storage() = default; - explicit constexpr Storage(T&& v) : T(absl::forward(v)) {} - constexpr const T& get() const { return *this; } - T& get() { return *this; } + + template + explicit constexpr Storage(absl::in_place_t, V&& v) + : T(absl::forward(v)) {} + + constexpr const T& get() const& { return *this; } + T& get() & { return *this; } + constexpr const T&& get() const&& { return absl::move(*this); } + T&& get() && { return std::move(*this); } }; -template +template struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl; -template -struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC - CompressedTupleImpl, absl::index_sequence> +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple, absl::index_sequence, ShouldAnyUseBase> // We use the dummy identity function through std::integral_constant to // convince MSVC of accepting and expanding I in that context. Without it // you would get: // error C3548: 'I': parameter pack cannot be used in this context - : Storage, - std::integral_constant::value>... { + : uses_inheritance, + Storage::value>... { constexpr CompressedTupleImpl() = default; - explicit constexpr CompressedTupleImpl(Ts&&... args) - : Storage, I>(absl::forward(args))... {} + template + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage(absl::in_place, absl::forward(args))... {} + friend CompressedTuple; }; +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple, absl::index_sequence, false> + // We use the dummy identity function as above... + : Storage::value, false>... { + constexpr CompressedTupleImpl() = default; + template + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage(absl::in_place, absl::forward(args))... {} + friend CompressedTuple; +}; + +std::false_type Or(std::initializer_list); +std::true_type Or(std::initializer_list); + +// MSVC requires this to be done separately rather than within the declaration +// of CompressedTuple below. +template +constexpr bool ShouldAnyUseBase() { + return decltype( + Or({std::integral_constant()>()...})){}; +} + +template +using TupleMoveConstructible = typename std::conditional< + std::is_reference::value, std::is_convertible, + std::is_constructible>::type; + } // namespace internal_compressed_tuple // Helper class to perform the Empty Base Class Optimization. // Ts can contain classes and non-classes, empty or not. For the ones that // are empty classes, we perform the CompressedTuple. If all types in Ts are -// empty classes, then CompressedTuple is itself an empty class. +// empty classes, then CompressedTuple is itself an empty class. (This +// does not apply when one or more of those empty classes is itself an empty +// CompressedTuple.) // // To access the members, use member .get() function. // @@ -138,28 +192,62 @@ struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC // const T2& t2 = value.get<2>(); // ... // -// http://en.cppreference.com/w/cpp/language/ebo +// https://en.cppreference.com/w/cpp/language/ebo template class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple : private internal_compressed_tuple::CompressedTupleImpl< - CompressedTuple, absl::index_sequence_for> { + CompressedTuple, absl::index_sequence_for, + internal_compressed_tuple::ShouldAnyUseBase()> { private: template using ElemT = internal_compressed_tuple::ElemT; + template + using StorageT = internal_compressed_tuple::Storage, I>; + public: + // There seems to be a bug in MSVC dealing in which using '=default' here will + // cause the compiler to ignore the body of other constructors. The work- + // around is to explicitly implement the default constructor. +#if defined(_MSC_VER) + constexpr CompressedTuple() : CompressedTuple::CompressedTupleImpl() {} +#else constexpr CompressedTuple() = default; - explicit constexpr CompressedTuple(Ts... base) - : CompressedTuple::CompressedTupleImpl(absl::forward(base)...) {} +#endif + explicit constexpr CompressedTuple(const Ts&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {} + + template ...)>>, + internal_compressed_tuple::TupleMoveConstructible< + Ts, Vs&&>...>::value, + bool> = true> + explicit constexpr CompressedTuple(Vs&&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, + absl::forward(base)...) {} + + template + ElemT& get() & { + return internal_compressed_tuple::Storage, I>::get(); + } + + template + constexpr const ElemT& get() const& { + return StorageT::get(); + } template - ElemT& get() { - return internal_compressed_tuple::Storage::get(); + ElemT&& get() && { + return std::move(*this).StorageT::get(); } template - constexpr const ElemT& get() const { - return internal_compressed_tuple::Storage::get(); + constexpr const ElemT&& get() const&& { + return absl::move(*this).StorageT::get(); } }; @@ -169,7 +257,7 @@ template <> class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {}; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 2b5ed4a4..ec893b90 100644 --- a/absl/container/internal/compressed_tuple_test.cc +++ b/absl/container/internal/compressed_tuple_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -14,18 +14,26 @@ #include "absl/container/internal/compressed_tuple.h" +#include #include #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/container/internal/test_instance_tracker.h" +#include "absl/memory/memory.h" +#include "absl/types/any.h" +#include "absl/types/optional.h" +#include "absl/utility/utility.h" -namespace absl { -inline namespace lts_2018_12_18 { -namespace container_internal { -namespace { +// These are declared at global scope purely so that error messages +// are smaller and easier to understand. +enum class CallType { kConstRef, kConstMove }; template -struct Empty {}; +struct Empty { + constexpr CallType value() const& { return CallType::kConstRef; } + constexpr CallType value() const&& { return CallType::kConstMove; } +}; template struct NotEmpty { @@ -38,6 +46,15 @@ struct TwoValues { U value2; }; + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { +namespace { + +using absl::test_internal::CopyableMovableInstance; +using absl::test_internal::InstanceTracker; + TEST(CompressedTupleTest, Sizeof) { EXPECT_EQ(sizeof(int), sizeof(CompressedTuple)); EXPECT_EQ(sizeof(int), sizeof(CompressedTuple>)); @@ -53,6 +70,141 @@ TEST(CompressedTupleTest, Sizeof) { sizeof(CompressedTuple, NotEmpty, Empty<1>>)); } +TEST(CompressedTupleTest, OneMoveOnRValueConstructionTemp) { + InstanceTracker tracker; + CompressedTuple x1(CopyableMovableInstance(1)); + EXPECT_EQ(tracker.instances(), 1); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_LE(tracker.moves(), 1); + EXPECT_EQ(x1.get<0>().value(), 1); +} + +TEST(CompressedTupleTest, OneMoveOnRValueConstructionMove) { + InstanceTracker tracker; + + CopyableMovableInstance i1(1); + CompressedTuple x1(std::move(i1)); + EXPECT_EQ(tracker.instances(), 2); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_LE(tracker.moves(), 1); + EXPECT_EQ(x1.get<0>().value(), 1); +} + +TEST(CompressedTupleTest, OneMoveOnRValueConstructionMixedTypes) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + CopyableMovableInstance i2(2); + Empty<0> empty; + CompressedTuple> + x1(std::move(i1), i2, empty); + EXPECT_EQ(x1.get<0>().value(), 1); + EXPECT_EQ(x1.get<1>().value(), 2); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); +} + +struct IncompleteType; +CompressedTuple> +MakeWithIncomplete(CopyableMovableInstance i1, + IncompleteType& t, // NOLINT + Empty<0> empty) { + return CompressedTuple>{ + std::move(i1), t, empty}; +} + +struct IncompleteType {}; +TEST(CompressedTupleTest, OneMoveOnRValueConstructionWithIncompleteType) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + Empty<0> empty; + struct DerivedType : IncompleteType {int value = 0;}; + DerivedType fd; + fd.value = 7; + + CompressedTuple> x1 = + MakeWithIncomplete(std::move(i1), fd, empty); + + EXPECT_EQ(x1.get<0>().value(), 1); + EXPECT_EQ(static_cast(x1.get<1>()).value, 7); + + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 2); +} + +TEST(CompressedTupleTest, + OneMoveOnRValueConstructionMixedTypes_BraceInitPoisonPillExpected) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + CopyableMovableInstance i2(2); + CompressedTuple> + x1(std::move(i1), i2, {}); // NOLINT + EXPECT_EQ(x1.get<0>().value(), 1); + EXPECT_EQ(x1.get<1>().value(), 2); + EXPECT_EQ(tracker.instances(), 3); + // We are forced into the `const Ts&...` constructor (invoking copies) + // because we need it to deduce the type of `{}`. + // std::tuple also has this behavior. + // Note, this test is proof that this is expected behavior, but it is not + // _desired_ behavior. + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 0); +} + +TEST(CompressedTupleTest, OneCopyOnLValueConstruction) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + + CompressedTuple x1(i1); + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 0); + + tracker.ResetCopiesMovesSwaps(); + + CopyableMovableInstance i2(2); + const CopyableMovableInstance& i2_ref = i2; + CompressedTuple x2(i2_ref); + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 0); +} + +TEST(CompressedTupleTest, OneMoveOnRValueAccess) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + CompressedTuple x(std::move(i1)); + tracker.ResetCopiesMovesSwaps(); + + CopyableMovableInstance i2 = std::move(x).get<0>(); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); +} + +TEST(CompressedTupleTest, OneCopyOnLValueAccess) { + InstanceTracker tracker; + + CompressedTuple x(CopyableMovableInstance(0)); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); + + CopyableMovableInstance t = x.get<0>(); + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 1); +} + +TEST(CompressedTupleTest, ZeroCopyOnRefAccess) { + InstanceTracker tracker; + + CompressedTuple x(CopyableMovableInstance(0)); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); + + CopyableMovableInstance& t1 = x.get<0>(); + const CopyableMovableInstance& t2 = x.get<0>(); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); + EXPECT_EQ(t1.value(), 0); + EXPECT_EQ(t2.value(), 0); +} + TEST(CompressedTupleTest, Access) { struct S { std::string x; @@ -113,9 +265,14 @@ TEST(CompressedTupleTest, Nested) { EXPECT_EQ(4 * sizeof(char), sizeof(CompressedTuple, CompressedTuple>)); - EXPECT_TRUE( - (std::is_empty>, - CompressedTuple>>>::value)); + EXPECT_TRUE((std::is_empty, Empty<1>>>::value)); + + // Make sure everything still works when things are nested. + struct CT_Empty : CompressedTuple> {}; + CompressedTuple, CT_Empty> nested_empty; + auto contained = nested_empty.get<0>(); + auto nested = nested_empty.get<1>().get<0>(); + EXPECT_TRUE((std::is_same::value)); } TEST(CompressedTupleTest, Reference) { @@ -141,15 +298,103 @@ TEST(CompressedTupleTest, NoElements) { EXPECT_TRUE(std::is_empty>::value); } +TEST(CompressedTupleTest, MoveOnlyElements) { + CompressedTuple> str_tup( + absl::make_unique("str")); + + CompressedTuple>, + std::unique_ptr> + x(std::move(str_tup), absl::make_unique(5)); + + EXPECT_EQ(*x.get<0>().get<0>(), "str"); + EXPECT_EQ(*x.get<1>(), 5); + + std::unique_ptr x0 = std::move(x.get<0>()).get<0>(); + std::unique_ptr x1 = std::move(x).get<1>(); + + EXPECT_EQ(*x0, "str"); + EXPECT_EQ(*x1, 5); +} + +TEST(CompressedTupleTest, MoveConstructionMoveOnlyElements) { + CompressedTuple> base( + absl::make_unique("str")); + EXPECT_EQ(*base.get<0>(), "str"); + + CompressedTuple> copy(std::move(base)); + EXPECT_EQ(*copy.get<0>(), "str"); +} + +TEST(CompressedTupleTest, AnyElements) { + any a(std::string("str")); + CompressedTuple x(any(5), a); + EXPECT_EQ(absl::any_cast(x.get<0>()), 5); + EXPECT_EQ(absl::any_cast(x.get<1>()), "str"); + + a = 0.5f; + EXPECT_EQ(absl::any_cast(x.get<1>()), 0.5); + + // Ensure copy construction work in the face of a type with a universal + // implicit constructor; + CompressedTuple c{}, d(c); // NOLINT +} + TEST(CompressedTupleTest, Constexpr) { - constexpr CompressedTuple> x( - 7, 1.25, CompressedTuple(5)); + struct NonTrivialStruct { + constexpr NonTrivialStruct() = default; + constexpr int value() const { return v; } + int v = 5; + }; + struct TrivialStruct { + TrivialStruct() = default; + constexpr int value() const { return v; } + int v; + }; + constexpr CompressedTuple, Empty<0>> x( + 7, 1.25, CompressedTuple(5), {}); constexpr int x0 = x.get<0>(); constexpr double x1 = x.get<1>(); constexpr int x2 = x.get<2>().get<0>(); + constexpr CallType x3 = x.get<3>().value(); + EXPECT_EQ(x0, 7); EXPECT_EQ(x1, 1.25); EXPECT_EQ(x2, 5); + EXPECT_EQ(x3, CallType::kConstRef); + +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 + constexpr CompressedTuple, TrivialStruct, int> trivial = {}; + constexpr CallType trivial0 = trivial.get<0>().value(); + constexpr int trivial1 = trivial.get<1>().value(); + constexpr int trivial2 = trivial.get<2>(); + + EXPECT_EQ(trivial0, CallType::kConstRef); + EXPECT_EQ(trivial1, 0); + EXPECT_EQ(trivial2, 0); +#endif + + constexpr CompressedTuple, NonTrivialStruct, absl::optional> + non_trivial = {}; + constexpr CallType non_trivial0 = non_trivial.get<0>().value(); + constexpr int non_trivial1 = non_trivial.get<1>().value(); + constexpr absl::optional non_trivial2 = non_trivial.get<2>(); + + EXPECT_EQ(non_trivial0, CallType::kConstRef); + EXPECT_EQ(non_trivial1, 5); + EXPECT_EQ(non_trivial2, absl::nullopt); + + static constexpr char data[] = "DEF"; + constexpr CompressedTuple z(data); + constexpr const char* z1 = z.get<0>(); + EXPECT_EQ(std::string(z1), std::string(data)); + +#if defined(__clang__) + // An apparent bug in earlier versions of gcc claims these are ambiguous. + constexpr int x2m = absl::move(x.get<2>()).get<0>(); + constexpr CallType x3m = absl::move(x).get<3>().value(); + EXPECT_EQ(x2m, 5); + EXPECT_EQ(x3m, CallType::kConstMove); +#endif } #if defined(__clang__) || defined(__GNUC__) @@ -164,5 +409,5 @@ TEST(CompressedTupleTest, EmptyFinalClass) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index ddccbe05..eb6d7eb7 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -34,7 +34,7 @@ #include "absl/utility/utility.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // Allocates at least n bytes aligned to the specified alignment. @@ -287,13 +287,48 @@ struct IsLayoutCompatible { } // namespace memory_internal -// If kMutableKeys is false, only the value member is accessed. +// The internal storage type for key-value containers like flat_hash_map. // -// If kMutableKeys is true, key is accessed through all slots while value and -// mutable_value are accessed only via INITIALIZED slots. Slots are created and -// destroyed via mutable_value so that the key can be moved later. +// It is convenient for the value_type of a flat_hash_map to be +// pair; the "const K" prevents accidental modification of the key +// when dealing with the reference returned from find() and similar methods. +// However, this creates other problems; we want to be able to emplace(K, V) +// efficiently with move operations, and similarly be able to move a +// pair in insert(). +// +// The solution is this union, which aliases the const and non-const versions +// of the pair. This also allows flat_hash_map to work, even though +// that has the same efficiency issues with move in emplace() and insert() - +// but people do it anyway. +// +// If kMutableKeys is false, only the value member can be accessed. +// +// If kMutableKeys is true, key can be accessed through all slots while value +// and mutable_value must be accessed only via INITIALIZED slots. Slots are +// created and destroyed via mutable_value so that the key can be moved later. +// +// Accessing one of the union fields while the other is active is safe as +// long as they are layout-compatible, which is guaranteed by the definition of +// kMutableKeys. For C++11, the relevant section of the standard is +// https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19) template -union slot_type { +union map_slot_type { + map_slot_type() {} + ~map_slot_type() = delete; + using value_type = std::pair; + using mutable_value_type = std::pair; + + value_type value; + mutable_value_type mutable_value; + K key; +}; + +template +struct map_slot_policy { + using slot_type = map_slot_type; + using value_type = std::pair; + using mutable_value_type = std::pair; + private: static void emplace(slot_type* slot) { // The construction of union doesn't do anything at runtime but it allows us @@ -303,19 +338,17 @@ union slot_type { // If pair and pair are layout-compatible, we can accept one // or the other via slot_type. We are also free to access the key via // slot_type::key in this case. - using kMutableKeys = - std::integral_constant::value>; + using kMutableKeys = memory_internal::IsLayoutCompatible; public: - slot_type() {} - ~slot_type() = delete; - using value_type = std::pair; - using mutable_value_type = std::pair; + static value_type& element(slot_type* slot) { return slot->value; } + static const value_type& element(const slot_type* slot) { + return slot->value; + } - value_type value; - mutable_value_type mutable_value; - K key; + static const K& key(const slot_type* slot) { + return kMutableKeys::value ? slot->key : slot->value.first; + } template static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { @@ -401,7 +434,7 @@ union slot_type { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 da87ca20..ea9568dc 100644 --- a/absl/container/internal/container_memory_test.cc +++ b/absl/container/internal/container_memory_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -186,5 +186,5 @@ TEST(DecomposePair, NotDecomposable) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/counting_allocator.h b/absl/container/internal/counting_allocator.h new file mode 100644 index 00000000..94a457ca --- /dev/null +++ b/absl/container/internal/counting_allocator.h @@ -0,0 +1,81 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ +#define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ + +#include +#include +#include + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +// This is a stateful allocator, but the state lives outside of the +// allocator (in whatever test is using the allocator). This is odd +// but helps in tests where the allocator is propagated into nested +// containers - that chain of allocators uses the same state and is +// thus easier to query for aggregate allocation information. +template +class CountingAllocator : public std::allocator { + public: + using Alloc = std::allocator; + using pointer = typename Alloc::pointer; + using size_type = typename Alloc::size_type; + + CountingAllocator() : bytes_used_(nullptr) {} + explicit CountingAllocator(int64_t* b) : bytes_used_(b) {} + + template + CountingAllocator(const CountingAllocator& x) + : Alloc(x), bytes_used_(x.bytes_used_) {} + + pointer allocate(size_type n, + std::allocator::const_pointer hint = nullptr) { + assert(bytes_used_ != nullptr); + *bytes_used_ += n * sizeof(T); + return Alloc::allocate(n, hint); + } + + void deallocate(pointer p, size_type n) { + Alloc::deallocate(p, n); + assert(bytes_used_ != nullptr); + *bytes_used_ -= n * sizeof(T); + } + + template + class rebind { + public: + using other = CountingAllocator; + }; + + friend bool operator==(const CountingAllocator& a, + const CountingAllocator& b) { + return a.bytes_used_ == b.bytes_used_; + } + + friend bool operator!=(const CountingAllocator& a, + const CountingAllocator& b) { + return !(a == b); + } + + int64_t* bytes_used_; +}; + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // 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 72c75fa0..2155076d 100644 --- a/absl/container/internal/hash_function_defaults.h +++ b/absl/container/internal/hash_function_defaults.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -39,8 +39,8 @@ // equal functions are still bound to T. This is important because some type U // can be hashed by/tested for equality differently depending on T. A notable // example is `const char*`. `const char*` is treated as a c-style string when -// the hash function is hash but as a pointer when the hash function is -// hash. +// the hash function is hash but as a pointer when the hash +// function is hash. // #ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ #define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ @@ -56,7 +56,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // The hash of an object of type T is computed by using absl::Hash. @@ -84,6 +84,7 @@ struct StringHashEq { } }; }; + template <> struct HashEq : StringHashEq {}; template <> @@ -139,7 +140,7 @@ template using hash_default_eq = typename container_internal::HashEq::Eq; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 4610843a..ce6133f8 100644 --- a/absl/container/internal/hash_function_defaults_test.cc +++ b/absl/container/internal/hash_function_defaults_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -78,14 +78,14 @@ struct EqString : ::testing::Test { hash_default_eq key_eq; }; -TYPED_TEST_CASE(EqString, StringTypes); +TYPED_TEST_SUITE(EqString, StringTypes); template struct HashString : ::testing::Test { hash_default_hash hasher; }; -TYPED_TEST_CASE(HashString, StringTypes); +TYPED_TEST_SUITE(HashString, StringTypes); TYPED_TEST(EqString, Works) { auto eq = this->key_eq; @@ -122,14 +122,14 @@ struct EqPointer : ::testing::Test { hash_default_eq key_eq; }; -TYPED_TEST_CASE(EqPointer, PointerTypes); +TYPED_TEST_SUITE(EqPointer, PointerTypes); template struct HashPointer : ::testing::Test { hash_default_hash hasher; }; -TYPED_TEST_CASE(HashPointer, PointerTypes); +TYPED_TEST_SUITE(HashPointer, PointerTypes); TYPED_TEST(EqPointer, Works) { int dummy; @@ -203,15 +203,11 @@ TYPED_TEST(HashPointer, Works) { EXPECT_NE(hash(&dummy), hash(cuptr)); } -// Cartesian product of (string, std::string, absl::string_view) -// with (string, std::string, absl::string_view, const char*). +// Cartesian product of (std::string, absl::string_view) +// with (std::string, absl::string_view, const char*). using StringTypesCartesianProduct = Types< // clang-format off - std::pair, - std::pair, - std::pair, - std::pair, std::pair, std::pair>; @@ -249,11 +245,11 @@ TYPED_TEST_P(StringLikeTest, HashEq) { EXPECT_NE(this->hash(this->a1), this->hash(this->b2)); } -TYPED_TEST_CASE(StringLikeTest, StringTypesCartesianProduct); +TYPED_TEST_SUITE(StringLikeTest, StringTypesCartesianProduct); } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl enum Hash : size_t { @@ -284,7 +280,7 @@ struct hash> { } // namespace std namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -299,5 +295,5 @@ TEST(Delegate, HashDispatch) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc index aef41d72..36b2571b 100644 --- a/absl/container/internal/hash_generator_testing.cc +++ b/absl/container/internal/hash_generator_testing.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace hash_internal { namespace { @@ -70,5 +70,5 @@ absl::string_view Generator::operator()() const { } // namespace hash_internal } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h index 65e88964..27962c35 100644 --- a/absl/container/internal/hash_generator_testing.h +++ b/absl/container/internal/hash_generator_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -31,7 +31,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace hash_internal { namespace generator_internal { @@ -146,7 +146,7 @@ using GeneratedType = decltype( } // namespace hash_internal } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 9c310ad4..8f0d2a52 100644 --- a/absl/container/internal/hash_policy_testing.h +++ b/absl/container/internal/hash_policy_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,7 +30,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace hash_testing_internal { @@ -163,7 +163,7 @@ auto keys(const Set& s) } } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 00c436b3..8fd1df00 100644 --- a/absl/container/internal/hash_policy_testing_test.cc +++ b/absl/container/internal/hash_policy_testing_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -41,5 +41,5 @@ TEST(_, Hash) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/hash_policy_traits.h b/absl/container/internal/hash_policy_traits.h index 41e26212..3d87e821 100644 --- a/absl/container/internal/hash_policy_traits.h +++ b/absl/container/internal/hash_policy_traits.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // Defines how slots are initialized/destroyed/moved. @@ -185,7 +185,7 @@ struct hash_policy_traits { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 07cecdfa..edfaf63e 100644 --- a/absl/container/internal/hash_policy_traits_test.cc +++ b/absl/container/internal/hash_policy_traits_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -140,5 +140,5 @@ TEST_F(Test, with_transfer) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/hashtable_debug.h b/absl/container/internal/hashtable_debug.h index b6a43512..1d1a9c28 100644 --- a/absl/container/internal/hashtable_debug.h +++ b/absl/container/internal/hashtable_debug.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,7 +38,7 @@ #include "absl/container/internal/hashtable_debug_hooks.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // Returns the number of probes required to lookup `key`. Returns 0 for a @@ -61,7 +61,7 @@ std::vector GetHashtableDebugNumProbesHistogram(const C& container) { size_t num_probes = GetHashtableDebugNumProbes( container, absl::container_internal::hashtable_debug_internal::GetKey(*it, 0)); - v.resize(std::max(v.size(), num_probes + 1)); + v.resize((std::max)(v.size(), num_probes + 1)); v[num_probes]++; } return v; @@ -104,7 +104,7 @@ size_t LowerBoundAllocatedByteSize(size_t num_elements) { } } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 50ba6ba5..7b95fcef 100644 --- a/absl/container/internal/hashtable_debug_hooks.h +++ b/absl/container/internal/hashtable_debug_hooks.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace hashtable_debug_internal { @@ -77,7 +77,7 @@ struct HashtableDebugAccess { } // namespace hashtable_debug_internal } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 new file mode 100644 index 00000000..2338045d --- /dev/null +++ b/absl/container/internal/hashtablez_sampler.cc @@ -0,0 +1,310 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include +#include +#include +#include +#include + +#include "absl/base/attributes.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 { +namespace container_internal { +constexpr int HashtablezInfo::kMaxStackDepth; + +namespace { +ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ + false +}; +ABSL_CONST_INIT std::atomic g_hashtablez_sample_parameter{1 << 10}; +ABSL_CONST_INIT std::atomic 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 global_rand(0); + uint64_t r = reinterpret_cast(&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(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(std::numeric_limits::max() / 2)) { + return std::numeric_limits::max() / 2; + } + + // Small values of interval are equivalent to just sampling next time. + if (interval < 1) { + return 1; + } + return static_cast(interval); +} + +} // namespace + +HashtablezSampler& HashtablezSampler::Global() { + static auto* sampler = new HashtablezSampler(); + return *sampler; +} + +HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback( + DisposeCallback f) { + return dispose_.exchange(f, std::memory_order_relaxed); +} + +HashtablezInfo::HashtablezInfo() { PrepareForSampling(); } +HashtablezInfo::~HashtablezInfo() = default; + +void HashtablezInfo::PrepareForSampling() { + capacity.store(0, std::memory_order_relaxed); + size.store(0, std::memory_order_relaxed); + num_erases.store(0, std::memory_order_relaxed); + max_probe_length.store(0, std::memory_order_relaxed); + total_probe_length.store(0, std::memory_order_relaxed); + hashes_bitwise_or.store(0, std::memory_order_relaxed); + hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed); + + create_time = absl::Now(); + // The inliner makes hardcoded skip_count difficult (especially when combined + // with LTO). We use the ability to exclude stacks by regex when encoding + // instead. + depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth, + /* skip_count= */ 0); + dead = nullptr; +} + +HashtablezSampler::HashtablezSampler() + : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) { + absl::MutexLock l(&graveyard_.init_mu); + graveyard_.dead = &graveyard_; +} + +HashtablezSampler::~HashtablezSampler() { + HashtablezInfo* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + HashtablezInfo* next = s->next; + delete s; + s = next; + } +} + +void HashtablezSampler::PushNew(HashtablezInfo* sample) { + sample->next = all_.load(std::memory_order_relaxed); + while (!all_.compare_exchange_weak(sample->next, sample, + std::memory_order_release, + std::memory_order_relaxed)) { + } +} + +void HashtablezSampler::PushDead(HashtablezInfo* sample) { + if (auto* dispose = dispose_.load(std::memory_order_relaxed)) { + dispose(*sample); + } + + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + absl::MutexLock sample_lock(&sample->init_mu); + sample->dead = graveyard_.dead; + graveyard_.dead = sample; +} + +HashtablezInfo* HashtablezSampler::PopDead() { + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + + // The list is circular, so eventually it collapses down to + // graveyard_.dead == &graveyard_ + // when it is empty. + HashtablezInfo* sample = graveyard_.dead; + if (sample == &graveyard_) return nullptr; + + absl::MutexLock sample_lock(&sample->init_mu); + graveyard_.dead = sample->dead; + sample->PrepareForSampling(); + return sample; +} + +HashtablezInfo* HashtablezSampler::Register() { + int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); + if (size > g_hashtablez_max_samples.load(std::memory_order_relaxed)) { + size_estimate_.fetch_sub(1, std::memory_order_relaxed); + dropped_samples_.fetch_add(1, std::memory_order_relaxed); + return nullptr; + } + + HashtablezInfo* sample = PopDead(); + if (sample == nullptr) { + // Resurrection failed. Hire a new warlock. + sample = new HashtablezInfo(); + PushNew(sample); + } + + return sample; +} + +void HashtablezSampler::Unregister(HashtablezInfo* sample) { + PushDead(sample); + size_estimate_.fetch_sub(1, std::memory_order_relaxed); +} + +int64_t HashtablezSampler::Iterate( + const std::function& f) { + HashtablezInfo* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + absl::MutexLock l(&s->init_mu); + if (s->dead == nullptr) { + f(*s); + } + s = s->next; + } + + return dropped_samples_.load(std::memory_order_relaxed); +} + +HashtablezInfo* SampleSlow(int64_t* next_sample) { + if (kAbslContainerInternalSampleEverything) { + *next_sample = 1; + return HashtablezSampler::Global().Register(); + } + + bool first = *next_sample < 0; + *next_sample = GetGeometricVariable( + g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); + + // 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 + // the default sampling rate. + if (!g_hashtablez_enabled.load(std::memory_order_relaxed)) return nullptr; + + // We will only be negative on our first count, so we should just retry in + // that case. + if (first) { + if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr; + return SampleSlow(next_sample); + } + + return HashtablezSampler::Global().Register(); +} + +#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); +} + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired) { + // SwissTables probe in groups of 16, so scale this to count items probes and + // not offset from desired. + size_t probe_length = distance_from_desired; +#if SWISSTABLE_HAVE_SSE2 + probe_length /= 16; +#else + probe_length /= 8; +#endif + + info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed); + info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed); + info->max_probe_length.store( + std::max(info->max_probe_length.load(std::memory_order_relaxed), + probe_length), + std::memory_order_relaxed); + info->total_probe_length.fetch_add(probe_length, std::memory_order_relaxed); + info->size.fetch_add(1, std::memory_order_relaxed); +} + +void SetHashtablezEnabled(bool enabled) { + g_hashtablez_enabled.store(enabled, std::memory_order_release); +} + +void SetHashtablezSampleParameter(int32_t rate) { + if (rate > 0) { + g_hashtablez_sample_parameter.store(rate, std::memory_order_release); + } else { + ABSL_RAW_LOG(ERROR, "Invalid hashtablez sample rate: %lld", + static_cast(rate)); // NOLINT(runtime/int) + } +} + +void SetHashtablezMaxSamples(int32_t max) { + if (max > 0) { + g_hashtablez_max_samples.store(max, std::memory_order_release); + } else { + ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld", + static_cast(max)); // NOLINT(runtime/int) + } +} + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h new file mode 100644 index 00000000..f17c425c --- /dev/null +++ b/absl/container/internal/hashtablez_sampler.h @@ -0,0 +1,290 @@ +// 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: hashtablez_sampler.h +// ----------------------------------------------------------------------------- +// +// This header file defines the API for a low level library to sample hashtables +// and collect runtime statistics about them. +// +// `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which +// store information about a single sample. +// +// `Record*` methods store information into samples. +// `Sample()` and `Unsample()` make use of a single global sampler with +// properties controlled by the flags hashtablez_enabled, +// hashtablez_sample_rate, and hashtablez_max_samples. +// +// WARNING +// +// Using this sampling API may cause sampled Swiss tables to use the global +// allocator (operator `new`) in addition to any custom allocator. If you +// are using a table in an unusual circumstance where allocation or calling a +// linux syscall is unacceptable, this could interfere. +// +// This utility is internal-only. Use at your own risk. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ + +#include +#include +#include +#include + +#include "absl/base/internal/per_thread_tls.h" +#include "absl/base/optimization.h" +#include "absl/container/internal/have_sse.h" +#include "absl/synchronization/mutex.h" +#include "absl/utility/utility.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +// Stores information about a sampled hashtable. All mutations to this *must* +// be made through `Record*` functions below. All reads from this *must* only +// occur in the callback to `HashtablezSampler::Iterate`. +struct HashtablezInfo { + // Constructs the object but does not fill in any fields. + HashtablezInfo(); + ~HashtablezInfo(); + HashtablezInfo(const HashtablezInfo&) = delete; + HashtablezInfo& operator=(const HashtablezInfo&) = delete; + + // 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); + + // These fields are mutated by the various Record* APIs and need to be + // thread-safe. + std::atomic capacity; + std::atomic size; + std::atomic num_erases; + std::atomic max_probe_length; + std::atomic total_probe_length; + std::atomic hashes_bitwise_or; + std::atomic hashes_bitwise_and; + + // `HashtablezSampler` maintains intrusive linked lists for all samples. See + // comments on `HashtablezSampler::all_` for details on these. `init_mu` + // guards the ability to restore the sample to a pristine state. This + // prevents races with sampling and resurrecting an object. + absl::Mutex init_mu; + HashtablezInfo* next; + HashtablezInfo* dead 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. + // These are guarded by init_mu, but that is not externalized to clients, who + // can only read them during `HashtablezSampler::Iterate` which will hold the + // lock. + static constexpr int kMaxStackDepth = 64; + absl::Time create_time; + int32_t depth; + void* stack[kMaxStackDepth]; +}; + +inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { +#if SWISSTABLE_HAVE_SSE2 + total_probe_length /= 16; +#else + total_probe_length /= 8; +#endif + info->total_probe_length.store(total_probe_length, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); +} + +inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, + size_t capacity) { + info->size.store(size, std::memory_order_relaxed); + info->capacity.store(capacity, std::memory_order_relaxed); + if (size == 0) { + // This is a clear, reset the total/num_erases too. + RecordRehashSlow(info, 0); + } +} + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired); + +inline void RecordEraseSlow(HashtablezInfo* info) { + info->size.fetch_sub(1, std::memory_order_relaxed); + info->num_erases.fetch_add(1, std::memory_order_relaxed); +} + +HashtablezInfo* SampleSlow(int64_t* next_sample); +void UnsampleSlow(HashtablezInfo* info); + +class HashtablezInfoHandle { + public: + explicit HashtablezInfoHandle() : info_(nullptr) {} + explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {} + ~HashtablezInfoHandle() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + UnsampleSlow(info_); + } + + HashtablezInfoHandle(const HashtablezInfoHandle&) = delete; + HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete; + + HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept + : info_(absl::exchange(o.info_, nullptr)) {} + HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept { + if (ABSL_PREDICT_FALSE(info_ != nullptr)) { + UnsampleSlow(info_); + } + info_ = absl::exchange(o.info_, nullptr); + return *this; + } + + inline void RecordStorageChanged(size_t size, size_t capacity) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordStorageChangedSlow(info_, size, capacity); + } + + inline void RecordRehash(size_t total_probe_length) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordRehashSlow(info_, total_probe_length); + } + + inline void RecordInsert(size_t hash, size_t distance_from_desired) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordInsertSlow(info_, hash, distance_from_desired); + } + + inline void RecordErase() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordEraseSlow(info_); + } + + friend inline void swap(HashtablezInfoHandle& lhs, + HashtablezInfoHandle& rhs) { + std::swap(lhs.info_, rhs.info_); + } + + private: + friend class HashtablezInfoHandlePeer; + HashtablezInfo* info_; +}; + +#if ABSL_PER_THREAD_TLS == 1 +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 (ABSL_PREDICT_TRUE(--global_next_sample > 0)) { + return HashtablezInfoHandle(nullptr); + } + return HashtablezInfoHandle(SampleSlow(&global_next_sample)); +} + +// Holds samples and their associated stack traces with a soft limit of +// `SetHashtablezMaxSamples()`. +// +// Thread safe. +class HashtablezSampler { + public: + // Returns a global Sampler. + static HashtablezSampler& Global(); + + HashtablezSampler(); + ~HashtablezSampler(); + + // Registers for sampling. Returns an opaque registration info. + HashtablezInfo* Register(); + + // Unregisters the sample. + void Unregister(HashtablezInfo* sample); + + // The dispose callback will be called on all samples the moment they are + // being unregistered. Only affects samples that are unregistered after the + // callback has been set. + // Returns the previous callback. + using DisposeCallback = void (*)(const HashtablezInfo&); + DisposeCallback SetDisposeCallback(DisposeCallback f); + + // Iterates over all the registered `StackInfo`s. Returning the number of + // samples that have been dropped. + int64_t Iterate(const std::function& f); + + private: + void PushNew(HashtablezInfo* sample); + void PushDead(HashtablezInfo* sample); + HashtablezInfo* PopDead(); + + std::atomic dropped_samples_; + std::atomic size_estimate_; + + // Intrusive lock free linked lists for tracking samples. + // + // `all_` records all samples (they are never removed from this list) and is + // terminated with a `nullptr`. + // + // `graveyard_.dead` is a circular linked list. When it is empty, + // `graveyard_.dead == &graveyard`. The list is circular so that + // every item on it (even the last) has a non-null dead pointer. This allows + // `Iterate` to determine if a given sample is live or dead using only + // information on the sample itself. + // + // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead + // looks like this (G is the Graveyard): + // + // +---+ +---+ +---+ +---+ +---+ + // all -->| A |--->| B |--->| C |--->| D |--->| E | + // | | | | | | | | | | + // +---+ | | +->| |-+ | | +->| |-+ | | + // | G | +---+ | +---+ | +---+ | +---+ | +---+ + // | | | | | | + // | | --------+ +--------+ | + // +---+ | + // ^ | + // +--------------------------------------+ + // + std::atomic all_; + HashtablezInfo graveyard_; + + std::atomic dispose_; +}; + +// Enables or disables sampling for Swiss tables. +void SetHashtablezEnabled(bool enabled); + +// Sets the rate at which Swiss tables will be sampled. +void SetHashtablezSampleParameter(int32_t rate); + +// Sets a soft max for the number of samples that will be kept. +void SetHashtablezMaxSamples(int32_t max); + +// Configuration override. +// This allows process-wide sampling without depending on order of +// 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; + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // 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 new file mode 100644 index 00000000..d3f41c7c --- /dev/null +++ b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc @@ -0,0 +1,29 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include "absl/base/attributes.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +// See hashtablez_sampler.h for details. +extern "C" ABSL_ATTRIBUTE_WEAK const bool + kAbslContainerInternalSampleEverything = false; + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc new file mode 100644 index 00000000..bdae75f3 --- /dev/null +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -0,0 +1,357 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/container/internal/have_sse.h" +#include "absl/synchronization/blocking_counter.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/mutex.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" + +#if SWISSTABLE_HAVE_SSE2 +constexpr int kProbeLength = 16; +#else +constexpr int kProbeLength = 8; +#endif + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { +class HashtablezInfoHandlePeer { + public: + static bool IsSampled(const HashtablezInfoHandle& h) { + return h.info_ != nullptr; + } + + static HashtablezInfo* GetInfo(HashtablezInfoHandle* h) { return h->info_; } +}; + +namespace { +using ::absl::synchronization_internal::ThreadPool; +using ::testing::IsEmpty; +using ::testing::UnorderedElementsAre; + +std::vector GetSizes(HashtablezSampler* s) { + std::vector res; + s->Iterate([&](const HashtablezInfo& info) { + res.push_back(info.size.load(std::memory_order_acquire)); + }); + return res; +} + +HashtablezInfo* Register(HashtablezSampler* s, size_t size) { + auto* info = s->Register(); + assert(info != nullptr); + info->size.store(size); + return info; +} + +TEST(HashtablezInfoTest, PrepareForSampling) { + absl::Time test_start = absl::Now(); + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + + EXPECT_EQ(info.capacity.load(), 0); + EXPECT_EQ(info.size.load(), 0); + EXPECT_EQ(info.num_erases.load(), 0); + EXPECT_EQ(info.max_probe_length.load(), 0); + EXPECT_EQ(info.total_probe_length.load(), 0); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0); + EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); + EXPECT_GE(info.create_time, test_start); + + info.capacity.store(1, std::memory_order_relaxed); + info.size.store(1, std::memory_order_relaxed); + info.num_erases.store(1, std::memory_order_relaxed); + info.max_probe_length.store(1, std::memory_order_relaxed); + info.total_probe_length.store(1, std::memory_order_relaxed); + info.hashes_bitwise_or.store(1, std::memory_order_relaxed); + info.hashes_bitwise_and.store(1, std::memory_order_relaxed); + info.create_time = test_start - absl::Hours(20); + + info.PrepareForSampling(); + EXPECT_EQ(info.capacity.load(), 0); + EXPECT_EQ(info.size.load(), 0); + EXPECT_EQ(info.num_erases.load(), 0); + EXPECT_EQ(info.max_probe_length.load(), 0); + EXPECT_EQ(info.total_probe_length.load(), 0); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0); + EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); + EXPECT_GE(info.create_time, test_start); +} + +TEST(HashtablezInfoTest, RecordStorageChanged) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + RecordStorageChangedSlow(&info, 17, 47); + EXPECT_EQ(info.size.load(), 17); + EXPECT_EQ(info.capacity.load(), 47); + RecordStorageChangedSlow(&info, 20, 20); + EXPECT_EQ(info.size.load(), 20); + EXPECT_EQ(info.capacity.load(), 20); +} + +TEST(HashtablezInfoTest, RecordInsert) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + EXPECT_EQ(info.max_probe_length.load(), 0); + RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); + EXPECT_EQ(info.max_probe_length.load(), 6); + EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00); + RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength); + EXPECT_EQ(info.max_probe_length.load(), 6); + EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00); + RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength); + EXPECT_EQ(info.max_probe_length.load(), 12); + EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00); +} + +TEST(HashtablezInfoTest, RecordErase) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + EXPECT_EQ(info.num_erases.load(), 0); + EXPECT_EQ(info.size.load(), 0); + RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); + EXPECT_EQ(info.size.load(), 1); + RecordEraseSlow(&info); + EXPECT_EQ(info.size.load(), 0); + EXPECT_EQ(info.num_erases.load(), 1); +} + +TEST(HashtablezInfoTest, RecordRehash) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + RecordInsertSlow(&info, 0x1, 0); + RecordInsertSlow(&info, 0x2, kProbeLength); + RecordInsertSlow(&info, 0x4, kProbeLength); + RecordInsertSlow(&info, 0x8, 2 * kProbeLength); + EXPECT_EQ(info.size.load(), 4); + EXPECT_EQ(info.total_probe_length.load(), 4); + + RecordEraseSlow(&info); + RecordEraseSlow(&info); + EXPECT_EQ(info.size.load(), 2); + EXPECT_EQ(info.total_probe_length.load(), 4); + EXPECT_EQ(info.num_erases.load(), 2); + + RecordRehashSlow(&info, 3 * kProbeLength); + EXPECT_EQ(info.size.load(), 2); + EXPECT_EQ(info.total_probe_length.load(), 3); + EXPECT_EQ(info.num_erases.load(), 0); +} + +TEST(HashtablezSamplerTest, SmallSampleParameter) { + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(100); + + for (int i = 0; i < 1000; ++i) { + int64_t next_sample = 0; + HashtablezInfo* sample = SampleSlow(&next_sample); + EXPECT_GT(next_sample, 0); + EXPECT_NE(sample, nullptr); + UnsampleSlow(sample); + } +} + +TEST(HashtablezSamplerTest, LargeSampleParameter) { + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(std::numeric_limits::max()); + + for (int i = 0; i < 1000; ++i) { + int64_t next_sample = 0; + HashtablezInfo* sample = SampleSlow(&next_sample); + EXPECT_GT(next_sample, 0); + EXPECT_NE(sample, nullptr); + UnsampleSlow(sample); + } +} + +TEST(HashtablezSamplerTest, Sample) { + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(100); + int64_t num_sampled = 0; + int64_t total = 0; + double sample_rate = 0.0; + for (int i = 0; i < 1000000; ++i) { + HashtablezInfoHandle h = Sample(); + ++total; + if (HashtablezInfoHandlePeer::IsSampled(h)) { + ++num_sampled; + } + sample_rate = static_cast(num_sampled) / total; + if (0.005 < sample_rate && sample_rate < 0.015) break; + } + EXPECT_NEAR(sample_rate, 0.01, 0.005); +} + +TEST(HashtablezSamplerTest, Handle) { + auto& sampler = HashtablezSampler::Global(); + HashtablezInfoHandle h(sampler.Register()); + auto* info = HashtablezInfoHandlePeer::GetInfo(&h); + info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed); + + bool found = false; + sampler.Iterate([&](const HashtablezInfo& h) { + if (&h == info) { + EXPECT_EQ(h.hashes_bitwise_and.load(), 0x12345678); + found = true; + } + }); + EXPECT_TRUE(found); + + h = HashtablezInfoHandle(); + found = false; + sampler.Iterate([&](const HashtablezInfo& h) { + if (&h == info) { + // this will only happen if some other thread has resurrected the info + // the old handle was using. + if (h.hashes_bitwise_and.load() == 0x12345678) { + found = true; + } + } + }); + EXPECT_FALSE(found); +} + +TEST(HashtablezSamplerTest, Registration) { + HashtablezSampler sampler; + auto* info1 = Register(&sampler, 1); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1)); + + auto* info2 = Register(&sampler, 2); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2)); + info1->size.store(3); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2)); + + sampler.Unregister(info1); + sampler.Unregister(info2); +} + +TEST(HashtablezSamplerTest, Unregistration) { + HashtablezSampler sampler; + std::vector infos; + for (size_t i = 0; i < 3; ++i) { + infos.push_back(Register(&sampler, i)); + } + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2)); + + sampler.Unregister(infos[1]); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2)); + + infos.push_back(Register(&sampler, 3)); + infos.push_back(Register(&sampler, 4)); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4)); + sampler.Unregister(infos[3]); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4)); + + sampler.Unregister(infos[0]); + sampler.Unregister(infos[2]); + sampler.Unregister(infos[4]); + EXPECT_THAT(GetSizes(&sampler), IsEmpty()); +} + +TEST(HashtablezSamplerTest, MultiThreaded) { + HashtablezSampler sampler; + Notification stop; + ThreadPool pool(10); + + for (int i = 0; i < 10; ++i) { + pool.Schedule([&sampler, &stop]() { + std::random_device rd; + std::mt19937 gen(rd()); + + std::vector infoz; + while (!stop.HasBeenNotified()) { + if (infoz.empty()) { + infoz.push_back(sampler.Register()); + } + switch (std::uniform_int_distribution<>(0, 2)(gen)) { + case 0: { + infoz.push_back(sampler.Register()); + break; + } + case 1: { + size_t p = + std::uniform_int_distribution<>(0, infoz.size() - 1)(gen); + HashtablezInfo* info = infoz[p]; + infoz[p] = infoz.back(); + infoz.pop_back(); + sampler.Unregister(info); + break; + } + case 2: { + absl::Duration oldest = absl::ZeroDuration(); + sampler.Iterate([&](const HashtablezInfo& info) { + oldest = std::max(oldest, absl::Now() - info.create_time); + }); + ASSERT_GE(oldest, absl::ZeroDuration()); + break; + } + } + } + }); + } + // The threads will hammer away. Give it a little bit of time for tsan to + // spot errors. + absl::SleepFor(absl::Seconds(3)); + stop.Notify(); +} + +TEST(HashtablezSamplerTest, Callback) { + HashtablezSampler sampler; + + auto* info1 = Register(&sampler, 1); + auto* info2 = Register(&sampler, 2); + + static const HashtablezInfo* expected; + + auto callback = [](const HashtablezInfo& info) { + // We can't use `info` outside of this callback because the object will be + // disposed as soon as we return from here. + EXPECT_EQ(&info, expected); + }; + + // Set the callback. + EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr); + expected = info1; + sampler.Unregister(info1); + + // Unset the callback. + EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr)); + expected = nullptr; // no more calls. + sampler.Unregister(info2); +} + +} // namespace +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/container/internal/have_sse.h b/absl/container/internal/have_sse.h new file mode 100644 index 00000000..43414418 --- /dev/null +++ b/absl/container/internal/have_sse.h @@ -0,0 +1,49 @@ +// 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. +// +// Shared config probing for SSE instructions used in Swiss tables. +#ifndef ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ +#define ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ + +#ifndef SWISSTABLE_HAVE_SSE2 +#if defined(__SSE2__) || \ + (defined(_MSC_VER) && \ + (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2))) +#define SWISSTABLE_HAVE_SSE2 1 +#else +#define SWISSTABLE_HAVE_SSE2 0 +#endif +#endif + +#ifndef SWISSTABLE_HAVE_SSSE3 +#ifdef __SSSE3__ +#define SWISSTABLE_HAVE_SSSE3 1 +#else +#define SWISSTABLE_HAVE_SSSE3 0 +#endif +#endif + +#if SWISSTABLE_HAVE_SSSE3 && !SWISSTABLE_HAVE_SSE2 +#error "Bad configuration!" +#endif + +#if SWISSTABLE_HAVE_SSE2 +#include +#endif + +#if SWISSTABLE_HAVE_SSSE3 +#include +#endif + +#endif // ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h new file mode 100644 index 00000000..123e04c9 --- /dev/null +++ b/absl/container/internal/inlined_vector.h @@ -0,0 +1,895 @@ +// 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_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ +#define ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/types/span.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace inlined_vector_internal { + +template +using IsAtLeastForwardIterator = std::is_convertible< + typename std::iterator_traits::iterator_category, + std::forward_iterator_tag>; + +template +using IsMemcpyOk = absl::conjunction< + std::is_same, + AllocatorType>, + absl::is_trivially_copy_constructible, + absl::is_trivially_copy_assignable, + absl::is_trivially_destructible>; + +template +void DestroyElements(AllocatorType* alloc_ptr, ValueType* destroy_first, + SizeType destroy_size) { + using AllocatorTraits = absl::allocator_traits; + + if (destroy_first != nullptr) { + for (auto i = destroy_size; i != 0;) { + --i; + 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(destroy_first); + auto memory_size = sizeof(ValueType) * destroy_size; + std::memset(memory_ptr, 0xab, memory_size); +#endif // NDEBUG + } +} + +template +void ConstructElements(AllocatorType* alloc_ptr, ValueType* construct_first, + ValueAdapter* values_ptr, SizeType construct_size) { + for (SizeType i = 0; i < construct_size; ++i) { + ABSL_INTERNAL_TRY { + values_ptr->ConstructNext(alloc_ptr, construct_first + i); + } + ABSL_INTERNAL_CATCH_ANY { + inlined_vector_internal::DestroyElements(alloc_ptr, construct_first, i); + ABSL_INTERNAL_RETHROW; + } + } +} + +template +void AssignElements(ValueType* assign_first, ValueAdapter* values_ptr, + SizeType assign_size) { + for (SizeType i = 0; i < assign_size; ++i) { + values_ptr->AssignNext(assign_first + i); + } +} + +template +struct StorageView { + using pointer = typename AllocatorType::pointer; + using size_type = typename AllocatorType::size_type; + + pointer data; + size_type size; + size_type capacity; +}; + +template +class IteratorValueAdapter { + using pointer = typename AllocatorType::pointer; + using AllocatorTraits = absl::allocator_traits; + + public: + explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} + + void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at, *it_); + ++it_; + } + + void AssignNext(pointer assign_at) { + *assign_at = *it_; + ++it_; + } + + private: + Iterator it_; +}; + +template +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; + + public: + explicit CopyValueAdapter(const_reference v) : ptr_(std::addressof(v)) {} + + void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_); + } + + void AssignNext(pointer assign_at) { *assign_at = *ptr_; } + + private: + const_pointer ptr_; +}; + +template +class DefaultValueAdapter { + using pointer = typename AllocatorType::pointer; + using value_type = typename AllocatorType::value_type; + using AllocatorTraits = absl::allocator_traits; + + public: + explicit DefaultValueAdapter() {} + + void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at); + } + + void AssignNext(pointer assign_at) { *assign_at = value_type(); } +}; + +template +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; + + public: + explicit AllocationTransaction(AllocatorType* alloc_ptr) + : alloc_data_(*alloc_ptr, nullptr) {} + + ~AllocationTransaction() { + if (DidAllocate()) { + AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity()); + } + } + + AllocationTransaction(const AllocationTransaction&) = delete; + void operator=(const AllocationTransaction&) = delete; + + AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } + pointer& GetData() { return alloc_data_.template get<1>(); } + size_type& GetCapacity() { return capacity_; } + + bool DidAllocate() { return GetData() != nullptr; } + pointer Allocate(size_type capacity) { + GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); + GetCapacity() = capacity; + return GetData(); + } + + private: + container_internal::CompressedTuple alloc_data_; + size_type capacity_ = 0; +}; + +template +class ConstructionTransaction { + using pointer = typename AllocatorType::pointer; + using size_type = typename AllocatorType::size_type; + + public: + explicit ConstructionTransaction(AllocatorType* alloc_ptr) + : alloc_data_(*alloc_ptr, nullptr) {} + + ~ConstructionTransaction() { + if (DidConstruct()) { + inlined_vector_internal::DestroyElements(std::addressof(GetAllocator()), + GetData(), GetSize()); + } + } + + ConstructionTransaction(const ConstructionTransaction&) = delete; + void operator=(const ConstructionTransaction&) = delete; + + AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } + pointer& GetData() { return alloc_data_.template get<1>(); } + size_type& GetSize() { return size_; } + + bool DidConstruct() { return GetData() != nullptr; } + template + void Construct(pointer data, ValueAdapter* values_ptr, size_type size) { + inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()), + data, values_ptr, size); + GetData() = data; + GetSize() = size; + } + void Commit() { + GetData() = nullptr; + GetSize() = 0; + } + + private: + container_internal::CompressedTuple alloc_data_; + size_type size_ = 0; +}; + +template +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 iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using MoveIterator = std::move_iterator; + using AllocatorTraits = absl::allocator_traits; + using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk; + + using StorageView = inlined_vector_internal::StorageView; + + template + using IteratorValueAdapter = + inlined_vector_internal::IteratorValueAdapter; + using CopyValueAdapter = + inlined_vector_internal::CopyValueAdapter; + using DefaultValueAdapter = + inlined_vector_internal::DefaultValueAdapter; + + using AllocationTransaction = + inlined_vector_internal::AllocationTransaction; + using ConstructionTransaction = + inlined_vector_internal::ConstructionTransaction; + + static size_type NextCapacity(size_type current_capacity) { + return current_capacity * 2; + } + + static size_type ComputeCapacity(size_type current_capacity, + size_type requested_capacity) { + return (std::max)(NextCapacity(current_capacity), requested_capacity); + } + + // --------------------------------------------------------------------------- + // Storage Constructors and Destructor + // --------------------------------------------------------------------------- + + Storage() : metadata_() {} + + explicit Storage(const allocator_type& alloc) + : metadata_(alloc, /* empty and inlined */ 0) {} + + ~Storage() { + pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); + inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize()); + DeallocateIfAllocated(); + } + + // --------------------------------------------------------------------------- + // Storage Member Accessors + // --------------------------------------------------------------------------- + + size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } + + const size_type& GetSizeAndIsAllocated() const { + return metadata_.template get<1>(); + } + + size_type GetSize() const { return GetSizeAndIsAllocated() >> 1; } + + bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } + + pointer GetAllocatedData() { return data_.allocated.allocated_data; } + + const_pointer GetAllocatedData() const { + return data_.allocated.allocated_data; + } + + pointer GetInlinedData() { + return reinterpret_cast( + std::addressof(data_.inlined.inlined_data[0])); + } + + const_pointer GetInlinedData() const { + return reinterpret_cast( + std::addressof(data_.inlined.inlined_data[0])); + } + + size_type GetAllocatedCapacity() const { + return data_.allocated.allocated_capacity; + } + + size_type GetInlinedCapacity() const { return static_cast(N); } + + StorageView MakeStorageView() { + return GetIsAllocated() + ? StorageView{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()} + : StorageView{GetInlinedData(), GetSize(), GetInlinedCapacity()}; + } + + allocator_type* GetAllocPtr() { + return std::addressof(metadata_.template get<0>()); + } + + const allocator_type* GetAllocPtr() const { + return std::addressof(metadata_.template get<0>()); + } + + // --------------------------------------------------------------------------- + // Storage Member Mutators + // --------------------------------------------------------------------------- + + template + void Initialize(ValueAdapter values, size_type new_size); + + template + void Assign(ValueAdapter values, size_type new_size); + + template + void Resize(ValueAdapter values, size_type new_size); + + template + iterator Insert(const_iterator pos, ValueAdapter values, + size_type insert_count); + + template + reference EmplaceBack(Args&&... args); + + iterator Erase(const_iterator from, const_iterator to); + + void Reserve(size_type requested_capacity); + + void ShrinkToFit(); + + void Swap(Storage* other_storage_ptr); + + void SetIsAllocated() { + GetSizeAndIsAllocated() |= static_cast(1); + } + + void UnsetIsAllocated() { + GetSizeAndIsAllocated() &= ((std::numeric_limits::max)() - 1); + } + + void SetSize(size_type size) { + GetSizeAndIsAllocated() = + (size << 1) | static_cast(GetIsAllocated()); + } + + void SetAllocatedSize(size_type size) { + GetSizeAndIsAllocated() = (size << 1) | static_cast(1); + } + + void SetInlinedSize(size_type size) { + GetSizeAndIsAllocated() = size << static_cast(1); + } + + void AddSize(size_type count) { + GetSizeAndIsAllocated() += count << static_cast(1); + } + + void SubtractSize(size_type count) { + assert(count <= GetSize()); + + GetSizeAndIsAllocated() -= count << static_cast(1); + } + + void SetAllocatedData(pointer data, size_type capacity) { + data_.allocated.allocated_data = data; + data_.allocated.allocated_capacity = capacity; + } + + 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; + } + + void MemcpyFrom(const Storage& other_storage) { + assert(IsMemcpyOk::value || other_storage.GetIsAllocated()); + + GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); + data_ = other_storage.data_; + } + + void DeallocateIfAllocated() { + if (GetIsAllocated()) { + AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(), + GetAllocatedCapacity()); + } + } + + private: + using Metadata = + container_internal::CompressedTuple; + + struct Allocated { + pointer allocated_data; + size_type allocated_capacity; + }; + + struct Inlined { + using InlinedDataElement = + absl::aligned_storage_t; + InlinedDataElement inlined_data[N]; + }; + + union Data { + Allocated allocated; + Inlined inlined; + }; + + Metadata metadata_; + Data data_; +}; + +template +template +auto Storage::Initialize(ValueAdapter values, size_type new_size) + -> void { + // Only callable from constructors! + assert(!GetIsAllocated()); + 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); + SetIsAllocated(); + + construct_data = new_data; + } else { + construct_data = GetInlinedData(); + } + + inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, + &values, new_size); + + // Since the initial size was guaranteed to be `0` and the allocated bit is + // already correct for either case, *adding* `new_size` gives us the correct + // result faster than setting it directly. + AddSize(new_size); +} + +template +template +auto Storage::Assign(ValueAdapter values, size_type new_size) -> void { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocPtr()); + + absl::Span assign_loop; + absl::Span construct_loop; + absl::Span destroy_loop; + + if (new_size > storage_view.capacity) { + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + + construct_loop = {new_data, 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}; + construct_loop = {storage_view.data + storage_view.size, + new_size - storage_view.size}; + } else { + assign_loop = {storage_view.data, new_size}; + destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; + } + + inlined_vector_internal::AssignElements(assign_loop.data(), &values, + assign_loop.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), construct_loop.data(), &values, construct_loop.size()); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), + destroy_loop.size()); + + if (allocation_tx.DidAllocate()) { + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + SetSize(new_size); +} + +template +template +auto Storage::Resize(ValueAdapter values, size_type new_size) -> void { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocPtr()); + ConstructionTransaction construction_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + absl::Span construct_loop; + absl::Span move_construct_loop; + absl::Span destroy_loop; + + if (new_size > storage_view.capacity) { + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + construct_loop = {new_data + storage_view.size, + new_size - storage_view.size}; + move_construct_loop = {new_data, storage_view.size}; + destroy_loop = {storage_view.data, storage_view.size}; + } else if (new_size > storage_view.size) { + construct_loop = {storage_view.data + storage_view.size, + new_size - storage_view.size}; + } else { + destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; + } + + construction_tx.Construct(construct_loop.data(), &values, + construct_loop.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), move_construct_loop.data(), &move_values, + move_construct_loop.size()); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), + destroy_loop.size()); + + construction_tx.Commit(); + if (allocation_tx.DidAllocate()) { + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + SetSize(new_size); +} + +template +template +auto Storage::Insert(const_iterator pos, ValueAdapter values, + size_type insert_count) -> iterator { + StorageView storage_view = MakeStorageView(); + + size_type insert_index = + std::distance(const_iterator(storage_view.data), pos); + size_type insert_end_index = insert_index + insert_count; + size_type new_size = storage_view.size + insert_count; + + if (new_size > storage_view.capacity) { + AllocationTransaction allocation_tx(GetAllocPtr()); + ConstructionTransaction construction_tx(GetAllocPtr()); + ConstructionTransaction move_construciton_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + + construction_tx.Construct(new_data + insert_index, &values, insert_count); + + move_construciton_tx.Construct(new_data, &move_values, insert_index); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), new_data + insert_end_index, &move_values, + storage_view.size - insert_index); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + construction_tx.Commit(); + move_construciton_tx.Commit(); + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + + SetAllocatedSize(new_size); + return iterator(new_data + insert_index); + } else { + size_type move_construction_destination_index = + (std::max)(insert_end_index, storage_view.size); + + ConstructionTransaction move_construction_tx(GetAllocPtr()); + + IteratorValueAdapter move_construction_values( + MoveIterator(storage_view.data + + (move_construction_destination_index - insert_count))); + absl::Span move_construction = { + storage_view.data + move_construction_destination_index, + new_size - move_construction_destination_index}; + + pointer move_assignment_values = storage_view.data + insert_index; + absl::Span move_assignment = { + storage_view.data + insert_end_index, + move_construction_destination_index - insert_end_index}; + + absl::Span insert_assignment = {move_assignment_values, + move_construction.size()}; + + absl::Span insert_construction = { + insert_assignment.data() + insert_assignment.size(), + insert_count - insert_assignment.size()}; + + move_construction_tx.Construct(move_construction.data(), + &move_construction_values, + move_construction.size()); + + for (pointer destination = move_assignment.data() + move_assignment.size(), + last_destination = move_assignment.data(), + source = move_assignment_values + move_assignment.size(); + ;) { + --destination; + --source; + if (destination < last_destination) break; + *destination = std::move(*source); + } + + inlined_vector_internal::AssignElements(insert_assignment.data(), &values, + insert_assignment.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), insert_construction.data(), &values, + insert_construction.size()); + + move_construction_tx.Commit(); + + AddSize(insert_count); + return iterator(storage_view.data + insert_index); + } +} + +template +template +auto Storage::EmplaceBack(Args&&... args) -> reference { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + 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; + } else { + construct_data = storage_view.data; + } + + pointer end = construct_data + storage_view.size; + + AllocatorTraits::construct(*GetAllocPtr(), end, std::forward(args)...); + + if (allocation_tx.DidAllocate()) { + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements( + GetAllocPtr(), allocation_tx.GetData(), &move_values, + storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + AllocatorTraits::destroy(*GetAllocPtr(), end); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + AddSize(1); + return *end; +} + +template +auto Storage::Erase(const_iterator from, const_iterator to) + -> iterator { + assert(from != to); + + StorageView storage_view = MakeStorageView(); + + size_type erase_size = std::distance(from, to); + size_type erase_index = + std::distance(const_iterator(storage_view.data), from); + size_type erase_end_index = erase_index + erase_size; + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data + erase_end_index)); + + inlined_vector_internal::AssignElements(storage_view.data + erase_index, + &move_values, + storage_view.size - erase_end_index); + + inlined_vector_internal::DestroyElements( + GetAllocPtr(), storage_view.data + (storage_view.size - erase_size), + erase_size); + + SubtractSize(erase_size); + return iterator(storage_view.data + erase_index); +} + +template +auto Storage::Reserve(size_type requested_capacity) -> void { + StorageView storage_view = MakeStorageView(); + + if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + MoveIterator(storage_view.data)); + + size_type new_capacity = + ComputeCapacity(storage_view.capacity, requested_capacity); + pointer new_data = allocation_tx.Allocate(new_capacity); + + inlined_vector_internal::ConstructElements(GetAllocPtr(), new_data, + &move_values, storage_view.size); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); +} + +template +auto Storage::ShrinkToFit() -> void { + // May only be called on allocated instances! + assert(GetIsAllocated()); + + StorageView storage_view{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()}; + + if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter move_values( + 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; + } else { + construct_data = GetInlinedData(); + } + + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, + &move_values, storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + SetAllocatedData(storage_view.data, storage_view.capacity); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data, + storage_view.capacity); + + if (allocation_tx.DidAllocate()) { + AcquireAllocatedData(&allocation_tx); + } else { + UnsetIsAllocated(); + } +} + +template +auto Storage::Swap(Storage* other_storage_ptr) -> void { + using std::swap; + assert(this != other_storage_ptr); + + if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { + swap(data_.allocated, other_storage_ptr->data_.allocated); + } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) { + Storage* small_ptr = this; + Storage* large_ptr = other_storage_ptr; + if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); + + for (size_type i = 0; i < small_ptr->GetSize(); ++i) { + swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); + } + + IteratorValueAdapter move_values( + MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize())); + + inlined_vector_internal::ConstructElements( + large_ptr->GetAllocPtr(), + small_ptr->GetInlinedData() + small_ptr->GetSize(), &move_values, + large_ptr->GetSize() - small_ptr->GetSize()); + + inlined_vector_internal::DestroyElements( + large_ptr->GetAllocPtr(), + large_ptr->GetInlinedData() + small_ptr->GetSize(), + large_ptr->GetSize() - small_ptr->GetSize()); + } else { + Storage* allocated_ptr = this; + Storage* inlined_ptr = other_storage_ptr; + if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); + + StorageView allocated_storage_view{allocated_ptr->GetAllocatedData(), + allocated_ptr->GetSize(), + allocated_ptr->GetAllocatedCapacity()}; + + IteratorValueAdapter move_values( + MoveIterator(inlined_ptr->GetInlinedData())); + + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements( + inlined_ptr->GetAllocPtr(), allocated_ptr->GetInlinedData(), + &move_values, inlined_ptr->GetSize()); + } + ABSL_INTERNAL_CATCH_ANY { + allocated_ptr->SetAllocatedData(allocated_storage_view.data, + allocated_storage_view.capacity); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(inlined_ptr->GetAllocPtr(), + inlined_ptr->GetInlinedData(), + inlined_ptr->GetSize()); + + inlined_ptr->SetAllocatedData(allocated_storage_view.data, + allocated_storage_view.capacity); + } + + swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); + swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr()); +} + +} // namespace inlined_vector_internal +} // inline namespace lts_2019_08_08 +} // 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 f11a6ad2..3924b8aa 100644 --- a/absl/container/internal/layout.h +++ b/absl/container/internal/layout.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -188,7 +188,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // A type wrapper that instructs `Layout` to use the specific alignment for the @@ -644,7 +644,8 @@ class LayoutImpl, absl::index_sequence, std::string DebugString() const { const auto offsets = Offsets(); const size_t sizes[] = {SizeOf>()...}; - const std::string types[] = {adl_barrier::TypeName>()...}; + const std::string types[] = { + adl_barrier::TypeName>()...}; std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); for (size_t i = 0; i != NumOffsets - 1; ++i) { absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1], @@ -734,7 +735,7 @@ class Layout : public internal_layout::LayoutType { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 b9f98471..44d84607 100644 --- a/absl/container/internal/layout_test.cc +++ b/absl/container/internal/layout_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -46,7 +46,7 @@ Expected Type(Actual val) { return val; } -// Helper class to test different size and alignments. +// Helper classes to test different size and alignments. struct alignas(8) Int128 { uint64_t a, b; friend bool operator==(Int128 lhs, Int128 rhs) { @@ -58,6 +58,14 @@ struct alignas(8) Int128 { } }; +// int64_t is *not* 8-byte aligned on all platforms! +struct alignas(8) Int64 { + int64_t a; + friend bool operator==(Int64 lhs, Int64 rhs) { + return lhs.a == rhs.a; + } +}; + // Properties of types that this test relies on. static_assert(sizeof(int8_t) == 1, ""); static_assert(alignof(int8_t) == 1, ""); @@ -65,6 +73,8 @@ static_assert(sizeof(int16_t) == 2, ""); static_assert(alignof(int16_t) == 2, ""); static_assert(sizeof(int32_t) == 4, ""); static_assert(alignof(int32_t) == 4, ""); +static_assert(sizeof(Int64) == 8, ""); +static_assert(alignof(Int64) == 8, ""); static_assert(sizeof(Int128) == 16, ""); static_assert(alignof(Int128) == 8, ""); @@ -1282,14 +1292,14 @@ TEST(Layout, OverAligned) { TEST(Layout, Alignment) { static_assert(Layout::Alignment() == 1, ""); static_assert(Layout::Alignment() == 4, ""); - static_assert(Layout::Alignment() == 8, ""); + static_assert(Layout::Alignment() == 8, ""); static_assert(Layout>::Alignment() == 64, ""); - static_assert(Layout::Alignment() == 8, ""); - static_assert(Layout::Alignment() == 8, ""); - static_assert(Layout::Alignment() == 8, ""); - static_assert(Layout::Alignment() == 8, ""); - static_assert(Layout::Alignment() == 8, ""); - static_assert(Layout::Alignment() == 8, ""); + static_assert(Layout::Alignment() == 8, ""); + static_assert(Layout::Alignment() == 8, ""); + static_assert(Layout::Alignment() == 8, ""); + static_assert(Layout::Alignment() == 8, ""); + static_assert(Layout::Alignment() == 8, ""); + static_assert(Layout::Alignment() == 8, ""); } TEST(Layout, ConstexprPartial) { @@ -1324,7 +1334,7 @@ void ExpectPoisoned(const unsigned char (&buf)[N], } TEST(Layout, PoisonPadding) { - using L = Layout; + using L = Layout; constexpr size_t n = L::Partial(1, 2, 3, 4).AllocSize(); { @@ -1553,5 +1563,5 @@ TEST(CompactString, Works) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/node_hash_policy.h b/absl/container/internal/node_hash_policy.h index e8d89f63..d7581360 100644 --- a/absl/container/internal/node_hash_policy.h +++ b/absl/container/internal/node_hash_policy.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -40,7 +40,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template @@ -84,7 +84,7 @@ struct node_hash_policy { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 a73c7bba..d53b7364 100644 --- a/absl/container/internal/node_hash_policy_test.cc +++ b/absl/container/internal/node_hash_policy_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,7 @@ #include "absl/container/internal/hash_policy_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -65,5 +65,5 @@ TEST_F(NodeTest, transfer) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h index 53d4619a..00caa373 100644 --- a/absl/container/internal/raw_hash_map.h +++ b/absl/container/internal/raw_hash_map.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,11 +19,12 @@ #include #include +#include "absl/base/internal/throw_delegate.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template @@ -40,8 +41,8 @@ class raw_hash_map : public raw_hash_set { using MappedConstReference = decltype(P::value( std::addressof(std::declval()))); - using KeyArgImpl = container_internal::KeyArg::value && - IsTransparent::value>; + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; public: using key_type = typename Policy::key_type; @@ -137,14 +138,20 @@ class raw_hash_map : public raw_hash_set { template MappedReference

at(const key_arg& key) { auto it = this->find(key); - if (it == this->end()) std::abort(); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } return Policy::value(&*it); } template MappedConstReference

at(const key_arg& key) const { auto it = this->find(key); - if (it == this->end()) std::abort(); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } return Policy::value(&*it); } @@ -181,7 +188,7 @@ class raw_hash_map : public raw_hash_set { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 4e690dac..02e74e21 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 0c42e4ae..7b379d4f 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -91,36 +91,6 @@ #ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ #define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ -#ifndef SWISSTABLE_HAVE_SSE2 -#if defined(__SSE2__) || \ - (defined(_MSC_VER) && \ - (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2))) -#define SWISSTABLE_HAVE_SSE2 1 -#else -#define SWISSTABLE_HAVE_SSE2 0 -#endif -#endif - -#ifndef SWISSTABLE_HAVE_SSSE3 -#ifdef __SSSE3__ -#define SWISSTABLE_HAVE_SSSE3 1 -#else -#define SWISSTABLE_HAVE_SSSE3 0 -#endif -#endif - -#if SWISSTABLE_HAVE_SSSE3 && !SWISSTABLE_HAVE_SSE2 -#error "Bad configuration!" -#endif - -#if SWISSTABLE_HAVE_SSE2 -#include -#endif - -#if SWISSTABLE_HAVE_SSSE3 -#include -#endif - #include #include #include @@ -135,18 +105,20 @@ #include "absl/base/internal/bits.h" #include "absl/base/internal/endian.h" #include "absl/base/port.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/hash_policy_traits.h" #include "absl/container/internal/hashtable_debug_hooks.h" +#include "absl/container/internal/hashtablez_sampler.h" +#include "absl/container/internal/have_sse.h" #include "absl/container/internal/layout.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" -#include "absl/types/optional.h" #include "absl/utility/utility.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template @@ -194,12 +166,6 @@ struct IsDecomposable< std::declval()...))>, Policy, Hash, Eq, Ts...> : std::true_type {}; -template -struct IsTransparent : std::false_type {}; -template -struct IsTransparent> - : std::true_type {}; - // TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it. template constexpr bool IsNoThrowSwappable() { @@ -385,7 +351,7 @@ struct GroupSse2Impl { return BitMask( _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))); #else - return Match(kEmpty); + return Match(static_cast(kEmpty)); #endif } @@ -481,9 +447,7 @@ using Group = GroupPortableImpl; template class raw_hash_set; -inline bool IsValidCapacity(size_t n) { - return ((n + 1) & n) == 0 && n >= Group::kWidth - 1; -} +inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } // PRECONDITION: // IsValidCapacity(capacity) @@ -505,152 +469,32 @@ inline void ConvertDeletedToEmptyAndFullToDeleted( ctrl[capacity] = kSentinel; } -// Rounds up the capacity to the next power of 2 minus 1 and ensures it is -// greater or equal to Group::kWidth - 1. +// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1. inline size_t NormalizeCapacity(size_t n) { - constexpr size_t kMinCapacity = Group::kWidth - 1; - return n <= kMinCapacity - ? kMinCapacity - : (std::numeric_limits::max)() >> LeadingZeros(n); + return n ? ~size_t{} >> LeadingZeros(n) : 1; } -// The node_handle concept from C++17. -// We specialize node_handle for sets and maps. node_handle_base holds the -// common API of both. -template -class node_handle_base { - protected: - using PolicyTraits = hash_policy_traits; - using slot_type = typename PolicyTraits::slot_type; - - public: - using allocator_type = Alloc; - - constexpr node_handle_base() {} - node_handle_base(node_handle_base&& other) noexcept { - *this = std::move(other); - } - ~node_handle_base() { destroy(); } - node_handle_base& operator=(node_handle_base&& other) { - destroy(); - if (!other.empty()) { - alloc_ = other.alloc_; - PolicyTraits::transfer(alloc(), slot(), other.slot()); - other.reset(); - } - return *this; - } - - bool empty() const noexcept { return !alloc_; } - explicit operator bool() const noexcept { return !empty(); } - allocator_type get_allocator() const { return *alloc_; } - - protected: - template - friend class raw_hash_set; - - node_handle_base(const allocator_type& a, slot_type* s) : alloc_(a) { - PolicyTraits::transfer(alloc(), slot(), s); - } - - void destroy() { - if (!empty()) { - PolicyTraits::destroy(alloc(), slot()); - reset(); - } - } - - void reset() { - assert(alloc_.has_value()); - alloc_ = absl::nullopt; - } - - slot_type* slot() const { - assert(!empty()); - return reinterpret_cast(std::addressof(slot_space_)); - } - allocator_type* alloc() { return std::addressof(*alloc_); } - - private: - absl::optional alloc_; - mutable absl::aligned_storage_t - slot_space_; -}; - -// For sets. -template -class node_handle : public node_handle_base { - using Base = typename node_handle::node_handle_base; - - public: - using value_type = typename Base::PolicyTraits::value_type; - - constexpr node_handle() {} - - value_type& value() const { - return Base::PolicyTraits::element(this->slot()); - } - - private: - template - friend class raw_hash_set; - - node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {} -}; - -// For maps. -template -class node_handle> - : public node_handle_base { - using Base = typename node_handle::node_handle_base; - - public: - using key_type = typename Policy::key_type; - using mapped_type = typename Policy::mapped_type; - - constexpr node_handle() {} - - auto key() const -> decltype(Base::PolicyTraits::key(this->slot())) { - return Base::PolicyTraits::key(this->slot()); - } - - mapped_type& mapped() const { - return Base::PolicyTraits::value( - &Base::PolicyTraits::element(this->slot())); +// We use 7/8th as maximum load factor. +// For 16-wide groups, that gives an average of two empty slots per group. +inline size_t CapacityToGrowth(size_t capacity) { + assert(IsValidCapacity(capacity)); + // `capacity*7/8` + if (Group::kWidth == 8 && capacity == 7) { + // x-x/8 does not work when x==7. + return 6; } - - private: - template - friend class raw_hash_set; - - node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {} -}; - -// Implement the insert_return_type<> concept of C++17. -template -struct insert_return_type { - Iterator position; - bool inserted; - NodeType node; -}; - -// Helper trait to allow or disallow arbitrary keys when the hash and -// eq functions are transparent. -// It is very important that the inner template is an alias and that the type it -// produces is not a dependent type. Otherwise, type deduction would fail. -template -struct KeyArg { - // Transparent. Forward `K`. - template - using type = K; -}; - -template <> -struct KeyArg { - // Not transparent. Always use `key_type`. - template - using type = key_type; -}; + return capacity - capacity / 8; +} +// From desired "growth" to a lowerbound of the necessary capacity. +// Might not be a valid one and required NormalizeCapacity(). +inline size_t GrowthToLowerboundCapacity(size_t growth) { + // `growth*8/7` + if (Group::kWidth == 8 && growth == 7) { + // x+(x-1)/7 does not work when x==7. + return 8; + } + return growth + static_cast((static_cast(growth) - 1) / 7); +} // Policy: a policy defines how to perform different operations on // the slots of the hashtable (see hash_policy_traits.h for the full interface @@ -666,14 +510,14 @@ struct KeyArg { // if they are equal, false if they are not. If two keys compare equal, then // their hash values as defined by Hash MUST be equal. // -// Allocator: an Allocator [http://devdocs.io/cpp/concept/allocator] with which +// Allocator: an Allocator [https://devdocs.io/cpp/concept/allocator] with which // the storage of the hashtable will be allocated and the elements will be // constructed and destroyed. template class raw_hash_set { using PolicyTraits = hash_policy_traits; - using KeyArgImpl = container_internal::KeyArg::value && - IsTransparent::value>; + using KeyArgImpl = + KeyArg::value && IsTransparent::value>; public: using init_type = typename PolicyTraits::init_type; @@ -814,7 +658,11 @@ class raw_hash_set { } ctrl_t* ctrl_ = nullptr; - slot_type* slot_; + // To avoid uninitialized member warnigs, put slot_ in an anonymous union. + // The member is not initialized on singleton and end iterators. + union { + slot_type* slot_; + }; }; class const_iterator { @@ -854,7 +702,8 @@ class raw_hash_set { iterator inner_; }; - using node_type = container_internal::node_handle; + using node_type = node_handle, Alloc>; + using insert_return_type = InsertReturnType; raw_hash_set() noexcept( std::is_nothrow_default_constructible::value&& @@ -867,7 +716,7 @@ class raw_hash_set { : ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) { if (bucket_count) { capacity_ = NormalizeCapacity(bucket_count); - growth_left() = static_cast(capacity_ * kMaxLoadFactor); + reset_growth_left(); initialize_slots(); } } @@ -909,8 +758,8 @@ class raw_hash_set { // that accept std::initializer_list and std::initializer_list. // This is advantageous for performance. // - // // Turns {"abc", "def"} into std::initializer_list, then copies - // // the strings into the set. + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. // std::unordered_set s = {"abc", "def"}; // // // Turns {"abc", "def"} into std::initializer_list, then @@ -973,9 +822,10 @@ class raw_hash_set { // than a full `insert`. for (const auto& v : that) { const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v); - const size_t i = find_first_non_full(hash); - set_ctrl(i, H2(hash)); - emplace_at(i, v); + auto target = find_first_non_full(hash); + set_ctrl(target.offset, H2(hash)); + emplace_at(target.offset, v); + infoz_.RecordInsert(hash, target.probe_length); } size_ = that.size(); growth_left() -= that.size(); @@ -989,6 +839,7 @@ class raw_hash_set { slots_(absl::exchange(that.slots_, nullptr)), size_(absl::exchange(that.size_, 0)), capacity_(absl::exchange(that.capacity_, 0)), + infoz_(absl::exchange(that.infoz_, HashtablezInfoHandle())), // Hash, equality and allocator are copied instead of moved because // `that` must be left valid. If Hash is std::function, moving it // would create a nullptr functor that cannot be called. @@ -1009,6 +860,7 @@ class raw_hash_set { std::swap(size_, that.size_); std::swap(capacity_, that.capacity_); std::swap(growth_left(), that.growth_left()); + std::swap(infoz_, that.infoz_); } else { reserve(that.size()); // Note: this will copy elements of dense_set and unordered_set instead of @@ -1058,7 +910,7 @@ class raw_hash_set { size_t capacity() const { return capacity_; } size_t max_size() const { return (std::numeric_limits::max)(); } - void clear() { + ABSL_ATTRIBUTE_REINITIALIZES void clear() { // Iterating over this container is O(bucket_count()). When bucket_count() // is much greater than size(), iteration becomes prohibitively expensive. // For clear() it is more important to reuse the allocated array when the @@ -1076,9 +928,10 @@ class raw_hash_set { } size_ = 0; reset_ctrl(); - growth_left() = static_cast(capacity_ * kMaxLoadFactor); + reset_growth_left(); } assert(empty()); + infoz_.RecordStorageChanged(0, capacity_); } // This overload kicks in when the argument is an rvalue of insertable and @@ -1117,7 +970,7 @@ class raw_hash_set { // This overload kicks in when the argument is an rvalue of init_type. Its // purpose is to handle brace-init-list arguments. // - // flat_hash_set s; + // flat_hash_map s; // s.insert({"abc", 42}); std::pair insert(init_type&& value) { return emplace(std::move(value)); @@ -1158,13 +1011,14 @@ class raw_hash_set { insert(ilist.begin(), ilist.end()); } - insert_return_type insert(node_type&& node) { + insert_return_type insert(node_type&& node) { if (!node) return {end(), false, node_type()}; - const auto& elem = PolicyTraits::element(node.slot()); + const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node)); auto res = PolicyTraits::apply( - InsertSlot{*this, std::move(*node.slot())}, elem); + InsertSlot{*this, std::move(*CommonAccess::GetSlot(node))}, + elem); if (res.second) { - node.reset(); + CommonAccess::Reset(&node); return {res.first, true, node_type()}; } else { return {res.first, false, std::move(node)}; @@ -1328,7 +1182,8 @@ class raw_hash_set { } node_type extract(const_iterator position) { - node_type node(alloc_ref(), position.inner_.slot_); + auto node = + CommonAccess::Transfer(alloc_ref(), position.inner_.slot_); erase_meta_only(position); return node; } @@ -1353,6 +1208,7 @@ class raw_hash_set { swap(growth_left(), that.growth_left()); swap(hash_ref(), that.hash_ref()); swap(eq_ref(), that.eq_ref()); + swap(infoz_, that.infoz_); if (AllocTraits::propagate_on_container_swap::value) { swap(alloc_ref(), that.alloc_ref()); } else { @@ -1363,17 +1219,21 @@ class raw_hash_set { void rehash(size_t n) { if (n == 0 && capacity_ == 0) return; - if (n == 0 && size_ == 0) return destroy_slots(); - auto m = NormalizeCapacity(std::max(n, NumSlotsFast(size()))); + if (n == 0 && size_ == 0) { + destroy_slots(); + infoz_.RecordStorageChanged(0, 0); + return; + } + // bitor is a faster way of doing `max` here. We will round up to the next + // power-of-2-minus-1, so bitor is good enough. + auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size())); // n == 0 unconditionally rehashes as per the standard. if (n == 0 || m > capacity_) { resize(m); } } - void reserve(size_t n) { - rehash(NumSlotsFast(n)); - } + void reserve(size_t n) { rehash(GrowthToLowerboundCapacity(n)); } // Extension API: support for heterogeneous keys. // @@ -1551,13 +1411,6 @@ class raw_hash_set { slot_type&& slot; }; - // Computes std::ceil(n / kMaxLoadFactor). Faster than calling std::ceil. - static inline size_t NumSlotsFast(size_t n) { - return static_cast( - (n * kMaxLoadFactorDenominator + (kMaxLoadFactorNumerator - 1)) / - kMaxLoadFactorNumerator); - } - // "erases" the object from the container, except that it doesn't actually // destroy the object. It only updates all the metadata of the class. // This can be used in conjunction with Policy::transfer to move the object to @@ -1580,17 +1433,34 @@ class raw_hash_set { set_ctrl(index, was_never_full ? kEmpty : kDeleted); growth_left() += was_never_full; + infoz_.RecordErase(); } void initialize_slots() { assert(capacity_); + // Folks with custom allocators often make unwarranted assumptions about the + // behavior of their classes vis-a-vis trivial destructability and what + // calls they will or wont make. Avoid sampling for people with custom + // allocators to get us out of this mess. This is not a hard guarantee but + // a workaround while we plan the exact guarantee we want to provide. + // + // People are often sloppy with the exact type of their allocator (sometimes + // it has an extra const or is missing the pair, but rebinds made it work + // anyway). To avoid the ambiguity, we work off SlotAlloc which we have + // bound more carefully. + if (std::is_same>::value && + slots_ == nullptr) { + infoz_ = Sample(); + } + auto layout = MakeLayout(capacity_); char* mem = static_cast( Allocate(&alloc_ref(), layout.AllocSize())); ctrl_ = reinterpret_cast(layout.template Pointer<0>(mem)); slots_ = layout.template Pointer<1>(mem); reset_ctrl(); - growth_left() = static_cast(capacity_ * kMaxLoadFactor) - size_; + reset_growth_left(); + infoz_.RecordStorageChanged(size_, capacity_); } void destroy_slots() { @@ -1619,11 +1489,14 @@ class raw_hash_set { capacity_ = new_capacity; initialize_slots(); + size_t total_probe_length = 0; for (size_t i = 0; i != old_capacity; ++i) { if (IsFull(old_ctrl[i])) { size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, PolicyTraits::element(old_slots + i)); - size_t new_i = find_first_non_full(hash); + auto target = find_first_non_full(hash); + size_t new_i = target.offset; + total_probe_length += target.probe_length; set_ctrl(new_i, H2(hash)); PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); } @@ -1635,10 +1508,12 @@ class raw_hash_set { Deallocate(&alloc_ref(), old_ctrl, layout.AllocSize()); } + infoz_.RecordRehash(total_probe_length); } void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE { assert(IsValidCapacity(capacity_)); + assert(!is_small()); // Algorithm: // - mark all DELETED slots as EMPTY // - mark all FULL slots as DELETED @@ -1658,12 +1533,15 @@ class raw_hash_set { ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_); typename std::aligned_storage::type raw; + size_t total_probe_length = 0; slot_type* slot = reinterpret_cast(&raw); for (size_t i = 0; i != capacity_; ++i) { if (!IsDeleted(ctrl_[i])) continue; size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, PolicyTraits::element(slots_ + i)); - size_t new_i = find_first_non_full(hash); + auto target = find_first_non_full(hash); + size_t new_i = target.offset; + total_probe_length += target.probe_length; // Verify if the old and new i fall within the same group wrt the hash. // If they do, we don't need to move the object as it falls already in the @@ -1695,13 +1573,14 @@ class raw_hash_set { --i; // repeat } } - growth_left() = static_cast(capacity_ * kMaxLoadFactor) - size_; + reset_growth_left(); + infoz_.RecordRehash(total_probe_length); } void rehash_and_grow_if_necessary() { if (capacity_ == 0) { - resize(Group::kWidth - 1); - } else if (size() <= kMaxLoadFactor / 2 * capacity_) { + resize(1); + } else if (size() <= CapacityToGrowth(capacity()) / 2) { // Squash DELETED without growing if there is enough capacity. drop_deletes_without_resize(); } else { @@ -1736,24 +1615,26 @@ class raw_hash_set { // - the input is already a set // - there are enough slots // - the element with the hash is not in the table - size_t find_first_non_full(size_t hash) { + struct FindInfo { + size_t offset; + size_t probe_length; + }; + FindInfo find_first_non_full(size_t hash) { auto seq = probe(hash); while (true) { Group g{ctrl_ + seq.offset()}; auto mask = g.MatchEmptyOrDeleted(); if (mask) { #if !defined(NDEBUG) - // We want to force small tables to have random entries too, so - // in debug build we will randomly insert in either the front or back of + // We want to add entropy even when ASLR is not enabled. + // In debug build we will randomly insert in either the front or back of // the group. // TODO(kfm,sbenza): revisit after we do unconditional mixing - if (ShouldInsertBackwards(hash, ctrl_)) - return seq.offset(mask.HighestBitSet()); - else - return seq.offset(mask.LowestBitSet()); -#else - return seq.offset(mask.LowestBitSet()); + if (!is_small() && ShouldInsertBackwards(hash, ctrl_)) { + return {seq.offset(mask.HighestBitSet()), seq.index()}; + } #endif + return {seq.offset(mask.LowestBitSet()), seq.index()}; } assert(seq.index() < capacity_ && "full table!"); seq.next(); @@ -1792,15 +1673,17 @@ class raw_hash_set { } size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE { - size_t target = find_first_non_full(hash); - if (ABSL_PREDICT_FALSE(growth_left() == 0 && !IsDeleted(ctrl_[target]))) { + auto target = find_first_non_full(hash); + if (ABSL_PREDICT_FALSE(growth_left() == 0 && + !IsDeleted(ctrl_[target.offset]))) { rehash_and_grow_if_necessary(); target = find_first_non_full(hash); } ++size_; - growth_left() -= IsEmpty(ctrl_[target]); - set_ctrl(target, H2(hash)); - return target; + growth_left() -= IsEmpty(ctrl_[target.offset]); + set_ctrl(target.offset, H2(hash)); + infoz_.RecordInsert(hash, target.probe_length); + return target.offset; } // Constructs the value in the space pointed by the iterator. This only works @@ -1838,6 +1721,10 @@ class raw_hash_set { SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); } + void reset_growth_left() { + growth_left() = CapacityToGrowth(capacity()) - size_; + } + // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at // the end too. void set_ctrl(size_t i, ctrl_t h) { @@ -1850,11 +1737,28 @@ class raw_hash_set { } ctrl_[i] = h; - ctrl_[((i - Group::kWidth) & capacity_) + Group::kWidth] = h; + ctrl_[((i - Group::kWidth) & capacity_) + 1 + + ((Group::kWidth - 1) & capacity_)] = h; } size_t& growth_left() { return settings_.template get<0>(); } + // The representation of the object has two modes: + // - small: For capacities < kWidth-1 + // - large: For the rest. + // + // Differences: + // - In small mode we are able to use the whole capacity. The extra control + // bytes give us at least one "empty" control byte to stop the iteration. + // This is important to make 1 a valid capacity. + // + // - In small mode only the first `capacity()` control bytes after the + // sentinel are valid. The rest contain dummy kEmpty values that do not + // represent a real slot. This is important to take into account on + // find_first_non_full(), where we never try ShouldInsertBackwards() for + // small tables. + bool is_small() const { return capacity_ < Group::kWidth - 1; } + hasher& hash_ref() { return settings_.template get<1>(); } const hasher& hash_ref() const { return settings_.template get<1>(); } key_equal& eq_ref() { return settings_.template get<2>(); } @@ -1864,12 +1768,6 @@ class raw_hash_set { return settings_.template get<3>(); } - // On average each group has 2 empty slot (for the vectorized case). - static constexpr int64_t kMaxLoadFactorNumerator = 14; - static constexpr int64_t kMaxLoadFactorDenominator = 16; - static constexpr float kMaxLoadFactor = - 1.0 * kMaxLoadFactorNumerator / kMaxLoadFactorDenominator; - // TODO(alkis): Investigate removing some of these fields: // - ctrl/slots can be derived from each other // - size can be moved into the slot array @@ -1877,6 +1775,7 @@ class raw_hash_set { slot_type* slots_ = nullptr; // [capacity * slot_type] size_t size_ = 0; // number of full slots size_t capacity_ = 0; // total number of slots + HashtablezInfoHandle infoz_; absl::container_internal::CompressedTuple settings_{0, hasher{}, key_equal{}, allocator_type{}}; @@ -1929,10 +1828,9 @@ struct HashtableDebugAccess> { } static size_t LowerBoundAllocatedByteSize(size_t size) { - size_t capacity = container_internal::NormalizeCapacity( - std::ceil(size / Set::kMaxLoadFactor)); + size_t capacity = GrowthToLowerboundCapacity(size); if (capacity == 0) return 0; - auto layout = Set::MakeLayout(capacity); + auto layout = Set::MakeLayout(NormalizeCapacity(capacity)); size_t m = layout.AllocSize(); size_t per_slot = Traits::space_used(static_cast(nullptr)); if (per_slot != ~size_t{}) { @@ -1944,7 +1842,7 @@ struct HashtableDebugAccess> { } // namespace hashtable_debug_internal } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 f5779d62..5188b3ae 100644 --- a/absl/container/internal/raw_hash_set_allocator_test.cc +++ b/absl/container/internal/raw_hash_set_allocator_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "absl/container/internal/tracked.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -426,5 +426,5 @@ TEST_F(PropagateOnAll, Swap) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 302f9758..2783f5c4 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -35,7 +35,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { struct RawHashSetTestOnlyAccess { @@ -49,18 +49,47 @@ namespace { using ::testing::DoubleNear; using ::testing::ElementsAre; +using ::testing::Ge; +using ::testing::Lt; using ::testing::Optional; using ::testing::Pair; using ::testing::UnorderedElementsAre; TEST(Util, NormalizeCapacity) { - constexpr size_t kMinCapacity = Group::kWidth - 1; - EXPECT_EQ(kMinCapacity, NormalizeCapacity(0)); - EXPECT_EQ(kMinCapacity, NormalizeCapacity(1)); - EXPECT_EQ(kMinCapacity, NormalizeCapacity(2)); - EXPECT_EQ(kMinCapacity, NormalizeCapacity(kMinCapacity)); - EXPECT_EQ(kMinCapacity * 2 + 1, NormalizeCapacity(kMinCapacity + 1)); - EXPECT_EQ(kMinCapacity * 2 + 1, NormalizeCapacity(kMinCapacity + 2)); + EXPECT_EQ(1, NormalizeCapacity(0)); + EXPECT_EQ(1, NormalizeCapacity(1)); + EXPECT_EQ(3, NormalizeCapacity(2)); + EXPECT_EQ(3, NormalizeCapacity(3)); + EXPECT_EQ(7, NormalizeCapacity(4)); + EXPECT_EQ(7, NormalizeCapacity(7)); + EXPECT_EQ(15, NormalizeCapacity(8)); + EXPECT_EQ(15, NormalizeCapacity(15)); + EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 1)); + EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 2)); +} + +TEST(Util, GrowthAndCapacity) { + // Verify that GrowthToCapacity gives the minimum capacity that has enough + // growth. + for (size_t growth = 0; growth < 10000; ++growth) { + SCOPED_TRACE(growth); + size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth)); + // The capacity is large enough for `growth` + EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth)); + if (growth != 0 && capacity > 1) { + // There is no smaller capacity that works. + EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth)); + } + } + + for (size_t capacity = Group::kWidth - 1; capacity < 10000; + capacity = 2 * capacity + 1) { + SCOPED_TRACE(capacity); + size_t growth = CapacityToGrowth(capacity); + EXPECT_THAT(growth, Lt(capacity)); + EXPECT_LE(GrowthToLowerboundCapacity(growth), capacity); + EXPECT_EQ(NormalizeCapacity(GrowthToLowerboundCapacity(growth)), capacity); + } } TEST(Util, probe_seq) { @@ -107,14 +136,14 @@ TEST(BitMask, WithShift) { } TEST(BitMask, LeadingTrailing) { - EXPECT_EQ((BitMask(0b0001101001000000).LeadingZeros()), 3); - EXPECT_EQ((BitMask(0b0001101001000000).TrailingZeros()), 6); + EXPECT_EQ((BitMask(0x00001a40).LeadingZeros()), 3); + EXPECT_EQ((BitMask(0x00001a40).TrailingZeros()), 6); - EXPECT_EQ((BitMask(0b0000000000000001).LeadingZeros()), 15); - EXPECT_EQ((BitMask(0b0000000000000001).TrailingZeros()), 0); + EXPECT_EQ((BitMask(0x00000001).LeadingZeros()), 15); + EXPECT_EQ((BitMask(0x00000001).TrailingZeros()), 0); - EXPECT_EQ((BitMask(0b1000000000000000).LeadingZeros()), 0); - EXPECT_EQ((BitMask(0b1000000000000000).TrailingZeros()), 15); + EXPECT_EQ((BitMask(0x00008000).LeadingZeros()), 0); + EXPECT_EQ((BitMask(0x00008000).TrailingZeros()), 15); EXPECT_EQ((BitMask(0x0000008080808000).LeadingZeros()), 3); EXPECT_EQ((BitMask(0x0000008080808000).TrailingZeros()), 1); @@ -315,7 +344,25 @@ struct IntTable : raw_hash_set, std::equal_to, std::allocator> { using Base = typename IntTable::raw_hash_set; - IntTable() {} + using Base::Base; +}; + +template +struct CustomAlloc : std::allocator { + CustomAlloc() {} + + template + CustomAlloc(const CustomAlloc& other) {} + + template struct rebind { + using other = CustomAlloc; + }; +}; + +struct CustomAllocIntTable + : raw_hash_set, + std::equal_to, CustomAlloc> { + using Base = typename CustomAllocIntTable::raw_hash_set; using Base::Base; }; @@ -343,6 +390,7 @@ TEST(Table, EmptyFunctorOptimization) { size_t size; size_t capacity; size_t growth_left; + void* infoz; }; struct StatelessHash { size_t operator()(absl::string_view) const { return 0; } @@ -385,10 +433,11 @@ TEST(Table, Prefetch) { t.prefetch(2); // Do not run in debug mode, when prefetch is not implemented, or when - // sanitizers are enabled. -#if defined(NDEBUG) && defined(__GNUC__) && !defined(ADDRESS_SANITIZER) && \ - !defined(MEMORY_SANITIZER) && !defined(THREAD_SANITIZER) && \ - !defined(UNDEFINED_BEHAVIOR_SANITIZER) + // 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) @@ -785,7 +834,7 @@ TEST(Table, EnsureNonQuadraticAsInRust) { TEST(Table, ClearBug) { IntTable t; constexpr size_t capacity = container_internal::Group::kWidth - 1; - constexpr size_t max_size = capacity / 2; + constexpr size_t max_size = capacity / 2 + 1; for (size_t i = 0; i < max_size; ++i) { t.insert(i); } @@ -816,6 +865,25 @@ TEST(Table, Erase) { EXPECT_TRUE(t.find(0) == t.end()); } +TEST(Table, EraseMaintainsValidIterator) { + IntTable t; + const int kNumElements = 100; + for (int i = 0; i < kNumElements; i ++) { + EXPECT_TRUE(t.emplace(i).second); + } + EXPECT_EQ(t.size(), kNumElements); + + int num_erase_calls = 0; + auto it = t.begin(); + while (it != t.end()) { + t.erase(it++); + num_erase_calls++; + } + + EXPECT_TRUE(t.empty()); + EXPECT_EQ(num_erase_calls, kNumElements); +} + // Collect N bad keys by following algorithm: // 1. Create an empty table and reserve it to 2 * N. // 2. Insert N random elements. @@ -1014,7 +1082,7 @@ ProbeStats CollectProbeStatsOnKeysXoredWithSeed(const std::vector& keys ExpectedStats XorSeedExpectedStats() { constexpr bool kRandomizesInserts = -#if NDEBUG +#ifdef NDEBUG false; #else // NDEBUG true; @@ -1051,6 +1119,7 @@ ExpectedStats XorSeedExpectedStats() { ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width"); return {}; } + TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) { ProbeStatsPerSize stats; std::vector sizes = {Group::kWidth << 5, Group::kWidth << 10}; @@ -1107,7 +1176,7 @@ ProbeStats CollectProbeStatsOnLinearlyTransformedKeys( ExpectedStats LinearTransformExpectedStats() { constexpr bool kRandomizesInserts = -#if NDEBUG +#ifdef NDEBUG false; #else // NDEBUG true; @@ -1144,6 +1213,7 @@ ExpectedStats LinearTransformExpectedStats() { ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width"); return {}; } + TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) { ProbeStatsPerSize stats; std::vector sizes = {Group::kWidth << 5, Group::kWidth << 10}; @@ -1296,37 +1366,31 @@ TEST(Table, ConstructFromInitList) { TEST(Table, CopyConstruct) { IntTable t; - t.max_load_factor(.321f); t.emplace(0); EXPECT_EQ(1, t.size()); { IntTable u(t); EXPECT_EQ(1, u.size()); - EXPECT_EQ(t.max_load_factor(), u.max_load_factor()); EXPECT_THAT(*u.find(0), 0); } { IntTable u{t}; EXPECT_EQ(1, u.size()); - EXPECT_EQ(t.max_load_factor(), u.max_load_factor()); EXPECT_THAT(*u.find(0), 0); } { IntTable u = t; EXPECT_EQ(1, u.size()); - EXPECT_EQ(t.max_load_factor(), u.max_load_factor()); EXPECT_THAT(*u.find(0), 0); } } TEST(Table, CopyConstructWithAlloc) { StringTable t; - t.max_load_factor(.321f); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u(t, Alloc>()); EXPECT_EQ(1, u.size()); - EXPECT_EQ(t.max_load_factor(), u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } @@ -1344,94 +1408,75 @@ TEST(Table, AllocWithExplicitCtor) { TEST(Table, MoveConstruct) { { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u(std::move(t)); EXPECT_EQ(1, u.size()); - EXPECT_EQ(lf, u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u{std::move(t)}; EXPECT_EQ(1, u.size()); - EXPECT_EQ(lf, u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u = std::move(t); EXPECT_EQ(1, u.size()); - EXPECT_EQ(lf, u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } } TEST(Table, MoveConstructWithAlloc) { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u(std::move(t), Alloc>()); EXPECT_EQ(1, u.size()); - EXPECT_EQ(lf, u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } TEST(Table, CopyAssign) { StringTable t; - t.max_load_factor(.321f); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u; u = t; EXPECT_EQ(1, u.size()); - EXPECT_EQ(t.max_load_factor(), u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } TEST(Table, CopySelfAssign) { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); t = *&t; EXPECT_EQ(1, t.size()); - EXPECT_EQ(lf, t.max_load_factor()); EXPECT_THAT(*t.find("a"), Pair("a", "b")); } TEST(Table, MoveAssign) { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u; u = std::move(t); EXPECT_EQ(1, u.size()); - EXPECT_EQ(lf, u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } TEST(Table, Equality) { StringTable t; - std::vector> v = {{"a", "b"}, {"aa", "bb"}}; + std::vector> v = {{"a", "b"}, + {"aa", "bb"}}; t.insert(std::begin(v), std::end(v)); StringTable u = t; EXPECT_EQ(u, t); @@ -1439,20 +1484,24 @@ TEST(Table, Equality) { TEST(Table, Equality2) { StringTable t; - std::vector> v1 = {{"a", "b"}, {"aa", "bb"}}; + std::vector> v1 = {{"a", "b"}, + {"aa", "bb"}}; t.insert(std::begin(v1), std::end(v1)); StringTable u; - std::vector> v2 = {{"a", "a"}, {"aa", "aa"}}; + std::vector> v2 = {{"a", "a"}, + {"aa", "aa"}}; u.insert(std::begin(v2), std::end(v2)); EXPECT_NE(u, t); } TEST(Table, Equality3) { StringTable t; - std::vector> v1 = {{"b", "b"}, {"bb", "bb"}}; + std::vector> v1 = {{"b", "b"}, + {"bb", "bb"}}; t.insert(std::begin(v1), std::end(v1)); StringTable u; - std::vector> v2 = {{"a", "a"}, {"aa", "aa"}}; + std::vector> v2 = {{"a", "a"}, + {"aa", "aa"}}; u.insert(std::begin(v2), std::end(v2)); EXPECT_NE(u, t); } @@ -1677,7 +1726,7 @@ TEST(Nodes, ExtractInsert) { EXPECT_FALSE(node.empty()); StringTable t2; - auto res = t2.insert(std::move(node)); + StringTable::insert_return_type res = t2.insert(std::move(node)); EXPECT_TRUE(res.inserted); EXPECT_THAT(*res.position, Pair(k0, "")); EXPECT_FALSE(res.node); @@ -1707,80 +1756,74 @@ TEST(Nodes, ExtractInsert) { EXPECT_FALSE(node); } -StringTable MakeSimpleTable(size_t size) { - StringTable t; - for (size_t i = 0; i < size; ++i) t.emplace(std::string(1, 'A' + i), ""); +IntTable MakeSimpleTable(size_t size) { + IntTable t; + while (t.size() < size) t.insert(t.size()); return t; } -std::string OrderOfIteration(const StringTable& t) { - std::string order; - for (auto& p : t) order += p.first; - return order; +std::vector OrderOfIteration(const IntTable& t) { + return {t.begin(), t.end()}; } +// These IterationOrderChanges tests depend on non-deterministic behavior. +// We are injecting non-determinism from the pointer of the table, but do so in +// a way that only the page matters. We have to retry enough times to make sure +// we are touching different memory pages to cause the ordering to change. +// We also need to keep the old tables around to avoid getting the same memory +// blocks over and over. TEST(Table, IterationOrderChangesByInstance) { - // Needs to be more than kWidth elements to be able to affect order. - const StringTable reference = MakeSimpleTable(20); - - // Since order is non-deterministic we can't just try once and verify. - // We'll try until we find that order changed. It should not take many tries - // for that. - // Important: we have to keep the old tables around. Otherwise tcmalloc will - // just give us the same blocks and we would be doing the same order again. - std::vector garbage; - for (int i = 0; i < 10; ++i) { - auto trial = MakeSimpleTable(20); - if (OrderOfIteration(trial) != OrderOfIteration(reference)) { - // We are done. - return; + for (size_t size : {2, 6, 12, 20}) { + const auto reference_table = MakeSimpleTable(size); + const auto reference = OrderOfIteration(reference_table); + + std::vector tables; + bool found_difference = false; + for (int i = 0; !found_difference && i < 5000; ++i) { + tables.push_back(MakeSimpleTable(size)); + found_difference = OrderOfIteration(tables.back()) != reference; + } + if (!found_difference) { + FAIL() + << "Iteration order remained the same across many attempts with size " + << size; } - garbage.push_back(std::move(trial)); } - FAIL(); } TEST(Table, IterationOrderChangesOnRehash) { - // Since order is non-deterministic we can't just try once and verify. - // We'll try until we find that order changed. It should not take many tries - // for that. - // Important: we have to keep the old tables around. Otherwise tcmalloc will - // just give us the same blocks and we would be doing the same order again. - std::vector garbage; - for (int i = 0; i < 10; ++i) { - // Needs to be more than kWidth elements to be able to affect order. - StringTable t = MakeSimpleTable(20); - const std::string reference = OrderOfIteration(t); + std::vector garbage; + for (int i = 0; i < 5000; ++i) { + auto t = MakeSimpleTable(20); + const auto reference = OrderOfIteration(t); // Force rehash to the same size. t.rehash(0); - std::string trial = OrderOfIteration(t); + auto trial = OrderOfIteration(t); if (trial != reference) { // We are done. return; } garbage.push_back(std::move(t)); } - FAIL(); + FAIL() << "Iteration order remained the same across many attempts."; } -TEST(Table, IterationOrderChangesForSmallTables) { - // Since order is non-deterministic we can't just try once and verify. - // We'll try until we find that order changed. - // Important: we have to keep the old tables around. Otherwise tcmalloc will - // just give us the same blocks and we would be doing the same order again. - StringTable reference_table = MakeSimpleTable(5); - const std::string reference = OrderOfIteration(reference_table); - std::vector garbage; - for (int i = 0; i < 50; ++i) { - StringTable t = MakeSimpleTable(5); - std::string trial = OrderOfIteration(t); - if (trial != reference) { - // We are done. - return; - } - garbage.push_back(std::move(t)); - } - FAIL() << "Iteration order remained the same across many attempts."; +// Verify that pointers are invalidated as soon as a second element is inserted. +// This prevents dependency on pointer stability on small tables. +TEST(Table, UnstablePointers) { + IntTable table; + + const auto addr = [&](int i) { + return reinterpret_cast(&*table.find(i)); + }; + + table.insert(0); + const uintptr_t old_ptr = addr(0); + + // This causes a rehash. + table.insert(1); + + EXPECT_NE(old_ptr, addr(0)); } // Confirm that we assert if we try to erase() end(). @@ -1799,9 +1842,52 @@ TEST(TableDeathTest, EraseOfEndAsserts) { EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg); } +TEST(RawHashSamplerTest, Sample) { + // Enable the feature even if the prod default is off. + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(100); + + auto& sampler = HashtablezSampler::Global(); + size_t start_size = 0; + start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; }); + + std::vector tables; + for (int i = 0; i < 1000000; ++i) { + tables.emplace_back(); + tables.back().insert(1); + } + size_t end_size = 0; + end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; }); + + EXPECT_NEAR((end_size - start_size) / static_cast(tables.size()), + 0.01, 0.005); +} + +TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) { + // Enable the feature even if the prod default is off. + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(100); + + auto& sampler = HashtablezSampler::Global(); + size_t start_size = 0; + start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; }); + + std::vector tables; + for (int i = 0; i < 1000000; ++i) { + tables.emplace_back(); + tables.back().insert(1); + } + size_t end_size = 0; + end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; }); + + EXPECT_NEAR((end_size - start_size) / static_cast(tables.size()), + 0.00, 0.001); +} + #ifdef ADDRESS_SANITIZER TEST(Sanitizer, PoisoningUnused) { IntTable t; + t.reserve(5); // Insert something to force an allocation. int64_t& v1 = *t.insert(0).first; @@ -1826,5 +1912,5 @@ TEST(Sanitizer, PoisoningOnErase) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/test_instance_tracker.cc b/absl/container/internal/test_instance_tracker.cc index 91441729..f4b283fd 100644 --- a/absl/container/internal/test_instance_tracker.cc +++ b/absl/container/internal/test_instance_tracker.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,7 @@ #include "absl/container/internal/test_instance_tracker.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/test_instance_tracker.h b/absl/container/internal/test_instance_tracker.h index 060077d0..ab7f9f22 100644 --- a/absl/container/internal/test_instance_tracker.h +++ b/absl/container/internal/test_instance_tracker.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,11 +18,13 @@ #include #include +#include "absl/types/compare.h" + namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace test_internal { -// A type that counts number of occurences of the type, the live occurrences of +// A type that counts number of occurrences of the type, the live occurrences of // the type, as well as the number of copies, moves, swaps, and comparisons that // have occurred on the type. This is used as a base class for the copyable, // copyable+movable, and movable types below that are used in actual tests. Use @@ -97,6 +99,14 @@ class BaseCountedInstance { return value_ >= x.value_; } + absl::weak_ordering compare(const BaseCountedInstance& x) const { + ++num_comparisons_; + return value_ < x.value_ + ? absl::weak_ordering::less + : value_ == x.value_ ? absl::weak_ordering::equivalent + : absl::weak_ordering::greater; + } + int value() const { if (!is_live_) std::abort(); return value_; @@ -258,7 +268,7 @@ class MovableOnlyInstance : public BaseCountedInstance { }; } // namespace test_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ diff --git a/absl/container/internal/test_instance_tracker_test.cc b/absl/container/internal/test_instance_tracker_test.cc index 0ae57636..1c6a4fa7 100644 --- a/absl/container/internal/test_instance_tracker_test.cc +++ b/absl/container/internal/test_instance_tracker_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -174,6 +174,8 @@ TEST(TestInstanceTracker, Comparisons) { EXPECT_EQ(5, tracker.comparisons()); EXPECT_FALSE(one >= two); EXPECT_EQ(6, tracker.comparisons()); + EXPECT_TRUE(one.compare(two) < 0); // NOLINT + EXPECT_EQ(7, tracker.comparisons()); tracker.ResetCopiesMovesSwaps(); EXPECT_EQ(0, tracker.comparisons()); diff --git a/absl/container/internal/tracked.h b/absl/container/internal/tracked.h index f72c46ea..e9e6b95d 100644 --- a/absl/container/internal/tracked.h +++ b/absl/container/internal/tracked.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // A class that tracks its copies and moves so that it can be queried in tests. @@ -74,7 +74,7 @@ class Tracked { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 14ceeecb..b64b5520 100644 --- a/absl/container/internal/unordered_map_constructor_test.h +++ b/absl/container/internal/unordered_map_constructor_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,13 +24,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template class ConstructorTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(ConstructorTest); +TYPED_TEST_SUITE_P(ConstructorTest); TYPED_TEST_P(ConstructorTest, NoArgs) { TypeParam m; @@ -84,8 +84,28 @@ TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { +template +struct is_std_unordered_map : std::false_type {}; + +template +struct is_std_unordered_map> : std::true_type {}; + #if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +using has_cxx14_std_apis = std::true_type; +#else +using has_cxx14_std_apis = std::false_type; +#endif + +template +using expect_cxx14_apis = + absl::disjunction>, + has_cxx14_std_apis>; + +template +void BucketCountAllocTest(std::false_type) {} + +template +void BucketCountAllocTest(std::true_type) { using A = typename TypeParam::allocator_type; A alloc(0); TypeParam m(123, alloc); @@ -93,11 +113,17 @@ TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { EXPECT_TRUE(m.empty()); EXPECT_THAT(m, ::testing::UnorderedElementsAre()); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { + BucketCountAllocTest(expect_cxx14_apis()); +} + +template +void BucketCountHashAllocTest(std::false_type) {} + +template +void BucketCountHashAllocTest(std::true_type) { using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; H hasher; @@ -108,18 +134,38 @@ TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { EXPECT_TRUE(m.empty()); EXPECT_THAT(m, ::testing::UnorderedElementsAre()); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, BucketAlloc) { +TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { + BucketCountHashAllocTest(expect_cxx14_apis()); +} + #if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +using has_alloc_std_constructors = std::true_type; +#else +using has_alloc_std_constructors = std::false_type; +#endif + +template +using expect_alloc_constructors = + absl::disjunction>, + has_alloc_std_constructors>; + +template +void AllocTest(std::false_type) {} + +template +void AllocTest(std::true_type) { using A = typename TypeParam::allocator_type; A alloc(0); TypeParam m(alloc); EXPECT_EQ(m.get_allocator(), alloc); EXPECT_TRUE(m.empty()); EXPECT_THAT(m, ::testing::UnorderedElementsAre()); -#endif +} + +TYPED_TEST_P(ConstructorTest, Alloc) { + AllocTest(expect_alloc_constructors()); } TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { @@ -141,8 +187,11 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +template +void InputIteratorBucketAllocTest(std::false_type) {} + +template +void InputIteratorBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using A = typename TypeParam::allocator_type; A alloc(0); @@ -153,11 +202,17 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { + InputIteratorBucketAllocTest(expect_cxx14_apis()); +} + +template +void InputIteratorBucketHashAllocTest(std::false_type) {} + +template +void InputIteratorBucketHashAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; @@ -171,7 +226,10 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { + InputIteratorBucketHashAllocTest(expect_cxx14_apis()); } TYPED_TEST_P(ConstructorTest, CopyConstructor) { @@ -191,8 +249,11 @@ TYPED_TEST_P(ConstructorTest, CopyConstructor) { EXPECT_EQ(m, n); } -TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { -#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +template +void CopyConstructorAllocTest(std::false_type) {} + +template +void CopyConstructorAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -207,7 +268,10 @@ TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_NE(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); -#endif +} + +TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { + CopyConstructorAllocTest(expect_alloc_constructors()); } // TODO(alkis): Test non-propagating allocators on copy constructors. @@ -230,8 +294,11 @@ TYPED_TEST_P(ConstructorTest, MoveConstructor) { EXPECT_EQ(m, n); } -TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { -#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +template +void MoveConstructorAllocTest(std::false_type) {} + +template +void MoveConstructorAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -247,7 +314,10 @@ TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_NE(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); -#endif +} + +TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { + MoveConstructorAllocTest(expect_alloc_constructors()); } // TODO(alkis): Test non-propagating allocators on move constructors. @@ -270,8 +340,11 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +template +void InitializerListBucketAllocTest(std::false_type) {} + +template +void InitializerListBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using A = typename TypeParam::allocator_type; hash_internal::Generator gen; @@ -281,11 +354,17 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { + InitializerListBucketAllocTest(expect_cxx14_apis()); +} + +template +void InitializerListBucketHashAllocTest(std::false_type) {} + +template +void InitializerListBucketHashAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; @@ -298,7 +377,10 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif +} + +TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { + InitializerListBucketHashAllocTest(expect_cxx14_apis()); } TYPED_TEST_P(ConstructorTest, Assignment) { @@ -391,17 +473,17 @@ TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { REGISTER_TYPED_TEST_CASE_P( ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, - BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, - BucketAlloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, + BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, + InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc, MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc, InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment, - MoveAssignment, AssignmentFromInitializerList, - AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting, + MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting, + MoveAssignmentOverwritesExisting, AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 d767aa8d..9ad78a79 100644 --- a/absl/container/internal/unordered_map_lookup_test.h +++ b/absl/container/internal/unordered_map_lookup_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,13 +21,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template class LookupTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(LookupTest); +TYPED_TEST_SUITE_P(LookupTest); TYPED_TEST_P(LookupTest, At) { using T = hash_internal::GeneratedType; @@ -111,7 +111,7 @@ REGISTER_TYPED_TEST_CASE_P(LookupTest, At, OperatorBracket, Count, Find, EqualRange); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 new file mode 100644 index 00000000..c4600405 --- /dev/null +++ b/absl/container/internal/unordered_map_members_test.h @@ -0,0 +1,87 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ + +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +template +class MembersTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(MembersTest); + +template +void UseType() {} + +TYPED_TEST_P(MembersTest, Typedefs) { + EXPECT_TRUE((std::is_same, + typename TypeParam::value_type>())); + EXPECT_TRUE((absl::conjunction< + absl::negation>, + std::is_integral>())); + EXPECT_TRUE((absl::conjunction< + std::is_signed, + std::is_integral>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval()( + std::declval())), + size_t>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval()( + std::declval(), + std::declval())), + bool>())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same::pointer, + typename TypeParam::pointer>())); + EXPECT_TRUE( + (std::is_same::const_pointer, + typename TypeParam::const_pointer>())); +} + +TYPED_TEST_P(MembersTest, SimpleFunctions) { + EXPECT_GT(TypeParam().max_size(), 0); +} + +TYPED_TEST_P(MembersTest, BeginEnd) { + TypeParam t = {typename TypeParam::value_type{}}; + EXPECT_EQ(t.begin(), t.cbegin()); + EXPECT_EQ(t.end(), t.cend()); + EXPECT_NE(t.begin(), t.end()); + EXPECT_NE(t.cbegin(), t.cend()); +} + +REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // 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 5d7f1fe3..89dd7894 100644 --- a/absl/container/internal/unordered_map_modifiers_test.h +++ b/absl/container/internal/unordered_map_modifiers_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,13 +21,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template class ModifiersTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(ModifiersTest); +TYPED_TEST_SUITE_P(ModifiersTest); TYPED_TEST_P(ModifiersTest, Clear) { using T = hash_internal::GeneratedType; @@ -269,7 +269,7 @@ REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, Erase, EraseRange, EraseKey, Swap); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 548f69f7..51a90af8 100644 --- a/absl/container/internal/unordered_map_test.cc +++ b/absl/container/internal/unordered_map_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,10 +16,11 @@ #include "absl/container/internal/unordered_map_constructor_test.h" #include "absl/container/internal/unordered_map_lookup_test.h" +#include "absl/container/internal/unordered_map_members_test.h" #include "absl/container/internal/unordered_map_modifiers_test.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -30,11 +31,12 @@ using MapTypes = ::testing::Types< StatefulTestingEqual, Alloc>>>; -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, ConstructorTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, LookupTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, ModifiersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ConstructorTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, LookupTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, MembersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ModifiersTest, MapTypes); } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/unordered_set_constructor_test.h b/absl/container/internal/unordered_set_constructor_test.h index f370b249..ac73a896 100644 --- a/absl/container/internal/unordered_set_constructor_test.h +++ b/absl/container/internal/unordered_set_constructor_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,21 +16,23 @@ #define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_ #include +#include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/hash_policy_testing.h" +#include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template class ConstructorTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(ConstructorTest); +TYPED_TEST_SUITE_P(ConstructorTest); TYPED_TEST_P(ConstructorTest, NoArgs) { TypeParam m; @@ -92,8 +94,28 @@ TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) { EXPECT_GE(cm.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { +template +struct is_std_unordered_set : std::false_type {}; + +template +struct is_std_unordered_set> : std::true_type {}; + #if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +using has_cxx14_std_apis = std::true_type; +#else +using has_cxx14_std_apis = std::false_type; +#endif + +template +using expect_cxx14_apis = + absl::disjunction>, + has_cxx14_std_apis>; + +template +void BucketCountAllocTest(std::false_type) {} + +template +void BucketCountAllocTest(std::true_type) { using A = typename TypeParam::allocator_type; A alloc(0); TypeParam m(123, alloc); @@ -101,11 +123,17 @@ TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { EXPECT_TRUE(m.empty()); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { + BucketCountAllocTest(expect_cxx14_apis()); +} + +template +void BucketCountHashAllocTest(std::false_type) {} + +template +void BucketCountHashAllocTest(std::true_type) { using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; H hasher; @@ -116,18 +144,38 @@ TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { EXPECT_TRUE(m.empty()); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, BucketAlloc) { +TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { + BucketCountHashAllocTest(expect_cxx14_apis()); +} + #if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +using has_alloc_std_constructors = std::true_type; +#else +using has_alloc_std_constructors = std::false_type; +#endif + +template +using expect_alloc_constructors = + absl::disjunction>, + has_alloc_std_constructors>; + +template +void AllocTest(std::false_type) {} + +template +void AllocTest(std::true_type) { using A = typename TypeParam::allocator_type; A alloc(0); TypeParam m(alloc); EXPECT_EQ(m.get_allocator(), alloc); EXPECT_TRUE(m.empty()); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); -#endif +} + +TYPED_TEST_P(ConstructorTest, Alloc) { + AllocTest(expect_alloc_constructors()); } TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { @@ -149,8 +197,11 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +template +void InputIteratorBucketAllocTest(std::false_type) {} + +template +void InputIteratorBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using A = typename TypeParam::allocator_type; A alloc(0); @@ -161,11 +212,17 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { + InputIteratorBucketAllocTest(expect_cxx14_apis()); +} + +template +void InputIteratorBucketHashAllocTest(std::false_type) {} + +template +void InputIteratorBucketHashAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; @@ -179,7 +236,10 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { + InputIteratorBucketHashAllocTest(expect_cxx14_apis()); } TYPED_TEST_P(ConstructorTest, CopyConstructor) { @@ -197,10 +257,14 @@ TYPED_TEST_P(ConstructorTest, CopyConstructor) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_EQ(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); + EXPECT_NE(TypeParam(0, hasher, equal, alloc), n); } -TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { -#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +template +void CopyConstructorAllocTest(std::false_type) {} + +template +void CopyConstructorAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -215,7 +279,10 @@ TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_NE(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); -#endif +} + +TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { + CopyConstructorAllocTest(expect_alloc_constructors()); } // TODO(alkis): Test non-propagating allocators on copy constructors. @@ -238,8 +305,11 @@ TYPED_TEST_P(ConstructorTest, MoveConstructor) { EXPECT_EQ(m, n); } -TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { -#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +template +void MoveConstructorAllocTest(std::false_type) {} + +template +void MoveConstructorAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -255,7 +325,10 @@ TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_NE(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); -#endif +} + +TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { + MoveConstructorAllocTest(expect_alloc_constructors()); } // TODO(alkis): Test non-propagating allocators on move constructors. @@ -278,8 +351,11 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +template +void InitializerListBucketAllocTest(std::false_type) {} + +template +void InitializerListBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using A = typename TypeParam::allocator_type; hash_internal::Generator gen; @@ -289,11 +365,17 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { + InitializerListBucketAllocTest(expect_cxx14_apis()); +} + +template +void InitializerListBucketHashAllocTest(std::false_type) {} + +template +void InitializerListBucketHashAllocTest(std::true_type) { using T = hash_internal::GeneratedType; using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; @@ -306,10 +388,13 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, Assignment) { +TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { + InitializerListBucketHashAllocTest(expect_cxx14_apis()); +} + +TYPED_TEST_P(ConstructorTest, CopyAssignment) { using T = hash_internal::GeneratedType; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -395,17 +480,17 @@ TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { REGISTER_TYPED_TEST_CASE_P( ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, - BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, - BucketAlloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, + BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, + InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc, MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc, - InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment, - MoveAssignment, AssignmentFromInitializerList, - AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting, + InitializerListBucketAlloc, InitializerListBucketHashAlloc, CopyAssignment, + MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting, + MoveAssignmentOverwritesExisting, AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 9174279a..722fb1c2 100644 --- a/absl/container/internal/unordered_set_lookup_test.h +++ b/absl/container/internal/unordered_set_lookup_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,13 +21,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template class LookupTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(LookupTest); +TYPED_TEST_SUITE_P(LookupTest); TYPED_TEST_P(LookupTest, Count) { using T = hash_internal::GeneratedType; @@ -85,7 +85,7 @@ TYPED_TEST_P(LookupTest, EqualRange) { REGISTER_TYPED_TEST_CASE_P(LookupTest, Count, Find, EqualRange); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 new file mode 100644 index 00000000..756a95cb --- /dev/null +++ b/absl/container/internal/unordered_set_members_test.h @@ -0,0 +1,86 @@ +// 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_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_ + +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +template +class MembersTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(MembersTest); + +template +void UseType() {} + +TYPED_TEST_P(MembersTest, Typedefs) { + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((absl::conjunction< + absl::negation>, + std::is_integral>())); + EXPECT_TRUE((absl::conjunction< + std::is_signed, + std::is_integral>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval()( + std::declval())), + size_t>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval()( + std::declval(), + std::declval())), + bool>())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same())); + EXPECT_TRUE((std::is_same::pointer, + typename TypeParam::pointer>())); + EXPECT_TRUE( + (std::is_same::const_pointer, + typename TypeParam::const_pointer>())); +} + +TYPED_TEST_P(MembersTest, SimpleFunctions) { + EXPECT_GT(TypeParam().max_size(), 0); +} + +TYPED_TEST_P(MembersTest, BeginEnd) { + TypeParam t = {typename TypeParam::value_type{}}; + EXPECT_EQ(t.begin(), t.cbegin()); + EXPECT_EQ(t.end(), t.cend()); + EXPECT_NE(t.begin(), t.end()); + EXPECT_NE(t.cbegin(), t.cend()); +} + +REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // 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 0a1e9b1b..d3e534d3 100644 --- a/absl/container/internal/unordered_set_modifiers_test.h +++ b/absl/container/internal/unordered_set_modifiers_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,13 +21,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template class ModifiersTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(ModifiersTest); +TYPED_TEST_SUITE_P(ModifiersTest); TYPED_TEST_P(ModifiersTest, Clear) { using T = hash_internal::GeneratedType; @@ -184,7 +184,7 @@ REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, EraseKey, Swap); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 263059eb..2356e187 100644 --- a/absl/container/internal/unordered_set_test.cc +++ b/absl/container/internal/unordered_set_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,24 +16,26 @@ #include "absl/container/internal/unordered_set_constructor_test.h" #include "absl/container/internal/unordered_set_lookup_test.h" +#include "absl/container/internal/unordered_set_members_test.h" #include "absl/container/internal/unordered_set_modifiers_test.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { -using SetTypes = - ::testing::Types>, - std::unordered_set>>; +using SetTypes = ::testing::Types< + std::unordered_set>, + std::unordered_set>>; -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, ConstructorTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, LookupTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, ModifiersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ConstructorTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, LookupTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, MembersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ModifiersTest, SetTypes); } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index 48c7752e..addf120f 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -48,7 +48,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template class NodeHashMapPolicy; @@ -578,7 +578,7 @@ struct IsUnorderedContainer< } // namespace container_algorithm_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 76a387b8..7ce7ca02 100644 --- a/absl/container/node_hash_map_test.cc +++ b/absl/container/node_hash_map_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,10 +17,11 @@ #include "absl/container/internal/tracked.h" #include "absl/container/internal/unordered_map_constructor_test.h" #include "absl/container/internal/unordered_map_lookup_test.h" +#include "absl/container/internal/unordered_map_members_test.h" #include "absl/container/internal/unordered_map_modifiers_test.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -35,9 +36,10 @@ using MapTypes = ::testing::Types< StatefulTestingEqual, Alloc>>>; -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, ConstructorTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, LookupTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, ModifiersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ConstructorTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, LookupTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, MembersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ModifiersTest, MapTypes); using M = absl::node_hash_map>; @@ -216,5 +218,5 @@ TEST(NodeHashMap, MergeExtractInsert) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index c4179195..103d32d2 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -44,7 +44,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template struct NodeHashSetPolicy; @@ -484,7 +484,7 @@ struct IsUnorderedContainer> : std::true_type {}; } // namespace container_algorithm_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 59f25285..65d125ed 100644 --- a/absl/container/node_hash_set_test.cc +++ b/absl/container/node_hash_set_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,10 +16,11 @@ #include "absl/container/internal/unordered_set_constructor_test.h" #include "absl/container/internal/unordered_set_lookup_test.h" +#include "absl/container/internal/unordered_set_members_test.h" #include "absl/container/internal/unordered_set_modifiers_test.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { using ::absl::container_internal::hash_internal::Enum; @@ -30,14 +31,15 @@ using ::testing::UnorderedElementsAre; using SetTypes = ::testing::Types< node_hash_set>, node_hash_set>, + Alloc>, node_hash_set>, node_hash_set>>; -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, ConstructorTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, LookupTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, ModifiersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, ConstructorTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, LookupTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, MembersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, ModifiersTest, SetTypes); TEST(NodeHashSet, MoveableNotCopyableCompiles) { node_hash_set> t; @@ -101,5 +103,5 @@ TEST(NodeHashSet, MergeExtractInsert) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/copts.bzl b/absl/copts.bzl deleted file mode 100644 index 49c4c9e0..00000000 --- a/absl/copts.bzl +++ /dev/null @@ -1,170 +0,0 @@ -"""absl specific copts. - -Flags specified here must not impact ABI. Code compiled with and without these -opts will be linked together, and in some cases headers compiled with and -without these options will be part of the same program. - -DO NOT CHANGE THIS FILE WITHOUT CHANGING THE SAME FLAG IN absl/CMake/AbseilConfigureCopts.cmake!! -""" -GCC_FLAGS = [ - "-Wall", - "-Wextra", - "-Wcast-qual", - "-Wconversion-null", - "-Wmissing-declarations", - "-Woverlength-strings", - "-Wpointer-arith", - "-Wunused-local-typedefs", - "-Wunused-result", - "-Wvarargs", - "-Wvla", # variable-length array - "-Wwrite-strings", - # Google style does not use unsigned integers, though STL containers - # have unsigned types. - "-Wno-sign-compare", -] - -GCC_TEST_FLAGS = [ - "-Wno-conversion-null", - "-Wno-missing-declarations", - "-Wno-sign-compare", - "-Wno-unused-function", - "-Wno-unused-parameter", - "-Wno-unused-private-field", -] - -# Docs on single flags is preceded by a comment. -# Docs on groups of flags is preceded by ###. - -LLVM_FLAGS = [ - "-Wall", - "-Wextra", - "-Weverything", - # Abseil does not support C++98 - "-Wno-c++98-compat-pedantic", - # Turns off all implicit conversion warnings. Most are re-enabled below. - "-Wno-conversion", - "-Wno-covered-switch-default", - "-Wno-deprecated", - "-Wno-disabled-macro-expansion", - "-Wno-double-promotion", - ### - # Turned off as they include valid C++ code. - "-Wno-comma", - "-Wno-extra-semi", - "-Wno-packed", - "-Wno-padded", - ### - # Google style does not use unsigned integers, though STL containers - # have unsigned types. - "-Wno-sign-compare", - ### - "-Wno-float-conversion", - "-Wno-float-equal", - "-Wno-format-nonliteral", - # Too aggressive: warns on Clang extensions enclosed in Clang-only - # compilation paths. - "-Wno-gcc-compat", - ### - # Some internal globals are necessary. Don't do this at home. - "-Wno-global-constructors", - "-Wno-exit-time-destructors", - ### - "-Wno-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 - "-Wno-range-loop-analysis", - "-Wno-reserved-id-macro", - "-Wno-shorten-64-to-32", - "-Wno-switch-enum", - "-Wno-thread-safety-negative", - "-Wno-undef", - "-Wno-unknown-warning-option", - "-Wno-unreachable-code", - # Causes warnings on include guards - "-Wno-unused-macros", - "-Wno-weak-vtables", - ### - # Implicit conversion warnings turned off by -Wno-conversion - # which are re-enabled below. - "-Wbitfield-enum-conversion", - "-Wbool-conversion", - "-Wconstant-conversion", - "-Wenum-conversion", - "-Wint-conversion", - "-Wliteral-conversion", - "-Wnon-literal-null-conversion", - "-Wnull-conversion", - "-Wobjc-literal-conversion", - "-Wno-sign-conversion", - "-Wstring-conversion", - ### -] - -LLVM_TEST_FLAGS = [ - "-Wno-c99-extensions", - "-Wno-missing-noreturn", - "-Wno-missing-prototypes", - "-Wno-missing-variable-declarations", - "-Wno-null-conversion", - "-Wno-shadow", - "-Wno-shift-sign-overflow", - "-Wno-sign-compare", - "-Wno-unused-function", - "-Wno-unused-member-function", - "-Wno-unused-parameter", - "-Wno-unused-private-field", - "-Wno-unused-template", - "-Wno-used-but-marked-unused", - "-Wno-zero-as-null-pointer-constant", - # gtest depends on this GNU extension being offered. - "-Wno-gnu-zero-variadic-macro-arguments", -] - -MSVC_FLAGS = [ - "/W3", - "/wd4005", # macro-redefinition - "/wd4068", # unknown pragma - "/wd4180", # qualifier applied to function type has no meaning; ignored - "/wd4244", # conversion from 'type1' to 'type2', possible loss of data - "/wd4267", # conversion from 'size_t' to 'type', possible loss of data - "/wd4800", # forcing value to bool 'true' or 'false' (performance warning) - "/DNOMINMAX", # Don't define min and max macros (windows.h) - "/DWIN32_LEAN_AND_MEAN", # Don't bloat namespace with incompatible winsock versions. - "/D_CRT_SECURE_NO_WARNINGS", # Don't warn about usage of insecure C functions. - "/D_SCL_SECURE_NO_WARNINGS", # Don't warm when the compiler encounters a function or - # variable that is marked as deprecated (same as /wd4996). - "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", # Introduced in VS 2017 15.8, - # before the member type would non-conformingly have an alignment of only alignof(max_align_t). -] - -MSVC_TEST_FLAGS = [ - "/wd4018", # signed/unsigned mismatch - "/wd4101", # unreferenced local variable - "/wd4503", # decorated name length exceeded, name was truncated -] - -# /Wall with msvc includes unhelpful warnings such as C4711, C4710, ... -ABSL_DEFAULT_COPTS = select({ - "//absl:windows": MSVC_FLAGS, - "//absl:llvm_compiler": LLVM_FLAGS, - "//conditions:default": GCC_FLAGS, -}) - -# in absence of modules (--compiler=gcc or -c opt), cc_tests leak their copts -# to their (included header) dependencies and fail to build outside absl -ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({ - "//absl:windows": MSVC_TEST_FLAGS, - "//absl:llvm_compiler": LLVM_TEST_FLAGS, - "//conditions:default": GCC_TEST_FLAGS, -}) - -ABSL_EXCEPTIONS_FLAG = select({ - "//absl:windows": ["/U_HAS_EXCEPTIONS", "/D_HAS_EXCEPTIONS=1", "/EHsc"], - "//conditions:default": ["-fexceptions"], -}) - -ABSL_EXCEPTIONS_FLAG_LINKOPTS = select({ - "//conditions:default": [], -}) diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake new file mode 100644 index 00000000..4ece4c6f --- /dev/null +++ b/absl/copts/AbseilConfigureCopts.cmake @@ -0,0 +1,60 @@ +# See absl/copts/copts.py and absl/copts/generate_copts.py +include(GENERATED_AbseilCopts) + +set(ABSL_LSAN_LINKOPTS "") +set(ABSL_HAVE_LSAN OFF) +set(ABSL_DEFAULT_LINKOPTS "") + +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 + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5) + set(ABSL_LSAN_LINKOPTS "-fsanitize=leak") + set(ABSL_HAVE_LSAN ON) + endif() + endif() + endif() +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}") + message(STATUS "No CMAKE_CXX_STANDARD set, assuming 11") + set(ABSL_CXX_STANDARD 11) +else() + set(ABSL_CXX_STANDARD "${CMAKE_CXX_STANDARD}") +endif() diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake new file mode 100644 index 00000000..01bd40b2 --- /dev/null +++ b/absl/copts/GENERATED_AbseilCopts.cmake @@ -0,0 +1,236 @@ +# GENERATED! DO NOT MANUALLY EDIT THIS FILE. +# +# (1) Edit absl/copts/copts.py. +# (2) Run `python /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" + "-Wno-conversion" + "-Wno-covered-switch-default" + "-Wno-deprecated" + "-Wno-disabled-macro-expansion" + "-Wno-double-promotion" + "-Wno-comma" + "-Wno-extra-semi" + "-Wno-extra-semi-stmt" + "-Wno-packed" + "-Wno-padded" + "-Wno-sign-compare" + "-Wno-float-conversion" + "-Wno-float-equal" + "-Wno-format-nonliteral" + "-Wno-gcc-compat" + "-Wno-global-constructors" + "-Wno-exit-time-destructors" + "-Wno-nested-anon-types" + "-Wno-non-modular-include-in-module" + "-Wno-old-style-cast" + "-Wno-range-loop-analysis" + "-Wno-reserved-id-macro" + "-Wno-shorten-64-to-32" + "-Wno-switch-enum" + "-Wno-thread-safety-negative" + "-Wno-unknown-warning-option" + "-Wno-unreachable-code" + "-Wno-unused-macros" + "-Wno-weak-vtables" + "-Wno-zero-as-null-pointer-constant" + "-Wbitfield-enum-conversion" + "-Wbool-conversion" + "-Wconstant-conversion" + "-Wenum-conversion" + "-Wint-conversion" + "-Wliteral-conversion" + "-Wnon-literal-null-conversion" + "-Wnull-conversion" + "-Wobjc-literal-conversion" + "-Wno-sign-conversion" + "-Wstring-conversion" + "/DNOMINMAX" + "/DWIN32_LEAN_AND_MEAN" + "/D_CRT_SECURE_NO_WARNINGS" + "/D_SCL_SECURE_NO_WARNINGS" + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE" +) + +list(APPEND ABSL_CLANG_CL_TEST_FLAGS + "-Wno-c99-extensions" + "-Wno-deprecated-declarations" + "-Wno-missing-noreturn" + "-Wno-missing-prototypes" + "-Wno-missing-variable-declarations" + "-Wno-null-conversion" + "-Wno-shadow" + "-Wno-shift-sign-overflow" + "-Wno-sign-compare" + "-Wno-unused-function" + "-Wno-unused-member-function" + "-Wno-unused-parameter" + "-Wno-unused-private-field" + "-Wno-unused-template" + "-Wno-used-but-marked-unused" + "-Wno-zero-as-null-pointer-constant" + "-Wno-gnu-zero-variadic-macro-arguments" +) + +list(APPEND ABSL_GCC_EXCEPTIONS_FLAGS + "-fexceptions" +) + +list(APPEND ABSL_GCC_FLAGS + "-Wall" + "-Wextra" + "-Wcast-qual" + "-Wconversion-null" + "-Wmissing-declarations" + "-Woverlength-strings" + "-Wpointer-arith" + "-Wunused-local-typedefs" + "-Wunused-result" + "-Wvarargs" + "-Wvla" + "-Wwrite-strings" + "-Wno-missing-field-initializers" + "-Wno-sign-compare" +) + +list(APPEND ABSL_GCC_TEST_FLAGS + "-Wno-conversion-null" + "-Wno-deprecated-declarations" + "-Wno-missing-declarations" + "-Wno-sign-compare" + "-Wno-unused-function" + "-Wno-unused-parameter" + "-Wno-unused-private-field" +) + +list(APPEND ABSL_LLVM_EXCEPTIONS_FLAGS + "-fexceptions" +) + +list(APPEND ABSL_LLVM_FLAGS + "-Wall" + "-Wextra" + "-Weverything" + "-Wno-c++98-compat-pedantic" + "-Wno-conversion" + "-Wno-covered-switch-default" + "-Wno-deprecated" + "-Wno-disabled-macro-expansion" + "-Wno-double-promotion" + "-Wno-comma" + "-Wno-extra-semi" + "-Wno-extra-semi-stmt" + "-Wno-packed" + "-Wno-padded" + "-Wno-sign-compare" + "-Wno-float-conversion" + "-Wno-float-equal" + "-Wno-format-nonliteral" + "-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" + "-Wno-reserved-id-macro" + "-Wno-shorten-64-to-32" + "-Wno-switch-enum" + "-Wno-thread-safety-negative" + "-Wno-unknown-warning-option" + "-Wno-unreachable-code" + "-Wno-unused-macros" + "-Wno-weak-vtables" + "-Wno-zero-as-null-pointer-constant" + "-Wbitfield-enum-conversion" + "-Wbool-conversion" + "-Wconstant-conversion" + "-Wenum-conversion" + "-Wint-conversion" + "-Wliteral-conversion" + "-Wnon-literal-null-conversion" + "-Wnull-conversion" + "-Wobjc-literal-conversion" + "-Wno-sign-conversion" + "-Wstring-conversion" +) + +list(APPEND ABSL_LLVM_TEST_FLAGS + "-Wno-c99-extensions" + "-Wno-deprecated-declarations" + "-Wno-missing-noreturn" + "-Wno-missing-prototypes" + "-Wno-missing-variable-declarations" + "-Wno-null-conversion" + "-Wno-shadow" + "-Wno-shift-sign-overflow" + "-Wno-sign-compare" + "-Wno-unused-function" + "-Wno-unused-member-function" + "-Wno-unused-parameter" + "-Wno-unused-private-field" + "-Wno-unused-template" + "-Wno-used-but-marked-unused" + "-Wno-zero-as-null-pointer-constant" + "-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" + "/DWIN32_LEAN_AND_MEAN" + "/D_CRT_SECURE_NO_WARNINGS" + "/D_SCL_SECURE_NO_WARNINGS" + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE" + "/wd4005" + "/wd4068" + "/wd4180" + "/wd4244" + "/wd4267" + "/wd4503" + "/wd4800" +) + +list(APPEND ABSL_MSVC_LINKOPTS + "-ignore:4221" +) + +list(APPEND ABSL_MSVC_TEST_FLAGS + "/wd4018" + "/wd4101" + "/wd4503" + "/wd4996" + "/DNOMINMAX" +) + +list(APPEND ABSL_RANDOM_HWAES_ARM32_FLAGS + "-mfpu=neon" +) + +list(APPEND ABSL_RANDOM_HWAES_ARM64_FLAGS + "-march=armv8-a+crypto" +) + +list(APPEND ABSL_RANDOM_HWAES_MSVC_X64_FLAGS + "/O2" + "/Ob2" +) + +list(APPEND ABSL_RANDOM_HWAES_X64_FLAGS + "-maes" + "-msse4.1" +) diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl new file mode 100644 index 00000000..82f332f4 --- /dev/null +++ b/absl/copts/GENERATED_copts.bzl @@ -0,0 +1,237 @@ +"""GENERATED! DO NOT MANUALLY EDIT THIS FILE. + +(1) Edit absl/copts/copts.py. +(2) Run `python /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", + "-Wno-conversion", + "-Wno-covered-switch-default", + "-Wno-deprecated", + "-Wno-disabled-macro-expansion", + "-Wno-double-promotion", + "-Wno-comma", + "-Wno-extra-semi", + "-Wno-extra-semi-stmt", + "-Wno-packed", + "-Wno-padded", + "-Wno-sign-compare", + "-Wno-float-conversion", + "-Wno-float-equal", + "-Wno-format-nonliteral", + "-Wno-gcc-compat", + "-Wno-global-constructors", + "-Wno-exit-time-destructors", + "-Wno-nested-anon-types", + "-Wno-non-modular-include-in-module", + "-Wno-old-style-cast", + "-Wno-range-loop-analysis", + "-Wno-reserved-id-macro", + "-Wno-shorten-64-to-32", + "-Wno-switch-enum", + "-Wno-thread-safety-negative", + "-Wno-unknown-warning-option", + "-Wno-unreachable-code", + "-Wno-unused-macros", + "-Wno-weak-vtables", + "-Wno-zero-as-null-pointer-constant", + "-Wbitfield-enum-conversion", + "-Wbool-conversion", + "-Wconstant-conversion", + "-Wenum-conversion", + "-Wint-conversion", + "-Wliteral-conversion", + "-Wnon-literal-null-conversion", + "-Wnull-conversion", + "-Wobjc-literal-conversion", + "-Wno-sign-conversion", + "-Wstring-conversion", + "/DNOMINMAX", + "/DWIN32_LEAN_AND_MEAN", + "/D_CRT_SECURE_NO_WARNINGS", + "/D_SCL_SECURE_NO_WARNINGS", + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", +] + +ABSL_CLANG_CL_TEST_FLAGS = [ + "-Wno-c99-extensions", + "-Wno-deprecated-declarations", + "-Wno-missing-noreturn", + "-Wno-missing-prototypes", + "-Wno-missing-variable-declarations", + "-Wno-null-conversion", + "-Wno-shadow", + "-Wno-shift-sign-overflow", + "-Wno-sign-compare", + "-Wno-unused-function", + "-Wno-unused-member-function", + "-Wno-unused-parameter", + "-Wno-unused-private-field", + "-Wno-unused-template", + "-Wno-used-but-marked-unused", + "-Wno-zero-as-null-pointer-constant", + "-Wno-gnu-zero-variadic-macro-arguments", +] + +ABSL_GCC_EXCEPTIONS_FLAGS = [ + "-fexceptions", +] + +ABSL_GCC_FLAGS = [ + "-Wall", + "-Wextra", + "-Wcast-qual", + "-Wconversion-null", + "-Wmissing-declarations", + "-Woverlength-strings", + "-Wpointer-arith", + "-Wunused-local-typedefs", + "-Wunused-result", + "-Wvarargs", + "-Wvla", + "-Wwrite-strings", + "-Wno-missing-field-initializers", + "-Wno-sign-compare", +] + +ABSL_GCC_TEST_FLAGS = [ + "-Wno-conversion-null", + "-Wno-deprecated-declarations", + "-Wno-missing-declarations", + "-Wno-sign-compare", + "-Wno-unused-function", + "-Wno-unused-parameter", + "-Wno-unused-private-field", +] + +ABSL_LLVM_EXCEPTIONS_FLAGS = [ + "-fexceptions", +] + +ABSL_LLVM_FLAGS = [ + "-Wall", + "-Wextra", + "-Weverything", + "-Wno-c++98-compat-pedantic", + "-Wno-conversion", + "-Wno-covered-switch-default", + "-Wno-deprecated", + "-Wno-disabled-macro-expansion", + "-Wno-double-promotion", + "-Wno-comma", + "-Wno-extra-semi", + "-Wno-extra-semi-stmt", + "-Wno-packed", + "-Wno-padded", + "-Wno-sign-compare", + "-Wno-float-conversion", + "-Wno-float-equal", + "-Wno-format-nonliteral", + "-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", + "-Wno-reserved-id-macro", + "-Wno-shorten-64-to-32", + "-Wno-switch-enum", + "-Wno-thread-safety-negative", + "-Wno-unknown-warning-option", + "-Wno-unreachable-code", + "-Wno-unused-macros", + "-Wno-weak-vtables", + "-Wno-zero-as-null-pointer-constant", + "-Wbitfield-enum-conversion", + "-Wbool-conversion", + "-Wconstant-conversion", + "-Wenum-conversion", + "-Wint-conversion", + "-Wliteral-conversion", + "-Wnon-literal-null-conversion", + "-Wnull-conversion", + "-Wobjc-literal-conversion", + "-Wno-sign-conversion", + "-Wstring-conversion", +] + +ABSL_LLVM_TEST_FLAGS = [ + "-Wno-c99-extensions", + "-Wno-deprecated-declarations", + "-Wno-missing-noreturn", + "-Wno-missing-prototypes", + "-Wno-missing-variable-declarations", + "-Wno-null-conversion", + "-Wno-shadow", + "-Wno-shift-sign-overflow", + "-Wno-sign-compare", + "-Wno-unused-function", + "-Wno-unused-member-function", + "-Wno-unused-parameter", + "-Wno-unused-private-field", + "-Wno-unused-template", + "-Wno-used-but-marked-unused", + "-Wno-zero-as-null-pointer-constant", + "-Wno-gnu-zero-variadic-macro-arguments", +] + +ABSL_MSVC_EXCEPTIONS_FLAGS = [ + "/U_HAS_EXCEPTIONS", + "/D_HAS_EXCEPTIONS=1", + "/EHsc", +] + +ABSL_MSVC_FLAGS = [ + "/W3", + "/DNOMINMAX", + "/DWIN32_LEAN_AND_MEAN", + "/D_CRT_SECURE_NO_WARNINGS", + "/D_SCL_SECURE_NO_WARNINGS", + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", + "/wd4005", + "/wd4068", + "/wd4180", + "/wd4244", + "/wd4267", + "/wd4503", + "/wd4800", +] + +ABSL_MSVC_LINKOPTS = [ + "-ignore:4221", +] + +ABSL_MSVC_TEST_FLAGS = [ + "/wd4018", + "/wd4101", + "/wd4503", + "/wd4996", + "/DNOMINMAX", +] + +ABSL_RANDOM_HWAES_ARM32_FLAGS = [ + "-mfpu=neon", +] + +ABSL_RANDOM_HWAES_ARM64_FLAGS = [ + "-march=armv8-a+crypto", +] + +ABSL_RANDOM_HWAES_MSVC_X64_FLAGS = [ + "/O2", + "/Ob2", +] + +ABSL_RANDOM_HWAES_X64_FLAGS = [ + "-maes", + "-msse4.1", +] diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl new file mode 100644 index 00000000..8c4efe77 --- /dev/null +++ b/absl/copts/configure_copts.bzl @@ -0,0 +1,89 @@ +"""absl specific copts. + +This file simply selects the correct options from the generated files. To +change Abseil copts, edit absl/copts/copts.py +""" + +load( + "//absl:copts/GENERATED_copts.bzl", + "ABSL_GCC_EXCEPTIONS_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", + "ABSL_RANDOM_HWAES_ARM32_FLAGS", + "ABSL_RANDOM_HWAES_ARM64_FLAGS", + "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS", + "ABSL_RANDOM_HWAES_X64_FLAGS", +) + +ABSL_DEFAULT_COPTS = select({ + "//absl:windows": ABSL_MSVC_FLAGS, + "//absl:llvm_compiler": ABSL_LLVM_FLAGS, + "//conditions:default": ABSL_GCC_FLAGS, +}) + +# in absence of modules (--compiler=gcc or -c opt), cc_tests leak their copts +# to their (included header) dependencies and fail to build outside absl +ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({ + "//absl:windows": ABSL_MSVC_TEST_FLAGS, + "//absl:llvm_compiler": ABSL_LLVM_TEST_FLAGS, + "//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": [], +}) + +# ABSL_RANDOM_RANDEN_COPTS blaze copts flags which are required by each +# environment to build an accelerated RandenHwAes library. +ABSL_RANDOM_RANDEN_COPTS = select({ + # APPLE + ":cpu_darwin_x86_64": ABSL_RANDOM_HWAES_X64_FLAGS, + ":cpu_darwin": ABSL_RANDOM_HWAES_X64_FLAGS, + ":cpu_x64_windows_msvc": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, + ":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, + ":cpu_haswell": ABSL_RANDOM_HWAES_X64_FLAGS, + ":cpu_ppc": ["-mcrypto"], + + # Supported by default or unsupported. + "//conditions:default": [], +}) + +# absl_random_randen_copts_init: +# Initialize the config targets based on cpu, os, etc. used to select +# the required values for ABSL_RANDOM_RANDEN_COPTS +def absl_random_randen_copts_init(): + """Initialize the config_settings used by ABSL_RANDOM_RANDEN_COPTS.""" + + # CPU configs. + # These configs have consistent flags to enable HWAES intsructions. + cpu_configs = [ + "ppc", + "haswell", + "darwin_x86_64", + "darwin", + "x64_windows_msvc", + "x64_windows", + ] + for cpu in cpu_configs: + native.config_setting( + name = "cpu_%s" % cpu, + values = {"cpu": cpu}, + ) diff --git a/absl/copts/copts.py b/absl/copts/copts.py new file mode 100644 index 00000000..068abceb --- /dev/null +++ b/absl/copts/copts.py @@ -0,0 +1,214 @@ +"""Abseil compiler options. + +This is the source of truth for Abseil compiler options. To modify Abseil +compilation options: + + (1) Edit the appropriate list in this file based on the platform the flag is + needed on. + (2) Run `/copts/generate_copts.py`. + +The generated copts are consumed by configure_copts.bzl and +AbseilConfigureCopts.cmake. +""" + +# /Wall with msvc includes unhelpful warnings such as C4711, C4710, ... +MSVC_BIG_WARNING_FLAGS = [ + "/W3", +] + +LLVM_BIG_WARNING_FLAGS = [ + "-Wall", + "-Wextra", + "-Weverything", +] + +# Docs on single flags is preceded by a comment. +# Docs on groups of flags is preceded by ###. +LLVM_DISABLE_WARNINGS_FLAGS = [ + # Abseil does not support C++98 + "-Wno-c++98-compat-pedantic", + # Turns off all implicit conversion warnings. Most are re-enabled below. + "-Wno-conversion", + "-Wno-covered-switch-default", + "-Wno-deprecated", + "-Wno-disabled-macro-expansion", + "-Wno-double-promotion", + ### + # Turned off as they include valid C++ code. + "-Wno-comma", + "-Wno-extra-semi", + "-Wno-extra-semi-stmt", + "-Wno-packed", + "-Wno-padded", + ### + # Google style does not use unsigned integers, though STL containers + # have unsigned types. + "-Wno-sign-compare", + ### + "-Wno-float-conversion", + "-Wno-float-equal", + "-Wno-format-nonliteral", + # Too aggressive: warns on Clang extensions enclosed in Clang-only + # compilation paths. + "-Wno-gcc-compat", + ### + # Some internal globals are necessary. Don't do this at home. + "-Wno-global-constructors", + "-Wno-exit-time-destructors", + ### + "-Wno-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 + "-Wno-range-loop-analysis", + "-Wno-reserved-id-macro", + "-Wno-shorten-64-to-32", + "-Wno-switch-enum", + "-Wno-thread-safety-negative", + "-Wno-unknown-warning-option", + "-Wno-unreachable-code", + # Causes warnings on include guards + "-Wno-unused-macros", + "-Wno-weak-vtables", + # Causes warnings on usage of types/compare.h comparison operators. + "-Wno-zero-as-null-pointer-constant", + ### + # Implicit conversion warnings turned off by -Wno-conversion + # which are re-enabled below. + "-Wbitfield-enum-conversion", + "-Wbool-conversion", + "-Wconstant-conversion", + "-Wenum-conversion", + "-Wint-conversion", + "-Wliteral-conversion", + "-Wnon-literal-null-conversion", + "-Wnull-conversion", + "-Wobjc-literal-conversion", + "-Wno-sign-conversion", + "-Wstring-conversion", +] + +LLVM_TEST_DISABLE_WARNINGS_FLAGS = [ + "-Wno-c99-extensions", + "-Wno-deprecated-declarations", + "-Wno-missing-noreturn", + "-Wno-missing-prototypes", + "-Wno-missing-variable-declarations", + "-Wno-null-conversion", + "-Wno-shadow", + "-Wno-shift-sign-overflow", + "-Wno-sign-compare", + "-Wno-unused-function", + "-Wno-unused-member-function", + "-Wno-unused-parameter", + "-Wno-unused-private-field", + "-Wno-unused-template", + "-Wno-used-but-marked-unused", + "-Wno-zero-as-null-pointer-constant", + # gtest depends on this GNU extension being offered. + "-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. + "/DWIN32_LEAN_AND_MEAN", + # Don't warn about usage of insecure C functions. + "/D_CRT_SECURE_NO_WARNINGS", + "/D_SCL_SECURE_NO_WARNINGS", + # Introduced in VS 2017 15.8, allow overaligned types in aligned_storage + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", +] + +COPT_VARS = { + "ABSL_GCC_FLAGS": [ + "-Wall", + "-Wextra", + "-Wcast-qual", + "-Wconversion-null", + "-Wmissing-declarations", + "-Woverlength-strings", + "-Wpointer-arith", + "-Wunused-local-typedefs", + "-Wunused-result", + "-Wvarargs", + "-Wvla", # variable-length array + "-Wwrite-strings", + # gcc-4.x has spurious missing field initializer warnings. + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750 + # Remove when gcc-4.x is no longer supported. + "-Wno-missing-field-initializers", + # Google style does not use unsigned integers, though STL containers + # have unsigned types. + "-Wno-sign-compare", + ], + "ABSL_GCC_TEST_FLAGS": [ + "-Wno-conversion-null", + "-Wno-deprecated-declarations", + "-Wno-missing-declarations", + "-Wno-sign-compare", + "-Wno-unused-function", + "-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 + [ + "/wd4005", # macro-redefinition + "/wd4068", # unknown pragma + # qualifier applied to function type has no meaning; ignored + "/wd4180", + # conversion from 'type1' to 'type2', possible loss of data + "/wd4244", + # conversion from 'size_t' to 'type', possible loss of data + "/wd4267", + # The decorated name was longer than the compiler limit + "/wd4503", + # forcing value to bool 'true' or 'false' (performance warning) + "/wd4800", + ], + "ABSL_MSVC_TEST_FLAGS": [ + "/wd4018", # signed/unsigned mismatch + "/wd4101", # unreferenced local variable + "/wd4503", # decorated name length exceeded, name was truncated + "/wd4996", # use of deprecated symbol + "/DNOMINMAX", # disable the min() and max() macros from + ], + "ABSL_MSVC_EXCEPTIONS_FLAGS": + MSVC_STYLE_EXCEPTIONS_FLAGS, + "ABSL_MSVC_LINKOPTS": [ + # Object file doesn't export any previously undefined symbols + "-ignore:4221", + ], + # "HWAES" is an abbreviation for "hardware AES" (AES - Advanced Encryption + # Standard). These flags are used for detecting whether or not the target + # architecture has hardware support for AES instructions which can be used + # to improve performance of some random bit generators. + "ABSL_RANDOM_HWAES_ARM64_FLAGS": ["-march=armv8-a+crypto"], + "ABSL_RANDOM_HWAES_ARM32_FLAGS": ["-mfpu=neon"], + "ABSL_RANDOM_HWAES_X64_FLAGS": [ + "-maes", + "-msse4.1", + ], + "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS": [ + "/O2", # Maximize speed + "/Ob2", # Aggressive inlining + ], +} diff --git a/absl/copts/generate_copts.py b/absl/copts/generate_copts.py new file mode 100755 index 00000000..0e5dc9fa --- /dev/null +++ b/absl/copts/generate_copts.py @@ -0,0 +1,109 @@ +#!/usr/bin/python +"""Generate Abseil compile compile option configs. + +Usage: /copts/generate_copts.py + +The configs are generated from copts.py. +""" + +from os import path +import sys +from copts import COPT_VARS + + +# Helper functions +def file_header_lines(): + return [ + "GENERATED! DO NOT MANUALLY EDIT THIS FILE.", "", + "(1) Edit absl/copts/copts.py.", + "(2) Run `python /copts/generate_copts.py`." + ] + + +def flatten(*lists): + return [item for sublist in lists for item in sublist] + + +def relative_filename(filename): + return path.join(path.dirname(__file__), filename) + + +# Style classes. These contain all the syntactic styling needed to generate a +# copt file for different build tools. +class CMakeStyle(object): + """Style object for CMake copts file.""" + + def separator(self): + return "" + + def list_introducer(self, name): + return "list(APPEND " + name + + def list_closer(self): + return ")\n" + + def docstring(self): + return "\n".join((("# " + line).strip() for line in file_header_lines())) + + def filename(self): + return "GENERATED_AbseilCopts.cmake" + + +class StarlarkStyle(object): + """Style object for Starlark copts file.""" + + def separator(self): + return "," + + def list_introducer(self, name): + return name + " = [" + + def list_closer(self): + return "]\n" + + def docstring(self): + docstring_quotes = "\"\"\"" + return docstring_quotes + "\n".join( + flatten(file_header_lines(), [docstring_quotes])) + + def filename(self): + return "GENERATED_copts.bzl" + + +def copt_list(name, arg_list, style): + """Copt file generation.""" + + make_line = lambda s: " \"" + s + "\"" + style.separator() + external_str_list = [make_line(s) for s in arg_list] + + return "\n".join( + flatten( + [style.list_introducer(name)], + external_str_list, + [style.list_closer()])) + + +def generate_copt_file(style): + """Creates a generated copt file using the given style object. + + Args: + style: either StarlarkStyle() or CMakeStyle() + """ + with open(relative_filename(style.filename()), "w") as f: + f.write(style.docstring()) + f.write("\n") + for var_name, arg_list in sorted(COPT_VARS.items()): + f.write("\n") + f.write(copt_list(var_name, arg_list, style)) + + +def main(argv): + if len(argv) > 1: + raise RuntimeError("generate_copts needs no command line args") + + generate_copt_file(StarlarkStyle()) + generate_copt_file(CMakeStyle()) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index a8ebaea4..913cfafb 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,9 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -33,6 +34,7 @@ cc_library( ], hdrs = ["stacktrace.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":debugging_internal", "//absl/base", @@ -53,11 +55,13 @@ cc_library( "symbolize.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":debugging_internal", ":demangle_internal", "//absl/base", "//absl/base:core_headers", + "//absl/base:dynamic_annotations", "//absl/base:malloc_internal", ], ) @@ -66,6 +70,7 @@ cc_test( name = "symbolize_test", srcs = ["symbolize_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":stack_consumption", ":symbolize", @@ -85,6 +90,7 @@ cc_library( "internal/examine_stack.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ ":stacktrace", @@ -99,6 +105,7 @@ cc_library( srcs = ["failure_signal_handler.cc"], hdrs = ["failure_signal_handler.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":examine_stack", ":stacktrace", @@ -115,7 +122,7 @@ cc_test( linkopts = select({ "//absl:windows": [], "//conditions:default": ["-pthread"], - }), + }) + ABSL_DEFAULT_LINKOPTS, deps = [ ":failure_signal_handler", ":stacktrace", @@ -147,6 +154,7 @@ cc_library( "internal/vdso_support.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base", "//absl/base:core_headers", @@ -169,6 +177,7 @@ cc_test( name = "demangle_test", srcs = ["internal/demangle_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":demangle_internal", ":stack_consumption", @@ -181,22 +190,9 @@ cc_test( cc_library( name = "leak_check", - srcs = select({ - # The leak checking interface depends on weak function - # declarations that may not necessarily have definitions. - # Windows doesn't support this, and ios requires - # guaranteed definitions for weak symbols. - "//absl:ios": [], - "//absl:windows": [], - "//conditions:default": [ - "leak_check.cc", - ], - }), - hdrs = select({ - "//absl:ios": [], - "//absl:windows": [], - "//conditions:default": ["leak_check.h"], - }), + srcs = ["leak_check.cc"], + hdrs = ["leak_check.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = ["//absl/base:core_headers"], ) @@ -206,6 +202,7 @@ cc_library( cc_library( name = "leak_check_disable", srcs = ["leak_check_disable.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, linkstatic = 1, alwayslink = 1, ) @@ -226,6 +223,7 @@ cc_library( "//absl:llvm_compiler": ["-DLEAK_SANITIZER"], "//conditions:default": [], }), + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], ) @@ -235,6 +233,7 @@ cc_library( srcs = ["leak_check.cc"], hdrs = ["leak_check.h"], copts = ["-ULEAK_SANITIZER"], + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], ) @@ -245,7 +244,8 @@ cc_test( "//absl:llvm_compiler": ["-DABSL_EXPECT_LEAK_SANITIZER"], "//conditions:default": [], }), - linkopts = ABSL_LSAN_LINKOPTS, + linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + tags = ["notsan"], deps = [ ":leak_check_api_enabled_for_testing", "//absl/base", @@ -257,6 +257,8 @@ cc_test( name = "leak_check_no_lsan_test", srcs = ["leak_check_test.cc"], copts = ["-UABSL_EXPECT_LEAK_SANITIZER"], + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["noasan"], deps = [ ":leak_check_api_disabled_for_testing", "//absl/base", # for raw_logging @@ -271,7 +273,8 @@ cc_test( cc_test( name = "disabled_leak_check_test", srcs = ["leak_check_fail_test.cc"], - linkopts = ABSL_LSAN_LINKOPTS, + linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + tags = ["notsan"], deps = [ ":leak_check_api_enabled_for_testing", ":leak_check_disable", @@ -286,6 +289,7 @@ cc_library( srcs = ["internal/stack_consumption.cc"], hdrs = ["internal/stack_consumption.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ "//absl/base", @@ -297,6 +301,7 @@ cc_test( name = "stack_consumption_test", srcs = ["internal/stack_consumption_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":stack_consumption", "//absl/base", diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index f66688ba..001e2727 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -43,11 +43,14 @@ absl_cc_library( "symbolize_win32.inc" COPTS ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} DEPS absl::debugging_internal absl::demangle_internal absl::base absl::core_headers + absl::dynamic_annotations absl::malloc_internal PUBLIC ) @@ -181,9 +184,9 @@ absl_cc_library( NAME leak_check HDRS - "$<$>:leak_check.h>" + "leak_check.h" SRCS - "$<$>:leak_check.cc>" + "leak_check.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -196,14 +199,11 @@ absl_cc_library( leak_check_disable SRCS "leak_check_disable.cc" + COPTS + ${ABSL_DEFAULT_COPTS} PUBLIC ) -# TODO(cohenjon) Move into the copts code. -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(ABSL_LSAN_LINKOPTS "-fsanitize=leak") -endif() - absl_cc_library( NAME leak_check_api_enabled_for_testing @@ -212,7 +212,8 @@ absl_cc_library( SRCS "leak_check.cc" COPTS - $<$:-DLEAK_SANITIZER> + ${ABSL_DEFAULT_COPTS} + $<$:-DLEAK_SANITIZER> TESTONLY ) @@ -224,6 +225,7 @@ absl_cc_library( SRCS "leak_check.cc" COPTS + ${ABSL_DEFAULT_COPTS} "-ULEAK_SANITIZER" TESTONLY ) @@ -234,7 +236,8 @@ absl_cc_test( SRCS "leak_check_test.cc" COPTS - "$<$:-DABSL_EXPECT_LEAK_SANITIZER>" + ${ABSL_DEFAULT_COPTS} + "$<$:-DABSL_EXPECT_LEAK_SANITIZER>" LINKOPTS "${ABSL_LSAN_LINKOPTS}" DEPS @@ -249,6 +252,7 @@ absl_cc_test( SRCS "leak_check_test.cc" COPTS + ${ABSL_TEST_COPTS} "-UABSL_EXPECT_LEAK_SANITIZER" DEPS absl::leak_check_api_disabled_for_testing @@ -261,6 +265,8 @@ absl_cc_test( disabled_leak_check_test SRCS "leak_check_fail_test.cc" + COPTS + ${ABSL_TEST_COPTS} LINKOPTS "${ABSL_LSAN_LINKOPTS}" DEPS @@ -303,6 +309,8 @@ absl_cc_test( absl_cc_library( NAME debugging + COPTS + ${ABSL_DEFAULT_COPTS} DEPS absl::stacktrace absl::leak_check diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index a3a36f64..af651c72 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -47,7 +47,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options; @@ -357,5 +357,5 @@ void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) { } } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h index 0aeb287f..87b202b0 100644 --- a/absl/debugging/failure_signal_handler.h +++ b/absl/debugging/failure_signal_handler.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -45,7 +45,7 @@ #define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // FailureSignalHandlerOptions // @@ -113,7 +113,7 @@ namespace debugging_internal { const char* FailureSignalToString(int signo); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 ba520910..bb2cc48e 100644 --- a/absl/debugging/failure_signal_handler_test.cc +++ b/absl/debugging/failure_signal_handler_test.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -133,16 +133,17 @@ constexpr int kFailureSignals[] = { }; std::string SignalParamToString(const ::testing::TestParamInfo& info) { - std::string result = absl::debugging_internal::FailureSignalToString(info.param); + std::string result = + absl::debugging_internal::FailureSignalToString(info.param); if (result.empty()) { result = absl::StrCat(info.param); } return result; } -INSTANTIATE_TEST_CASE_P(AbslDeathTest, FailureSignalHandlerDeathTest, - ::testing::ValuesIn(kFailureSignals), - SignalParamToString); +INSTANTIATE_TEST_SUITE_P(AbslDeathTest, FailureSignalHandlerDeathTest, + ::testing::ValuesIn(kFailureSignals), + SignalParamToString); #endif // GTEST_HAS_DEATH_TEST diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc index 2a83f4c8..64dd285b 100644 --- a/absl/debugging/internal/address_is_readable.cc +++ b/absl/debugging/internal/address_is_readable.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,14 +20,14 @@ #if !defined(__linux__) || defined(__ANDROID__) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // On platforms other than Linux, just return true. bool AddressIsReadable(const void* /* addr */) { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else @@ -42,7 +42,7 @@ bool AddressIsReadable(const void* /* addr */) { return true; } #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Pack a pid and two file descriptors into a 64-bit word, @@ -131,7 +131,7 @@ bool AddressIsReadable(const void *addr) { } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif diff --git a/absl/debugging/internal/address_is_readable.h b/absl/debugging/internal/address_is_readable.h index 3942f44b..dc626e5b 100644 --- a/absl/debugging/internal/address_is_readable.h +++ b/absl/debugging/internal/address_is_readable.h @@ -4,20 +4,19 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// #ifndef ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ #define ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Return whether the byte at *addr is readable, without faulting. @@ -25,7 +24,7 @@ namespace debugging_internal { bool AddressIsReadable(const void *addr); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 57b9393f..976e47a6 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { typedef struct { @@ -750,8 +750,8 @@ static bool ParseSourceName(State *state) { // ::= L [] // // References: -// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 -// http://gcc.gnu.org/viewcvs?view=rev&revision=124467 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 +// https://gcc.gnu.org/viewcvs?view=rev&revision=124467 static bool ParseLocalSourceName(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; @@ -1169,6 +1169,12 @@ 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; @@ -1869,5 +1875,5 @@ bool Demangle(const char *mangled, char *out, int out_size) { } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h index 1f8722c7..20adbe9c 100644 --- a/absl/debugging/internal/demangle.h +++ b/absl/debugging/internal/demangle.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -54,7 +54,7 @@ #define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Demangle `mangled`. On success, return true and write the @@ -63,7 +63,7 @@ namespace debugging_internal { bool Demangle(const char *mangled, char *out, int out_size); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 fa89fb80..7c50fe3a 100644 --- a/absl/debugging/internal/demangle_test.cc +++ b/absl/debugging/internal/demangle_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { namespace { @@ -82,8 +82,9 @@ TEST(Demangle, Clones) { // Tests that verify that Demangle footprint is within some limit. // They are not to be run under sanitizers as the sanitizers increase // stack consumption by about 4x. -#if defined(ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION) && \ - !ADDRESS_SANITIZER && !MEMORY_SANITIZER && !THREAD_SANITIZER +#if defined(ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION) && \ + !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ + !defined(THREAD_SANITIZER) static const char *g_mangled; static char g_demangle_buffer[4096]; @@ -177,6 +178,7 @@ static void TestOnInput(const char* input) { TEST(DemangleRegression, NegativeLength) { TestOnInput("_ZZn4"); } + TEST(DemangleRegression, DeeplyNestedArrayType) { const int depth = 100000; std::string data = "_ZStI"; @@ -189,5 +191,5 @@ TEST(DemangleRegression, DeeplyNestedArrayType) { } // namespace } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc index e7e35e9c..a795117a 100644 --- a/absl/debugging/internal/elf_mem_image.cc +++ b/absl/debugging/internal/elf_mem_image.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,7 +38,7 @@ #define VERSYM_VERSION 0x7fff namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { namespace { @@ -376,7 +376,7 @@ void ElfMemImage::SymbolIterator::Update(int increment) { } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 0adb5f5d..fcd32afe 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -34,12 +34,12 @@ #define ABSL_HAVE_ELF_MEM_IMAGE 1 #endif -#if ABSL_HAVE_ELF_MEM_IMAGE +#ifdef ABSL_HAVE_ELF_MEM_IMAGE #include // for ElfW namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // An in-memory ELF image (may not exist on disk). @@ -124,7 +124,7 @@ class ElfMemImage { }; } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 4764355a..4739fbc5 100644 --- a/absl/debugging/internal/examine_stack.cc +++ b/absl/debugging/internal/examine_stack.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,7 +30,7 @@ #include "absl/debugging/symbolize.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Returns the program counter from signal context, nullptr if @@ -151,5 +151,5 @@ void DumpPCAndFrameSizesAndStackTrace( } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h index 474fdd5e..861db75d 100644 --- a/absl/debugging/internal/examine_stack.h +++ b/absl/debugging/internal/examine_stack.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Returns the program counter from signal context, or nullptr if @@ -34,7 +34,7 @@ void DumpPCAndFrameSizesAndStackTrace( void (*writerfn)(const char*, void*), void* writerfn_arg); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 94b43e1d..8dfd94aa 100644 --- a/absl/debugging/internal/stack_consumption.cc +++ b/absl/debugging/internal/stack_consumption.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { namespace { @@ -168,7 +168,7 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 2defdf0d..1eb37eef 100644 --- a/absl/debugging/internal/stack_consumption.h +++ b/absl/debugging/internal/stack_consumption.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,7 @@ #define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1 namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Returns the stack consumption in bytes for the code exercised by @@ -39,7 +39,7 @@ namespace debugging_internal { int GetSignalHandlerStackConsumption(void (*signal_handler)(int)); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 022e508a..1c82d18a 100644 --- a/absl/debugging/internal/stack_consumption_test.cc +++ b/absl/debugging/internal/stack_consumption_test.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { namespace { @@ -44,7 +44,7 @@ TEST(SignalHandlerStackConsumptionTest, MeasuresStackConsumption) { } // namespace } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 2ed7ae1f..14b6e6b0 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_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 eb8ca77c..4f51d180 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_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h index 578e4968..d4e8480a 100644 --- a/absl/debugging/internal/stacktrace_config.h +++ b/absl/debugging/internal/stacktrace_config.h @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/internal/stacktrace_generic-inl.inc b/absl/debugging/internal/stacktrace_generic-inl.inc index 823942af..39c47866 100644 --- a/absl/debugging/internal/stacktrace_generic-inl.inc +++ b/absl/debugging/internal/stacktrace_generic-inl.inc @@ -12,13 +12,47 @@ #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ #include +#include #include #include "absl/debugging/stacktrace.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 +// expensive: the two extra system calls per stack trace do matter here). +// That can cause a self-deadlock. +// Protect against such reentrant call by failing to get a stack trace. +// +// We use __thread here because the code here is extremely low level -- it is +// called while collecting stack traces from within malloc and mmap, and thus +// can not call anything which might call malloc or mmap itself. +static __thread int recursive = 0; + +// The stack trace function might be invoked very early in the program's +// execution (e.g. from the very first malloc if using tcmalloc). Also, the +// glibc implementation itself will trigger malloc the first time it is called. +// As such, we suppress usage of backtrace during this early stage of execution. +static std::atomic 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 = []() { + 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 + // to use backtrace from a signal handler if we crash somewhere later. + backtrace(unused_stack, 1); + disable_stacktraces.store(false, std::memory_order_relaxed); + return 0; +}(); + template static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, const void *ucp, int *min_dropped_frames) { + if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) { + return 0; + } + ++recursive; + static_cast(ucp); // Unused. static const int kStackLength = 64; void * stack[kStackLength]; @@ -46,17 +80,19 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } } + --recursive; + return result_count; } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 aff2d516..ee6b38ff 100644 --- a/absl/debugging/internal/stacktrace_powerpc-inl.inc +++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -14,8 +14,8 @@ // // Produce stack trace. I'm guessing (hoping!) the code is much like // for x86. For apple machines, at least, it seems to be; see -// http://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html -// http://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK +// https://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html +// https://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK // Linux has similar code: http://patchwork.ozlabs.org/linuxppc/patch?id=8882 #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ @@ -162,7 +162,7 @@ ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, const void *ucp, int *min_dropped_frames) { void **sp; - // Apple OS X uses an old version of gnu as -- both Darwin 7.9.0 (Panther) + // Apple macOS uses an old version of gnu as -- both Darwin 7.9.0 (Panther) // and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a // different asm syntax. I don't know quite the best way to discriminate // systems using the old as from the new one; I've gone with __APPLE__. @@ -236,13 +236,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 65345efc..b49a929a 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_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 59e72785..0cd8c339 100644 --- a/absl/debugging/internal/stacktrace_win32-inl.inc +++ b/absl/debugging/internal/stacktrace_win32-inl.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -32,7 +32,7 @@ // server. // // This code is inspired by a patch from David Vitek: -// http://code.google.com/p/google-perftools/issues/detail?id=83 +// https://code.google.com/p/google-perftools/issues/detail?id=83 #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ @@ -73,13 +73,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 d29cd84b..ff0fd31f 100644 --- a/absl/debugging/internal/stacktrace_x86-inl.inc +++ b/absl/debugging/internal/stacktrace_x86-inl.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,6 +33,7 @@ #include "absl/debugging/internal/address_is_readable.h" #include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems #include "absl/debugging/stacktrace.h" + #include "absl/base/internal/raw_logging.h" #if defined(__linux__) && defined(__i386__) @@ -327,13 +328,13 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 2791105e..151bc77c 100644 --- a/absl/debugging/internal/symbolize.h +++ b/absl/debugging/internal/symbolize.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,6 @@ #include #include -#include "absl/base/port.h" // Needed for string vs std::string #ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set @@ -34,7 +33,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Iterates over all sections, invoking callback on each with the section name @@ -43,9 +42,9 @@ namespace debugging_internal { // Returns true on success; otherwise returns false in case of errors. // // This is not async-signal-safe. -bool ForEachSection( - int fd, const std::function& - callback); +bool ForEachSection(int fd, + const std::function& callback); // Gets the section header for the given name, if it exists. Returns true on // success. Otherwise, returns false. @@ -53,13 +52,13 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, ElfW(Shdr) *out); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { struct SymbolDecoratorArgs { @@ -121,7 +120,7 @@ bool GetFileMappingHint(const void** start, const char** filename); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 85b52bc8..fa88e1d8 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,7 +38,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { ABSL_CONST_INIT @@ -188,7 +188,7 @@ static class VDSOInitHelper { } vdso_init_helper; } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_VDSO_SUPPORT diff --git a/absl/debugging/internal/vdso_support.h b/absl/debugging/internal/vdso_support.h index 035e5964..bc5fdb11 100644 --- a/absl/debugging/internal/vdso_support.h +++ b/absl/debugging/internal/vdso_support.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -53,7 +53,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_ELF_MEM_IMAGE diff --git a/absl/debugging/leak_check.cc b/absl/debugging/leak_check.cc index cf65280a..63e54ffa 100644 --- a/absl/debugging/leak_check.cc +++ b/absl/debugging/leak_check.cc @@ -4,13 +4,14 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + // Wrappers around lsan_interface functions. // When lsan is not linked in, these functions are not available, // therefore Abseil code which depends on these functions is conditioned on the @@ -20,14 +21,14 @@ #ifndef LEAK_SANITIZER namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else @@ -35,7 +36,7 @@ LeakCheckDisabler::~LeakCheckDisabler() { } #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { bool HaveLeakSanitizer() { return true; } void DoIgnoreLeak(const void* ptr) { __lsan_ignore_object(ptr); } void RegisterLivePointers(const void* ptr, size_t size) { @@ -46,7 +47,7 @@ void UnRegisterLivePointers(const void* ptr, size_t size) { } LeakCheckDisabler::LeakCheckDisabler() { __lsan_disable(); } LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // LEAK_SANITIZER diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h index f5e4fd87..e1215e38 100644 --- a/absl/debugging/leak_check.h +++ b/absl/debugging/leak_check.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,7 +33,7 @@ #include namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // HaveLeakSanitizer() // @@ -105,7 +105,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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_LEAK_CHECK_H_ diff --git a/absl/debugging/leak_check_disable.cc b/absl/debugging/leak_check_disable.cc index df22c1ca..924d6e3d 100644 --- a/absl/debugging/leak_check_disable.cc +++ b/absl/debugging/leak_check_disable.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/leak_check_fail_test.cc b/absl/debugging/leak_check_fail_test.cc index bf541fe8..2887ceab 100644 --- a/absl/debugging/leak_check_fail_test.cc +++ b/absl/debugging/leak_check_fail_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/leak_check_test.cc b/absl/debugging/leak_check_test.cc index febd1ee4..93a7edd2 100644 --- a/absl/debugging/leak_check_test.cc +++ b/absl/debugging/leak_check_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc index e991fc59..3052fb97 100644 --- a/absl/debugging/stacktrace.cc +++ b/absl/debugging/stacktrace.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -46,6 +46,7 @@ #include ABSL_STACKTRACE_INL_HEADER #else # error Cannot calculate stack trace: will need to write for your environment + # include "absl/debugging/internal/stacktrace_aarch64-inl.inc" # include "absl/debugging/internal/stacktrace_arm-inl.inc" # include "absl/debugging/internal/stacktrace_generic-inl.inc" @@ -56,7 +57,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { typedef int (*Unwinder)(void**, int*, int, int, const void*, int*); @@ -135,5 +136,5 @@ int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip, return n; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/stacktrace.h b/absl/debugging/stacktrace.h index 7baf83bc..d7565aa4 100644 --- a/absl/debugging/stacktrace.h +++ b/absl/debugging/stacktrace.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -32,7 +32,7 @@ #define ABSL_DEBUGGING_STACKTRACE_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // GetStackFrames() // @@ -221,7 +221,7 @@ namespace debugging_internal { // working. extern bool StackTraceWorksForTest(); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_STACKTRACE_H_ diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc index a35e24cc..24e3a7f0 100644 --- a/absl/debugging/symbolize.cc +++ b/absl/debugging/symbolize.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/symbolize.h b/absl/debugging/symbolize.h index fc606e87..ab5447c4 100644 --- a/absl/debugging/symbolize.h +++ b/absl/debugging/symbolize.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -55,7 +55,7 @@ #include "absl/debugging/internal/symbolize.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // InitializeSymbolizer() // @@ -93,7 +93,7 @@ void InitializeSymbolizer(const char* argv0); // } bool Symbolize(const void *pc, char *out, int out_size); -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_SYMBOLIZE_H_ diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index 37f77ca6..2bd7659f 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -76,7 +76,7 @@ #include "absl/debugging/internal/vdso_support.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Value of argv[0]. Used by MaybeInitializeObjFile(). static char *argv0_value = nullptr; @@ -306,7 +306,7 @@ class Symbolizer { char *tmp_buf, int tmp_buf_size); enum { - SYMBOL_BUF_SIZE = 2048, + SYMBOL_BUF_SIZE = 3072, TMP_BUF_SIZE = 1024, SYMBOL_CACHE_LINES = 128, }; @@ -763,37 +763,27 @@ FindSymbolResult Symbolizer::GetSymbolFromObjectFile( } } - // Consult a regular symbol table first. - if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum, - obj.elf_header.e_shoff, SHT_SYMTAB, &symtab, - tmp_buf, tmp_buf_size)) { - return SYMBOL_NOT_FOUND; - } - if (!ReadFromOffsetExact( - obj.fd, &strtab, sizeof(strtab), - obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { - return SYMBOL_NOT_FOUND; - } - const FindSymbolResult rc = - FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab, - opd_ptr, tmp_buf, tmp_buf_size); - if (rc != SYMBOL_NOT_FOUND) { - return rc; // Found the symbol in a regular symbol table. + // Consult a regular symbol table, then fall back to the dynamic symbol table. + for (const auto symbol_table_type : {SHT_SYMTAB, SHT_DYNSYM}) { + if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum, + obj.elf_header.e_shoff, symbol_table_type, + &symtab, tmp_buf, tmp_buf_size)) { + continue; + } + if (!ReadFromOffsetExact( + obj.fd, &strtab, sizeof(strtab), + obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { + continue; + } + const FindSymbolResult rc = + FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab, + opd_ptr, tmp_buf, tmp_buf_size); + if (rc != SYMBOL_NOT_FOUND) { + return rc; + } } - // If the symbol is not found, then consult a dynamic symbol table. - if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum, - obj.elf_header.e_shoff, SHT_DYNSYM, &symtab, - tmp_buf, tmp_buf_size)) { - return SYMBOL_NOT_FOUND; - } - if (!ReadFromOffsetExact( - obj.fd, &strtab, sizeof(strtab), - obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { - return SYMBOL_NOT_FOUND; - } - return FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab, - opd_ptr, tmp_buf, tmp_buf_size); + return SYMBOL_NOT_FOUND; } namespace { @@ -926,6 +916,14 @@ static const char *GetHex(const char *start, const char *end, return p; } +// Normally we are only interested in "r?x" maps. +// On the PowerPC, function pointers point to descriptors in the .opd +// section. The descriptors themselves are not executable code, so +// we need to relax the check below to "r??". +static bool ShouldUseMapping(const char *const flags) { + return flags[0] == 'r' && (kPlatformUsesOPDSections || flags[2] == 'x'); +} + // Read /proc/self/maps and run "callback" for each mmapped file found. If // "callback" returns false, stop scanning and return true. Else continue // scanning /proc/self/maps. Return true if no parse error is found. @@ -995,12 +993,8 @@ static ABSL_ATTRIBUTE_NOINLINE bool ReadAddrMap( return false; } - // Check flags. Normally we are only interested in "r-x" maps. On - // the PowerPC, function pointers point to descriptors in the .opd - // section. The descriptors themselves are not executable code. So - // we need to relax the check below to "r**". - if (memcmp(flags_start, "r-x", 3) != 0 && // Not a "r-x" map. - !(kPlatformUsesOPDSections && flags_start[0] == 'r')) { + // Check flags. + if (!ShouldUseMapping(flags_start)) { continue; // We skip this map. } ++cursor; // Skip ' '. @@ -1475,5 +1469,5 @@ bool Symbolize(const void *pc, char *out, int out_size) { return ok; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index 8029fbe9..08068c30 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -107,7 +107,8 @@ static const char *TrySymbolizeWithLimit(void *pc, int limit) { ABSL_RAW_CHECK(strnlen(heap_buffer.get(), limit) < limit, "absl::Symbolize() did not properly terminate the string"); strncpy(try_symbolize_buffer, heap_buffer.get(), - sizeof(try_symbolize_buffer)); + sizeof(try_symbolize_buffer) - 1); + try_symbolize_buffer[sizeof(try_symbolize_buffer) - 1] = '\0'; } return found ? try_symbolize_buffer : nullptr; @@ -392,16 +393,20 @@ TEST(Symbolize, ForEachSection) { extern "C" { inline void *ABSL_ATTRIBUTE_ALWAYS_INLINE inline_func() { void *pc = nullptr; -#if defined(__i386__) || defined(__x86_64__) - __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc)); +#if defined(__i386__) + __asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc)); +#elif defined(__x86_64__) + __asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc)); #endif return pc; } void *ABSL_ATTRIBUTE_NOINLINE non_inline_func() { void *pc = nullptr; -#if defined(__i386__) || defined(__x86_64__) - __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc)); +#if defined(__i386__) + __asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc)); +#elif defined(__x86_64__) + __asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc)); #endif return pc; } diff --git a/absl/debugging/symbolize_unimplemented.inc b/absl/debugging/symbolize_unimplemented.inc index 874a4228..0c1c1951 100644 --- a/absl/debugging/symbolize_unimplemented.inc +++ b/absl/debugging/symbolize_unimplemented.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { @@ -27,11 +27,14 @@ bool RemoveAllSymbolDecorators(void) { return false; } bool RegisterFileMappingHint(const void *, const void *, uint64_t, const char *) { return false; } +bool GetFileMappingHint(const void **, const void **, uint64_t *, const char **) { + return false; +} } // namespace debugging_internal void InitializeSymbolizer(const char*) {} bool Symbolize(const void *, char *, int) { return false; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc index ee8fd55c..498ca2db 100644 --- a/absl/debugging/symbolize_win32.inc +++ b/absl/debugging/symbolize_win32.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,8 +16,14 @@ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx #include + +// MSVC header DbgHelp.h has a warning for an ignored typedef. +#pragma warning(push) +#pragma warning(disable:4091) #include -#pragma comment(lib, "DbgHelp") +#pragma warning(pop) + +#pragma comment(lib, "dbghelp.lib") #include #include @@ -25,7 +31,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { static HANDLE process = NULL; @@ -72,5 +78,5 @@ bool Symbolize(const void *pc, char *out, int out_size) { return true; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel new file mode 100644 index 00000000..2fe61eaa --- /dev/null +++ b/absl/flags/BUILD.bazel @@ -0,0 +1,384 @@ +# +# 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( + "//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 = "internal", + srcs = [ + "internal/program_name.cc", + ], + hdrs = [ + "internal/path_util.h", + "internal/program_name.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [ + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "config", + srcs = [ + "usage_config.cc", + ], + hdrs = [ + "config.h", + "usage_config.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":internal", + "//absl/base:core_headers", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "marshalling", + srcs = [ + "marshalling.cc", + ], + hdrs = [ + "marshalling.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:core_headers", + "//absl/strings", + "//absl/strings:str_format", + ], +) + +cc_library( + name = "handle", + srcs = [ + "internal/commandlineflag.cc", + ], + hdrs = [ + "internal/commandlineflag.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [ + ":config", + ":marshalling", + "//absl/base", + "//absl/base:core_headers", + "//absl/strings", + "//absl/synchronization", + "//absl/types:optional", + ], +) + +cc_library( + name = "registry", + srcs = [ + "internal/registry.cc", + "internal/type_erased.cc", + ], + hdrs = [ + "internal/registry.h", + "internal/type_erased.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [ + ":config", + ":handle", + "//absl/base", + "//absl/base:core_headers", + "//absl/base:dynamic_annotations", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "flag", + srcs = [ + "flag.cc", + ], + hdrs = [ + "declare.h", + "flag.h", + "internal/flag.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":handle", + ":marshalling", + ":registry", + "//absl/base", + "//absl/base:core_headers", + "//absl/strings", + ], +) + +cc_library( + name = "usage_internal", + srcs = [ + "internal/usage.cc", + ], + hdrs = [ + "internal/usage.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [ + ":config", + ":flag", + ":handle", + ":internal", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "usage", + srcs = [ + "usage.cc", + ], + hdrs = [ + "usage.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":usage_internal", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "parse", + srcs = ["parse.cc"], + hdrs = [ + "internal/parse.h", + "parse.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":flag", + ":handle", + ":internal", + ":registry", + ":usage", + ":usage_internal", + "//absl/strings", + "//absl/synchronization", + ], +) + +############################################################################ +# Unit tests in alpahabetical order. + +cc_test( + name = "commandlineflag_test", + size = "small", + srcs = [ + "internal/commandlineflag_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":flag", + ":handle", + ":registry", + "//absl/memory", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "config_test", + size = "small", + srcs = [ + "config_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "flag_test", + size = "small", + srcs = [ + "flag_test.cc", + "flag_test_defs.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":flag", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "marshalling_test", + size = "small", + srcs = [ + "marshalling_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":marshalling", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "path_util_test", + size = "small", + srcs = [ + "internal/path_util_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":internal", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "parse_test", + size = "small", + srcs = [ + "parse_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":flag", + ":parse", + "//absl/base", + "//absl/base:scoped_set_env", + "//absl/strings", + "//absl/types:span", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "program_name_test", + size = "small", + srcs = [ + "internal/program_name_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":internal", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "type_erased_test", + size = "small", + srcs = [ + "internal/type_erased_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":flag", + ":registry", + "//absl/memory", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "usage_config_test", + size = "small", + srcs = [ + "usage_config_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":internal", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "usage_test", + size = "small", + srcs = [ + "internal/usage_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":flag", + ":internal", + ":parse", + ":usage", + ":usage_internal", + "//absl/memory", + "//absl/strings", + "@com_google_googletest//:gtest", + ], +) diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt new file mode 100644 index 00000000..fa1d4e17 --- /dev/null +++ b/absl/flags/CMakeLists.txt @@ -0,0 +1,346 @@ +# +# 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. +# + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_internal + SRCS + "internal/program_name.cc" + HDRS + "internal/path_util.h" + "internal/program_name.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::strings + absl::synchronization + PUBLIC +) + +absl_cc_library( + NAME + flags_config + SRCS + "usage_config.cc" + HDRS + "config.h" + "usage_config.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_internal + absl::core_headers + absl::strings + absl::synchronization +) + +absl_cc_library( + NAME + flags_marshalling + SRCS + "marshalling.cc" + HDRS + "marshalling.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::core_headers + absl::strings + absl::str_format +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_handle + SRCS + "internal/commandlineflag.cc" + HDRS + "internal/commandlineflag.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags_marshalling + absl::base + absl::core_headers + absl::strings + absl::synchronization + absl::optional +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_registry + SRCS + "internal/registry.cc" + "internal/type_erased.cc" + HDRS + "internal/registry.h" + "internal/type_erased.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags_handle + absl::base + absl::core_headers + absl::dynamic_annotations + absl::strings + absl::synchronization +) + +absl_cc_library( + NAME + flags + SRCS + "flag.cc" + HDRS + "declare.h" + "flag.h" + "internal/flag.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags_handle + absl::flags_marshalling + absl::flags_registry + absl::base + absl::core_headers + absl::strings +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_usage_internal + SRCS + "internal/usage.cc" + HDRS + "internal/usage.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags + absl::flags_handle + absl::flags_internal + absl::strings + absl::synchronization +) + +absl_cc_library( + NAME + flags_usage + SRCS + "usage.cc" + HDRS + "usage.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_usage_internal + absl::strings + absl::synchronization +) + +absl_cc_library( + NAME + flags_parse + SRCS + "parse.cc" + HDRS + "internal/parse.h" + "parse.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags + absl::flags_handle + absl::flags_internal + absl::flags_registry + absl::flags_usage + absl::strings + absl::synchronization +) + +############################################################################ +# Unit tests in alpahabetical order. + +absl_cc_test( + NAME + flags_commandlineflag_test + SRCS + "internal/commandlineflag_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags + absl::flags_handle + absl::flags_registry + absl::memory + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_config_test + SRCS + "config_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_config + gtest_main +) + +absl_cc_test( + NAME + flags_flag_test + SRCS + "flag_test.cc" + "flag_test_defs.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_marshalling_test + SRCS + "marshalling_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_marshalling + gtest_main +) + +absl_cc_test( + NAME + flags_parse_test + SRCS + "parse_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags + absl::base + absl::flags_parse + absl::scoped_set_env + absl::span + absl::strings + gmock_main +) + +absl_cc_test( + NAME + flags_path_util_test + SRCS + "internal/path_util_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_internal + gtest_main +) + +absl_cc_test( + NAME + flags_program_name_test + SRCS + "internal/program_name_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_internal + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_type_erased_test + SRCS + "internal/type_erased_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags + absl::flags_registry + absl::memory + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_usage_config_test + SRCS + "usage_config_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_config + absl::flags_internal + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_usage_test + SRCS + "internal/usage_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_config + absl::flags + absl::flags_internal + absl::flags_parse + absl::flags_usage + absl::memory + absl::strings + gtest +) diff --git a/absl/flags/config.h b/absl/flags/config.h new file mode 100644 index 00000000..a9fd97ad --- /dev/null +++ b/absl/flags/config.h @@ -0,0 +1,48 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_CONFIG_H_ +#define ABSL_FLAGS_CONFIG_H_ + +// Determine if we should strip string literals from the Flag objects. +// By default we strip string literals on mobile platforms. +#if !defined(ABSL_FLAGS_STRIP_NAMES) + +#if defined(__ANDROID__) +#define ABSL_FLAGS_STRIP_NAMES 1 + +#elif defined(__APPLE__) +#include +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#define ABSL_FLAGS_STRIP_NAMES 1 +#elif defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED +#define ABSL_FLAGS_STRIP_NAMES 1 +#endif // TARGET_OS_* +#endif + +#endif // !defined(ABSL_FLAGS_STRIP_NAMES) + +#if !defined(ABSL_FLAGS_STRIP_NAMES) +// If ABSL_FLAGS_STRIP_NAMES wasn't set on the command line or above, +// the default is not to strip. +#define ABSL_FLAGS_STRIP_NAMES 0 +#endif + +#if !defined(ABSL_FLAGS_STRIP_HELP) +// By default, if we strip names, we also strip help. +#define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES +#endif + +#endif // ABSL_FLAGS_CONFIG_H_ diff --git a/absl/flags/config_test.cc b/absl/flags/config_test.cc new file mode 100644 index 00000000..63899866 --- /dev/null +++ b/absl/flags/config_test.cc @@ -0,0 +1,61 @@ +// 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/config.h" + +#ifdef __APPLE__ +#include +#endif + +#include "gtest/gtest.h" + +#ifndef ABSL_FLAGS_STRIP_NAMES +#error ABSL_FLAGS_STRIP_NAMES is not defined +#endif + +#ifndef ABSL_FLAGS_STRIP_HELP +#error ABSL_FLAGS_STRIP_HELP is not defined +#endif + +namespace { + +// Test that ABSL_FLAGS_STRIP_NAMES and ABSL_FLAGS_STRIP_HELP are configured how +// we expect them to be configured by default. If you override this +// configuration, this test will fail, but the code should still be safe to use. +TEST(FlagsConfigTest, Test) { +#if defined(__ANDROID__) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 1); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 1); +#elif defined(__myriad2__) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0); +#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 1); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 1); +#elif defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 1); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 1); +#elif defined(__APPLE__) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0); +#elif defined(_WIN32) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0); +#elif defined(__linux__) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0); +#endif +} + +} // namespace diff --git a/absl/flags/declare.h b/absl/flags/declare.h new file mode 100644 index 00000000..7ef1d432 --- /dev/null +++ b/absl/flags/declare.h @@ -0,0 +1,60 @@ +// +// 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: declare.h +// ----------------------------------------------------------------------------- +// +// This file defines the ABSL_DECLARE_FLAG macro, allowing you to declare an +// `absl::Flag` for use within a translation unit. You should place this +// declaration within the header file associated with the .cc file that defines +// and owns the `Flag`. + +#ifndef ABSL_FLAGS_DECLARE_H_ +#define ABSL_FLAGS_DECLARE_H_ + +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// absl::Flag represents a flag of type 'T' created by ABSL_FLAG. +template +class Flag; + +} // namespace flags_internal + +// Flag +// +// Forward declaration of the `absl::Flag` type for use in defining the macro. +template +using Flag = flags_internal::Flag; + +} // inline namespace lts_2019_08_08 +} // namespace absl + +// ABSL_DECLARE_FLAG() +// +// This macro is a convenience for declaring use of an `absl::Flag` within a +// translation unit. This macro should be used within a header file to +// declare usage of the flag within any .cc file including that header file. +// +// The ABSL_DECLARE_FLAG(type, name) macro expands to: +// +// extern absl::Flag FLAGS_name; +#define ABSL_DECLARE_FLAG(type, name) extern ::absl::Flag FLAGS_##name + +#endif // ABSL_FLAGS_DECLARE_H_ diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc new file mode 100644 index 00000000..0858259b --- /dev/null +++ b/absl/flags/flag.cc @@ -0,0 +1,46 @@ +// +// 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/flag.h" + +#include + +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& flag) { return flag.Get(); } +#else +#define ABSL_FLAGS_ATOMIC_GET(T) \ + T GetFlag(const absl::Flag& flag) { \ + T result; \ + if (flag.AtomicGet(&result)) { \ + return result; \ + } \ + return flag.Get(); \ + } +#endif + +ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET) + +#undef ABSL_FLAGS_ATOMIC_GET + +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/flag.h b/absl/flags/flag.h new file mode 100644 index 00000000..28925927 --- /dev/null +++ b/absl/flags/flag.h @@ -0,0 +1,259 @@ +// +// 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: flag.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::Flag` type for holding command-line +// flag data, and abstractions to create, get and set such flag data. +// +// It is important to note that this type is **unspecified** (an implementation +// detail) and you do not construct or manipulate actual `absl::Flag` +// instances. Instead, you define and declare flags using the +// `ABSL_FLAG()` and `ABSL_DECLARE_FLAG()` macros, and get and set flag values +// using the `absl::GetFlag()` and `absl::SetFlag()` functions. + +#ifndef ABSL_FLAGS_FLAG_H_ +#define ABSL_FLAGS_FLAG_H_ + +#include "absl/base/attributes.h" +#include "absl/base/casts.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/marshalling.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// Flag +// +// An `absl::Flag` holds a command-line flag value, providing a runtime +// parameter to a binary. Such flags should be defined in the global namespace +// and (preferably) in the module containing the binary's `main()` function. +// +// You should not construct and cannot use the `absl::Flag` type directly; +// instead, you should declare flags using the `ABSL_DECLARE_FLAG()` macro +// within a header file, and define your flag using `ABSL_FLAG()` within your +// header's associated `.cc` file. Such flags will be named `FLAGS_name`. +// +// Example: +// +// .h file +// +// // Declares usage of a flag named "FLAGS_count" +// ABSL_DECLARE_FLAG(int, count); +// +// .cc file +// +// // Defines a flag named "FLAGS_count" with a default `int` value of 0. +// ABSL_FLAG(int, count, 0, "Count of items to process"); +// +// No public methods of `absl::Flag` are part of the Abseil Flags API. +template +using Flag = flags_internal::Flag; + +// GetFlag() +// +// Returns the value (of type `T`) of an `absl::Flag` instance, by value. Do +// not construct an `absl::Flag` directly and call `absl::GetFlag()`; +// instead, refer to flag's constructed variable name (e.g. `FLAGS_name`). +// Because this function returns by value and not by reference, it is +// thread-safe, but note that the operation may be expensive; as a result, avoid +// `absl::GetFlag()` within any tight loops. +// +// Example: +// +// // FLAGS_count is a Flag of type `int` +// int my_count = absl::GetFlag(FLAGS_count); +// +// // FLAGS_firstname is a Flag of type `std::string` +// std::string first_name = absl::GetFlag(FLAGS_firstname); +template +T GetFlag(const absl::Flag& flag) { +#define ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE(BIT) \ + static_assert( \ + !std::is_same::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 + + 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& 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 +// `absl::Flag` directly and call `absl::SetFlag()`; instead, use the +// flag's variable name (e.g. `FLAGS_name`). This function is +// thread-safe, but is potentially expensive. Avoid setting flags in general, +// but especially within performance-critical code. +template +void SetFlag(absl::Flag* flag, const T& v) { + flag->Set(v); +} + +// Overload of `SetFlag()` to allow callers to pass in a value that is +// convertible to `T`. E.g., use this overload to pass a "const char*" when `T` +// is `std::string`. +template +void SetFlag(absl::Flag* flag, const V& v) { + T value(v); + flag->Set(value); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + + +// ABSL_FLAG() +// +// This macro defines an `absl::Flag` instance of a specified type `T`: +// +// ABSL_FLAG(T, name, default_value, help); +// +// where: +// +// * `T` is a supported flag type (see the list of types in `marshalling.h`), +// * `name` designates the name of the flag (as a global variable +// `FLAGS_name`), +// * `default_value` is an expression holding the default value for this flag +// (which must be implicitly convertible to `T`), +// * `help` is the help text, which can also be an expression. +// +// This macro expands to a flag named 'FLAGS_name' of type 'T': +// +// absl::Flag FLAGS_name = ...; +// +// Note that all such instances are created as global variables. +// +// For `ABSL_FLAG()` values that you wish to expose to other translation units, +// it is recommended to define those flags within the `.cc` file associated with +// the header where the flag is declared. +// +// Note: do not construct objects of type `absl::Flag` directly. Only use the +// `ABSL_FLAG()` macro for such construction. +#define ABSL_FLAG(Type, name, default_value, help) \ + ABSL_FLAG_IMPL(Type, name, default_value, help) + +// ABSL_FLAG().OnUpdate() +// +// Defines a flag of type `T` with a callback attached: +// +// ABSL_FLAG(T, name, default_value, help).OnUpdate(callback); +// +// After any setting of the flag value, the callback will be called at least +// once. A rapid sequence of changes may be merged together into the same +// callback. No concurrent calls to the callback will be made for the same +// flag. Callbacks are allowed to read the current value of the flag but must +// not mutate that flag. +// +// The update mechanism guarantees "eventual consistency"; if the callback +// derives an auxiliary data structure from the flag value, it is guaranteed +// that eventually the flag value and the derived data structure will be +// consistent. +// +// Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this +// comment serves as its API documentation. + + +// ----------------------------------------------------------------------------- +// Implementation details below this section +// ----------------------------------------------------------------------------- + +// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_NAMES + +#if ABSL_FLAGS_STRIP_NAMES +#define ABSL_FLAG_IMPL_FLAGNAME(txt) "" +#define ABSL_FLAG_IMPL_FILENAME() "" +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar(&flag) +#else +#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt +#define ABSL_FLAG_IMPL_FILENAME() __FILE__ +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar(&flag) +#endif + +// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP + +#if ABSL_FLAGS_STRIP_HELP +#define ABSL_FLAG_IMPL_FLAGHELP(txt) absl::flags_internal::kStrippedFlagHelp +#else +#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); \ + } + +#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + static void* AbslFlagsInitFlag##name() { \ + return absl::flags_internal::MakeFromDefaultValue(default_value); \ + } + +// ABSL_FLAG_IMPL +// +// Note: Name of registrar object is not arbitrary. It is used to "grab" +// global name for FLAGS_no symbol, thus preventing the possibility +// of defining two flags with names foo and nofoo. +#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 FLAGS_##name( \ + ABSL_FLAG_IMPL_FLAGNAME(#name), &AbslFlagsWrapHelp##name, \ + ABSL_FLAG_IMPL_FILENAME(), \ + &absl::flags_internal::FlagMarshallingOps, \ + &AbslFlagsInitFlag##name); \ + extern bool FLAGS_no##name; \ + bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) + +// ABSL_RETIRED_FLAG +// +// Designates the flag (which is usually pre-existing) as "retired." A retired +// flag is a flag that is now unused by the program, but may still be passed on +// the command line, usually by production scripts. A retired flag is ignored +// and code can't access it at runtime. +// +// This macro registers a retired flag with given name and type, with a name +// identical to the name of the original flag you are retiring. The retired +// flag's type can change over time, so that you can retire code to support a +// custom flag type. +// +// This macro has the same signature as `ABSL_FLAG`. To retire a flag, simply +// replace an `ABSL_FLAG` definition with `ABSL_RETIRED_FLAG`, leaving the +// arguments unchanged (unless of course you actually want to retire the flag +// type at this time as well). +// +// `default_value` is only used as a double check on the type. `explanation` is +// unused. +// TODO(rogeeff): Return an anonymous struct instead of bool, and place it into +// the unnamed namespace. +#define ABSL_RETIRED_FLAG(type, flagname, default_value, explanation) \ + ABSL_ATTRIBUTE_UNUSED static const bool ignored_##flagname = \ + ([] { return type(default_value); }, \ + absl::flags_internal::RetiredFlag(#flagname)) + +#endif // ABSL_FLAGS_FLAG_H_ diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc new file mode 100644 index 00000000..1bcd7e96 --- /dev/null +++ b/absl/flags/flag_test.cc @@ -0,0 +1,482 @@ +// +// 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/flag.h" + +#include "gtest/gtest.h" +#include "absl/strings/match.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" + +ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag); +ABSL_DECLARE_FLAG(std::vector, mistyped_string_flag); + +namespace { + +namespace flags = absl::flags_internal; + +std::string TestHelpMsg() { return "help"; } +template +void* TestMakeDflt() { + return new T{}; +} +void TestCallback() {} + +template +bool TestConstructionFor() { + constexpr flags::Flag f1("f1", &TestHelpMsg, "file", + &absl::flags_internal::FlagMarshallingOps, + &TestMakeDflt); + EXPECT_EQ(f1.Name(), "f1"); + EXPECT_EQ(f1.Help(), "help"); + EXPECT_EQ(f1.Filename(), "file"); + + ABSL_CONST_INIT static flags::Flag f2( + "f2", &TestHelpMsg, "file", &absl::flags_internal::FlagMarshallingOps, + &TestMakeDflt); + flags::FlagRegistrar(&f2).OnUpdate(TestCallback); + + EXPECT_EQ(f2.Name(), "f2"); + EXPECT_EQ(f2.Help(), "help"); + EXPECT_EQ(f2.Filename(), "file"); + + return true; +} + +struct UDT { + UDT() = default; + UDT(const UDT&) = default; +}; +bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } +std::string AbslUnparseFlag(const UDT&) { return ""; } + +TEST(FlagTest, TestConstruction) { + TestConstructionFor(); + TestConstructionFor(); + TestConstructionFor(); + TestConstructionFor(); + TestConstructionFor(); + TestConstructionFor(); + TestConstructionFor(); + TestConstructionFor(); + TestConstructionFor(); + TestConstructionFor(); + + TestConstructionFor(); +} + +// -------------------------------------------------------------------- + +} // namespace + +ABSL_DECLARE_FLAG(bool, test_flag_01); +ABSL_DECLARE_FLAG(int, test_flag_02); +ABSL_DECLARE_FLAG(int16_t, test_flag_03); +ABSL_DECLARE_FLAG(uint16_t, test_flag_04); +ABSL_DECLARE_FLAG(int32_t, test_flag_05); +ABSL_DECLARE_FLAG(uint32_t, test_flag_06); +ABSL_DECLARE_FLAG(int64_t, test_flag_07); +ABSL_DECLARE_FLAG(uint64_t, test_flag_08); +ABSL_DECLARE_FLAG(double, test_flag_09); +ABSL_DECLARE_FLAG(float, test_flag_10); +ABSL_DECLARE_FLAG(std::string, test_flag_11); + +namespace { + +#if !ABSL_FLAGS_STRIP_NAMES + +TEST(FlagTest, TestFlagDeclaration) { + // test that we can access flag objects. + EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01"); + EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02"); + EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03"); + EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04"); + EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05"); + EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06"); + EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07"); + EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08"); + EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09"); + EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10"); + EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11"); +} +#endif // !ABSL_FLAGS_STRIP_NAMES + +// -------------------------------------------------------------------- + +} // namespace + +ABSL_FLAG(bool, test_flag_01, true, "test flag 01"); +ABSL_FLAG(int, test_flag_02, 1234, "test flag 02"); +ABSL_FLAG(int16_t, test_flag_03, -34, "test flag 03"); +ABSL_FLAG(uint16_t, test_flag_04, 189, "test flag 04"); +ABSL_FLAG(int32_t, test_flag_05, 10765, "test flag 05"); +ABSL_FLAG(uint32_t, test_flag_06, 40000, "test flag 06"); +ABSL_FLAG(int64_t, test_flag_07, -1234567, "test flag 07"); +ABSL_FLAG(uint64_t, test_flag_08, 9876543, "test flag 08"); +ABSL_FLAG(double, test_flag_09, -9.876e-50, "test flag 09"); +ABSL_FLAG(float, test_flag_10, 1.234e12f, "test flag 10"); +ABSL_FLAG(std::string, test_flag_11, "", "test flag 11"); + +namespace { + +#if !ABSL_FLAGS_STRIP_NAMES +TEST(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_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_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_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_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_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_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_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_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_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_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)); +} +#endif // !ABSL_FLAGS_STRIP_NAMES + +// -------------------------------------------------------------------- + +TEST(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); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), ""); +} + +// -------------------------------------------------------------------- + +TEST(FlagTest, TestGetSet) { + absl::SetFlag(&FLAGS_test_flag_01, false); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), false); + + absl::SetFlag(&FLAGS_test_flag_02, 321); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 321); + + absl::SetFlag(&FLAGS_test_flag_03, 67); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), 67); + + absl::SetFlag(&FLAGS_test_flag_04, 1); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 1); + + absl::SetFlag(&FLAGS_test_flag_05, -908); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), -908); + + absl::SetFlag(&FLAGS_test_flag_06, 4001); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 4001); + + absl::SetFlag(&FLAGS_test_flag_07, -23456); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -23456); + + absl::SetFlag(&FLAGS_test_flag_08, 975310); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 975310); + + absl::SetFlag(&FLAGS_test_flag_09, 1.00001); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), 1.00001, 1e-10); + + absl::SetFlag(&FLAGS_test_flag_10, -3.54f); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), -3.54f, 1e-6f); + + absl::SetFlag(&FLAGS_test_flag_11, "asdf"); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "asdf"); +} + +// -------------------------------------------------------------------- + +int GetDflt1() { return 1; } + +} // namespace + +ABSL_FLAG(int, test_flag_12, GetDflt1(), "test flag 12"); +ABSL_FLAG(std::string, test_flag_13, absl::StrCat("AAA", "BBB"), + "test flag 13"); + +namespace { + +TEST(FlagTest, TestNonConstexprDefault) { + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), 1); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), "AAABBB"); +} + +// -------------------------------------------------------------------- + +} // namespace + +ABSL_FLAG(bool, test_flag_14, true, absl::StrCat("test ", "flag ", "14")); + +namespace { + +#if !ABSL_FLAGS_STRIP_HELP +TEST(FlagTest, TestNonConstexprHelp) { + EXPECT_EQ(FLAGS_test_flag_14.Help(), "test flag 14"); +} +#endif //! ABSL_FLAGS_STRIP_HELP + +// -------------------------------------------------------------------- + +int cb_test_value = -1; +void TestFlagCB(); + +} // namespace + +ABSL_FLAG(int, test_flag_with_cb, 100, "").OnUpdate(TestFlagCB); + +ABSL_FLAG(int, test_flag_with_lambda_cb, 200, "").OnUpdate([]() { + cb_test_value = absl::GetFlag(FLAGS_test_flag_with_lambda_cb) + + absl::GetFlag(FLAGS_test_flag_with_cb); +}); + +namespace { + +void TestFlagCB() { cb_test_value = absl::GetFlag(FLAGS_test_flag_with_cb); } + +// Tests side-effects of callback invocation. +TEST(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); + + absl::SetFlag(&FLAGS_test_flag_with_cb, 1); + EXPECT_EQ(cb_test_value, 1); + + absl::SetFlag(&FLAGS_test_flag_with_lambda_cb, 3); + EXPECT_EQ(cb_test_value, 4); +} + +// -------------------------------------------------------------------- + +struct CustomUDT { + CustomUDT() : a(1), b(1) {} + CustomUDT(int a_, int b_) : a(a_), b(b_) {} + + friend bool operator==(const CustomUDT& f1, const CustomUDT& f2) { + return f1.a == f2.a && f1.b == f2.b; + } + + int a; + int b; +}; +bool AbslParseFlag(absl::string_view in, CustomUDT* f, std::string*) { + std::vector parts = + absl::StrSplit(in, ':', absl::SkipWhitespace()); + + if (parts.size() != 2) return false; + + if (!absl::SimpleAtoi(parts[0], &f->a)) return false; + + if (!absl::SimpleAtoi(parts[1], &f->b)) return false; + + return true; +} +std::string AbslUnparseFlag(const CustomUDT& f) { + return absl::StrCat(f.a, ":", f.b); +} + +} // namespace + +ABSL_FLAG(CustomUDT, test_flag_15, CustomUDT(), "test flag 15"); + +namespace { + +TEST(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)); +} + +// 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 + +TEST(Flagtest, TestTypeMismatchValidations) { + // For builtin types, GetFlag() only does validation in debug mode. + EXPECT_DEBUG_DEATH( + 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), + "Flag 'mistyped_int_flag' is defined as one type and declared " + "as another"); + + EXPECT_DEATH(absl::GetFlag(FLAGS_mistyped_string_flag), + "Flag 'mistyped_string_flag' is defined as one type and " + "declared as another"); + EXPECT_DEATH( + absl::SetFlag(&FLAGS_mistyped_string_flag, std::vector{}), + "Flag 'mistyped_string_flag' is defined as one type and declared as " + "another"); +} + +#endif + +// -------------------------------------------------------------------- + +// A contrived type that offers implicit and explicit conversion from specific +// source types. +struct ConversionTestVal { + ConversionTestVal() = default; + explicit ConversionTestVal(int a_in) : a(a_in) {} + + enum class ViaImplicitConv { kTen = 10, kEleven }; + // NOLINTNEXTLINE + ConversionTestVal(ViaImplicitConv from) : a(static_cast(from)) {} + + int a; +}; + +bool AbslParseFlag(absl::string_view in, ConversionTestVal* val_out, + std::string*) { + if (!absl::SimpleAtoi(in, &val_out->a)) { + return false; + } + return true; +} +std::string AbslUnparseFlag(const ConversionTestVal& val) { + return absl::StrCat(val.a); +} + +} // namespace + +// Flag default values can be specified with a value that converts to the flag +// value type implicitly. +ABSL_FLAG(ConversionTestVal, test_flag_16, + ConversionTestVal::ViaImplicitConv::kTen, "test flag 16"); + +namespace { + +TEST(FlagTest, CanSetViaImplicitConversion) { + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 10); + absl::SetFlag(&FLAGS_test_flag_16, + ConversionTestVal::ViaImplicitConv::kEleven); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 11); +} + +// -------------------------------------------------------------------- + +struct NonDfltConstructible { + public: + // This constructor tests that we can initialize the flag with int value + NonDfltConstructible(int i) : value(i) {} // NOLINT + + // This constructor tests that we can't initialize the flag with char value + // but can with explicitly constructed NonDfltConstructible. + explicit NonDfltConstructible(char c) : value(100 + static_cast(c)) {} + + int value; +}; + +bool AbslParseFlag(absl::string_view in, NonDfltConstructible* ndc_out, + std::string*) { + return absl::SimpleAtoi(in, &ndc_out->value); +} +std::string AbslUnparseFlag(const NonDfltConstructible& ndc) { + return absl::StrCat(ndc.value); +} + +} // namespace + +ABSL_FLAG(NonDfltConstructible, ndc_flag1, NonDfltConstructible('1'), + "Flag with non default constructible type"); +ABSL_FLAG(NonDfltConstructible, ndc_flag2, 0, + "Flag with non default constructible type"); + +namespace { + +TEST(FlagTest, TestNonDefaultConstructibleType) { + EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, '1' + 100); + EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 0); + + absl::SetFlag(&FLAGS_ndc_flag1, NonDfltConstructible('A')); + absl::SetFlag(&FLAGS_ndc_flag2, 25); + + EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, 'A' + 100); + EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 25); +} + +// -------------------------------------------------------------------- + +} // namespace + +ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr"); +ABSL_RETIRED_FLAG(int, old_int_flag, (int)std::sqrt(10), "old descr"); +ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); + +namespace { + +TEST(FlagTest, TestRetiredFlagRegistration) { + bool is_bool = false; + EXPECT_TRUE(flags::IsRetiredFlag("old_bool_flag", &is_bool)); + EXPECT_TRUE(is_bool); + EXPECT_TRUE(flags::IsRetiredFlag("old_int_flag", &is_bool)); + EXPECT_FALSE(is_bool); + EXPECT_TRUE(flags::IsRetiredFlag("old_str_flag", &is_bool)); + EXPECT_FALSE(is_bool); + EXPECT_FALSE(flags::IsRetiredFlag("some_other_flag", &is_bool)); +} + +} // namespace diff --git a/absl/flags/flag_test_defs.cc b/absl/flags/flag_test_defs.cc new file mode 100644 index 00000000..3366c580 --- /dev/null +++ b/absl/flags/flag_test_defs.cc @@ -0,0 +1,22 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file is used to test the mismatch of the flag type between definition +// and declaration. These are definitions. flag_test.cc contains declarations. +#include +#include "absl/flags/flag.h" + +ABSL_FLAG(int, mistyped_int_flag, 0, ""); +ABSL_FLAG(std::string, mistyped_string_flag, "", ""); diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc new file mode 100644 index 00000000..f964165e --- /dev/null +++ b/absl/flags/internal/commandlineflag.cc @@ -0,0 +1,496 @@ +// +// 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 + +#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()) return false; + ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE) + DONT_VALIDATE(std::string) + DONT_VALIDATE(std::vector) +#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(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()) { \ + 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()) { + 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()) { \ + 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()) { \ + return *reinterpret_cast(a) != *reinterpret_cast(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 new file mode 100644 index 00000000..382553d2 --- /dev/null +++ b/absl/flags/internal/commandlineflag.h @@ -0,0 +1,385 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ +#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ + +#include + +#include "absl/base/macros.h" +#include "absl/flags/marshalling.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/optional.h" + +namespace absl { +inline namespace lts_2019_08_08 { +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*); + +// Options that control SetCommandLineOptionWithMode. +enum FlagSettingMode { + // update the flag's value unconditionally (can call this multiple times). + SET_FLAGS_VALUE, + // update the flag's value, but *only if* it has not yet been updated + // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef". + SET_FLAG_IF_DEFAULT, + // set the flag's default value to this. If the flag has not been updated + // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef") + // change the flag's current value to the new default value as well. + SET_FLAGS_DEFAULT +}; + +// Options that control SetFromString: Source of a value. +enum ValueSource { + // Flag is being set by value specified on a command line. + kCommandLine, + // Flag is being set by value specified in the code. + 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 +void* FlagOps(FlagOp op, const void* v1, void* v2) { + switch (op) { + case kDelete: + delete static_cast(v1); + return nullptr; + case kClone: + return new T(*static_cast(v1)); + case kCopy: + *static_cast(v2) = *static_cast(v1); + return nullptr; + case kCopyConstruct: + new (v2) T(*static_cast(v1)); + return nullptr; + case kSizeof: + return reinterpret_cast(sizeof(T)); + default: + return nullptr; + } +} + +template +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(v2)); + if (!absl::ParseFlag(*static_cast(v1), &temp, + static_cast(v3))) { + return nullptr; + } + *static_cast(v2) = std::move(temp); + return v2; + } + case kUnparse: + *static_cast(v2) = + absl::UnparseFlag(*static_cast(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(reinterpret_cast( + 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 since anybody passing 0 or nullptr in to a CommandLineFlag +// would find an ambiguity. +class HelpText { + 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; + + private: + explicit constexpr HelpText(const HelpGenFunc fn, const char* msg) + : help_function_(fn), help_message_(msg) {} + + HelpGenFunc help_function_; + const char* help_message_; +}; + +// 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; + + // 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; + + // Return true iff flag has type T. + template + inline bool IsOfType() const { + return this->op == &flags_internal::FlagOps; + } + + // Attempts to retrieve the flag value. Returns value on success, + // absl::nullopt otherwise. + template + absl::optional Get() const { + if (IsRetired() || flags_internal::FlagOps != this->op) + return absl::nullopt; + + T res; + Read(&res, flags_internal::FlagOps); + + return res; + } + + void SetCallback(const flags_internal::FlagCallback mutation_callback); + void InvokeCallback(); + + // Sets the value of the flag based on specified std::string `value`. If the flag + // was successfully set to new value, it returns true. Otherwise, sets `error` + // to indicate the error, leaves the flag unchanged, and returns false. There + // are three ways to set the flag's value: + // * Update the current flag value + // * Update the flag's default value + // * Update the current flag value if it was never set before + // The mode is selected based on `set_mode` parameter. + bool SetFromString(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, std::string* error); + + void StoreAtomic(size_t size); + + 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; + + 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 inited; // fields have been lazily initialized + + // 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 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); +}; + +// 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) + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc new file mode 100644 index 00000000..f0d57adb --- /dev/null +++ b/absl/flags/internal/commandlineflag_test.cc @@ -0,0 +1,196 @@ +// +// 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 "gtest/gtest.h" +#include "absl/flags/flag.h" +#include "absl/flags/internal/registry.h" +#include "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" + +ABSL_FLAG(int, int_flag, 201, "int_flag help"); +ABSL_FLAG(std::string, string_flag, "dflt", + absl::StrCat("string_flag", " help")); +ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help"); + +namespace { + +namespace flags = absl::flags_internal; + +class CommandLineFlagTest : public testing::Test { + protected: + void SetUp() override { flag_saver_ = absl::make_unique(); } + void TearDown() override { flag_saver_.reset(); } + + private: + std::unique_ptr flag_saver_; +}; + +TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + + ASSERT_TRUE(flag_01); + EXPECT_EQ(flag_01->Name(), "int_flag"); + EXPECT_EQ(flag_01->Help(), "int_flag help"); + EXPECT_EQ(flag_01->Typename(), ""); + EXPECT_TRUE(!flag_01->IsRetired()); + EXPECT_TRUE(flag_01->IsOfType()); + EXPECT_TRUE(absl::EndsWith( + flag_01->Filename(), + "absl/flags/internal/commandlineflag_test.cc")); + + auto* flag_02 = flags::FindCommandLineFlag("string_flag"); + + ASSERT_TRUE(flag_02); + EXPECT_EQ(flag_02->Name(), "string_flag"); + EXPECT_EQ(flag_02->Help(), "string_flag help"); + EXPECT_EQ(flag_02->Typename(), ""); + EXPECT_TRUE(!flag_02->IsRetired()); + EXPECT_TRUE(flag_02->IsOfType()); + EXPECT_TRUE(absl::EndsWith( + flag_02->Filename(), + "absl/flags/internal/commandlineflag_test.cc")); + + auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag"); + + ASSERT_TRUE(flag_03); + EXPECT_EQ(flag_03->Name(), "bool_retired_flag"); + EXPECT_EQ(flag_03->Help(), ""); + EXPECT_EQ(flag_03->Typename(), ""); + EXPECT_TRUE(flag_03->IsRetired()); + EXPECT_TRUE(flag_03->IsOfType()); + EXPECT_EQ(flag_03->Filename(), "RETIRED"); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestValueAccessMethods) { + absl::SetFlag(&FLAGS_int_flag, 301); + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + + ASSERT_TRUE(flag_01); + EXPECT_EQ(flag_01->CurrentValue(), "301"); + EXPECT_EQ(flag_01->DefaultValue(), "201"); + + absl::SetFlag(&FLAGS_string_flag, "new_str_value"); + auto* flag_02 = flags::FindCommandLineFlag("string_flag"); + + ASSERT_TRUE(flag_02); + EXPECT_EQ(flag_02->CurrentValue(), "new_str_value"); + EXPECT_EQ(flag_02->DefaultValue(), "dflt"); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestSetFromStringCurrentValue) { + std::string err; + + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(flag_01->SetFromString("11", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(flag_01->SetFromString("-123", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(!flag_01->SetFromString("xyz", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); + EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'"); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(!flag_01->SetFromString("A1", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); + EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'"); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(flag_01->SetFromString("0x10", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(flag_01->SetFromString("011", flags::SET_FLAGS_VALUE, + flags::kCommandLine, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); + EXPECT_TRUE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(!flag_01->SetFromString("", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'"); + + auto* flag_02 = flags::FindCommandLineFlag("string_flag"); + EXPECT_TRUE(flag_02->SetFromString("xyz", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz"); + + EXPECT_TRUE(flag_02->SetFromString("", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), ""); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestSetFromStringDefaultValue) { + std::string err; + + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + + EXPECT_TRUE(flag_01->SetFromString("111", flags::SET_FLAGS_DEFAULT, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(flag_01->DefaultValue(), "111"); + + auto* flag_02 = flags::FindCommandLineFlag("string_flag"); + + EXPECT_TRUE(flag_02->SetFromString("abc", flags::SET_FLAGS_DEFAULT, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(flag_02->DefaultValue(), "abc"); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestSetFromStringIfDefault) { + std::string err; + + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + + EXPECT_TRUE(flag_01->SetFromString("22", flags::SET_FLAG_IF_DEFAULT, + flags::kProgrammaticChange, &err)) + << err; + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); + + EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); + // EXPECT_EQ(err, "ERROR: int_flag is already set to 22"); + + // Reset back to default value + EXPECT_TRUE(flag_01->SetFromString("201", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + + EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201); + // EXPECT_EQ(err, "ERROR: int_flag is already set to 201"); +} + +} // namespace diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h new file mode 100644 index 00000000..9a5d9b4b --- /dev/null +++ b/absl/flags/internal/flag.h @@ -0,0 +1,123 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_ +#define ABSL_FLAGS_INTERNAL_FLAG_H_ + +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/registry.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// This is "unspecified" implementation of absl::Flag type. +template +class Flag : public flags_internal::CommandLineFlag { + 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, marshalling_op_arg, + initial_value_gen, + /*retired_arg=*/false, /*def_arg=*/nullptr, + /*cur_arg=*/nullptr) {} + + 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. + union U { + T value; + U() {} + ~U() { value.~T(); } + }; + U u; + + this->Read(&u.value, &flags_internal::FlagOps); + return std::move(u.value); + } + + 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; + } + + return false; + } + + void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps); } +}; + +// This class facilitates Flag object registration and tail expression-based +// flag definition, for example: +// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); +template +class FlagRegistrar { + public: + explicit FlagRegistrar(Flag* flag) : flag_(flag) { + if (do_register) flags_internal::RegisterCommandLineFlag(flag_); + } + + FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && { + flag_->SetCallback(cb); + return *this; + } + + // Make the registrar "die" gracefully as a bool on a line where registration + // happens. Registrar objects are intended to live only as temporary. + operator bool() const { return true; } // NOLINT + + private: + Flag* flag_; // Flag being registered (not owned). +}; + +// This struct and corresponding overload to MakeDefaultValue are used to +// facilitate usage of {} as default value in ABSL_FLAG macro. +struct EmptyBraces {}; + +template +T* MakeFromDefaultValue(T t) { + return new T(std::move(t)); +} + +template +T* MakeFromDefaultValue(EmptyBraces) { + return new T; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_FLAG_H_ diff --git a/absl/flags/internal/parse.h b/absl/flags/internal/parse.h new file mode 100644 index 00000000..6c79dc87 --- /dev/null +++ b/absl/flags/internal/parse.h @@ -0,0 +1,50 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_PARSE_H_ +#define ABSL_FLAGS_INTERNAL_PARSE_H_ + +#include +#include + +#include "absl/flags/declare.h" + +ABSL_DECLARE_FLAG(std::vector, flagfile); +ABSL_DECLARE_FLAG(std::vector, fromenv); +ABSL_DECLARE_FLAG(std::vector, tryfromenv); +ABSL_DECLARE_FLAG(std::vector, undefok); + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs }; +enum class UsageFlagsAction { kHandleUsage, kIgnoreUsage }; +enum class OnUndefinedFlag { + kIgnoreUndefined, + kReportUndefined, + kAbortIfUndefined +}; + +std::vector ParseCommandLineImpl(int argc, char* argv[], + ArgvListAction arg_list_act, + UsageFlagsAction usage_flag_act, + OnUndefinedFlag on_undef_flag); + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PARSE_H_ diff --git a/absl/flags/internal/path_util.h b/absl/flags/internal/path_util.h new file mode 100644 index 00000000..623a7bc9 --- /dev/null +++ b/absl/flags/internal/path_util.h @@ -0,0 +1,62 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ +#define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ + +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// A portable interface that returns the basename of the filename passed as an +// argument. It is similar to basename(3) +// . +// For example: +// flags_internal::Basename("a/b/prog/file.cc") +// returns "file.cc" +// flags_internal::Basename("file.cc") +// returns "file.cc" +inline absl::string_view Basename(absl::string_view filename) { + auto last_slash_pos = filename.find_last_of("/\\"); + + return last_slash_pos == absl::string_view::npos + ? filename + : filename.substr(last_slash_pos + 1); +} + +// A portable interface that returns the directory name of the filename +// passed as an argument, including the trailing slash. +// Returns the empty string if a slash is not found in the input file name. +// For example: +// flags_internal::Package("a/b/prog/file.cc") +// returns "a/b/prog/" +// flags_internal::Package("file.cc") +// returns "" +inline absl::string_view Package(absl::string_view filename) { + auto last_slash_pos = filename.find_last_of("/\\"); + + return last_slash_pos == absl::string_view::npos + ? absl::string_view() + : filename.substr(0, last_slash_pos + 1); +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ diff --git a/absl/flags/internal/path_util_test.cc b/absl/flags/internal/path_util_test.cc new file mode 100644 index 00000000..2091373c --- /dev/null +++ b/absl/flags/internal/path_util_test.cc @@ -0,0 +1,46 @@ +// +// 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/path_util.h" + +#include "gtest/gtest.h" + +namespace { + +namespace flags = absl::flags_internal; + +TEST(FlagsPathUtilTest, TestBasename) { + EXPECT_EQ(flags::Basename(""), ""); + EXPECT_EQ(flags::Basename("a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("dir/a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("dir1/dir2/a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("../dir1/dir2/a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("/dir1/dir2/a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("/dir1/dir2/../dir3/a.cc"), "a.cc"); +} + +// -------------------------------------------------------------------- + +TEST(FlagsPathUtilTest, TestPackage) { + EXPECT_EQ(flags::Package(""), ""); + EXPECT_EQ(flags::Package("a.cc"), ""); + EXPECT_EQ(flags::Package("dir/a.cc"), "dir/"); + EXPECT_EQ(flags::Package("dir1/dir2/a.cc"), "dir1/dir2/"); + EXPECT_EQ(flags::Package("../dir1/dir2/a.cc"), "../dir1/dir2/"); + EXPECT_EQ(flags::Package("/dir1/dir2/a.cc"), "/dir1/dir2/"); + EXPECT_EQ(flags::Package("/dir1/dir2/../dir3/a.cc"), "/dir1/dir2/../dir3/"); +} + +} // namespace diff --git a/absl/flags/internal/program_name.cc b/absl/flags/internal/program_name.cc new file mode 100644 index 00000000..28b8ed38 --- /dev/null +++ b/absl/flags/internal/program_name.cc @@ -0,0 +1,55 @@ +// +// 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/program_name.h" + +#include + +#include "absl/flags/internal/path_util.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +inline namespace lts_2019_08_08 { +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; + +std::string ProgramInvocationName() { + absl::MutexLock l(&program_name_guard); + + return program_name ? *program_name : "UNKNOWN"; +} + +std::string ShortProgramInvocationName() { + absl::MutexLock l(&program_name_guard); + + return program_name ? std::string(flags_internal::Basename(*program_name)) + : "UNKNOWN"; +} + +void SetProgramInvocationName(absl::string_view prog_name_str) { + absl::MutexLock l(&program_name_guard); + + if (!program_name) + program_name = new std::string(prog_name_str); + else + program_name->assign(prog_name_str.data(), prog_name_str.size()); +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/internal/program_name.h b/absl/flags/internal/program_name.h new file mode 100644 index 00000000..a2f0ca10 --- /dev/null +++ b/absl/flags/internal/program_name.h @@ -0,0 +1,49 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_ +#define ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_ + +#include + +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Program name + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// Returns program invocation name or "UNKNOWN" if `SetProgramInvocationName()` +// is never called. At the moment this is always set to argv[0] as part of +// library initialization. +std::string ProgramInvocationName(); + +// Returns base name for program invocation name. For example, if +// ProgramInvocationName() == "a/b/mybinary" +// then +// ShortProgramInvocationName() == "mybinary" +std::string ShortProgramInvocationName(); + +// Sets program invocation name to a new value. Should only be called once +// during program initialization, before any threads are spawned. +void SetProgramInvocationName(absl::string_view prog_name_str); + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // 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 new file mode 100644 index 00000000..ed69218b --- /dev/null +++ b/absl/flags/internal/program_name_test.cc @@ -0,0 +1,60 @@ +// +// 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/program_name.h" + +#include "gtest/gtest.h" +#include "absl/strings/match.h" + +namespace { + +namespace flags = absl::flags_internal; + +TEST(FlagsPathUtilTest, TestInitialProgamName) { + flags::SetProgramInvocationName("absl/flags/program_name_test"); + std::string program_name = flags::ProgramInvocationName(); + for (char& c : program_name) + if (c == '\\') c = '/'; + +#if !defined(__wasm__) && !defined(__asmjs__) + const std::string expect_name = "absl/flags/program_name_test"; + const std::string expect_basename = "program_name_test"; +#else + // For targets that generate javascript or webassembly the invocation name + // has the special value below. + const std::string expect_name = "this.program"; + const std::string expect_basename = "this.program"; +#endif + + EXPECT_TRUE(absl::EndsWith(program_name, expect_name)) << program_name; + EXPECT_EQ(flags::ShortProgramInvocationName(), expect_basename); +} + +TEST(FlagsPathUtilTest, TestProgamNameInterfaces) { + flags::SetProgramInvocationName("a/my_test"); + + EXPECT_EQ(flags::ProgramInvocationName(), "a/my_test"); + EXPECT_EQ(flags::ShortProgramInvocationName(), "my_test"); + + absl::string_view not_null_terminated("absl/aaa/bbb"); + not_null_terminated = not_null_terminated.substr(1, 10); + + flags::SetProgramInvocationName(not_null_terminated); + + EXPECT_EQ(flags::ProgramInvocationName(), "bsl/aaa/bb"); + EXPECT_EQ(flags::ShortProgramInvocationName(), "bb"); +} + +} // namespace diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc new file mode 100644 index 00000000..e39264e5 --- /dev/null +++ b/absl/flags/internal/registry.cc @@ -0,0 +1,445 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/registry.h" + +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/flags/config.h" +#include "absl/flags/usage_config.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +// -------------------------------------------------------------------- +// FlagRegistry implementation +// A FlagRegistry holds all flag objects indexed +// by their names so that if you know a flag's name you can access or +// set it. + +namespace absl { +inline namespace lts_2019_08_08 { +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 +// A FlagRegistry singleton object holds all flag objects indexed +// by their names so that if you know a flag's name (as a C +// string), you can access or set it. If the function is named +// FooLocked(), you must own the registry lock before calling +// the function; otherwise, you should *not* hold the lock, and +// the function will acquire it itself if needed. +// -------------------------------------------------------------------- + +class FlagRegistry { + public: + FlagRegistry() = default; + ~FlagRegistry() { + for (auto& p : flags_) { + DestroyFlag(p.second); + } + } + + // 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(); } + + // Returns the flag object for the specified name, or nullptr if not found. + // Will emit a warning if a 'retired' flag is specified. + CommandLineFlag* FindFlagLocked(absl::string_view name); + + // Returns the retired flag object for the specified name, or nullptr if not + // found or not retired. Does not emit a warning. + CommandLineFlag* FindRetiredFlagLocked(absl::string_view name); + + static FlagRegistry* GlobalRegistry(); // returns a singleton registry + + private: + friend class FlagSaverImpl; // reads all the flags in order to copy them + friend void ForEachFlagUnlocked( + std::function visitor); + + // The map from name to flag, for FindFlagLocked(). + using FlagMap = std::map; + using FlagIterator = FlagMap::iterator; + using FlagConstIterator = FlagMap::const_iterator; + FlagMap flags_; + + absl::Mutex lock_; + + // Disallow + FlagRegistry(const FlagRegistry&); + FlagRegistry& operator=(const FlagRegistry&); +}; + +FlagRegistry* FlagRegistry::GlobalRegistry() { + static FlagRegistry* global_registry = new FlagRegistry; + return global_registry; +} + +namespace { + +class FlagRegistryLock { + public: + explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); } + ~FlagRegistryLock() { fr_->Unlock(); } + + private: + FlagRegistry* const fr_; +}; + +} // namespace + +void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { + FlagRegistryLock registry_lock(this); + std::pair ins = + flags_.insert(FlagMap::value_type(flag->Name(), flag)); + if (ins.second == false) { // means the name was already in the map + CommandLineFlag* old_flag = ins.first->second; + if (flag->IsRetired() != old_flag->IsRetired()) { + // All registrations must agree on the 'retired' flag. + flags_internal::ReportUsageError( + absl::StrCat( + "Retired flag '", flag->Name(), + "' was defined normally in file '", + (flag->IsRetired() ? old_flag->Filename() : flag->Filename()), + "'."), + true); + } else if (flag->op != old_flag->op) { + flags_internal::ReportUsageError( + absl::StrCat("Flag '", flag->Name(), + "' was defined more than once but with " + "differing types. Defined in files '", + old_flag->Filename(), "' and '", flag->Filename(), + "' with types '", old_flag->Typename(), "' and '", + flag->Typename(), "', respectively."), + true); + } else if (old_flag->IsRetired()) { + // Retired definitions are idempotent. Just keep the old one. + DestroyFlag(flag); + return; + } else if (old_flag->Filename() != flag->Filename()) { + flags_internal::ReportUsageError( + absl::StrCat("Flag '", flag->Name(), + "' was defined more than once (in files '", + old_flag->Filename(), "' and '", flag->Filename(), + "')."), + true); + } else { + flags_internal::ReportUsageError( + absl::StrCat( + "Something wrong with flag '", flag->Name(), "' in file '", + flag->Filename(), "'. One possibility: file '", flag->Filename(), + "' is being linked both statically and dynamically into this " + "executable. e.g. some files listed as srcs to a test and also " + "listed as srcs of some shared lib deps of the same test."), + true); + } + // All cases above are fatal, except for the retired flags. + std::exit(1); + } +} + +CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) { + FlagConstIterator i = flags_.find(name); + if (i == flags_.end()) { + return nullptr; + } + + if (i->second->IsRetired()) { + flags_internal::ReportUsageError( + absl::StrCat("Accessing retired flag '", name, "'"), false); + } + + return i->second; +} + +CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) { + FlagConstIterator i = flags_.find(name); + if (i == flags_.end() || !i->second->IsRetired()) { + return nullptr; + } + + return i->second; +} + +// -------------------------------------------------------------------- +// FlagSaver +// FlagSaverImpl +// This class stores the states of all flags at construct time, +// and restores all flags to that state at destruct time. +// Its major implementation challenge is that it never modifies +// pointers in the 'main' registry, so global FLAG_* vars always +// point to the right place. +// -------------------------------------------------------------------- + +class FlagSaverImpl { + public: + // 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); + } + } + + // 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; + } + 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. + 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))); + } + } + } + + 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 backup_registry_; + + FlagSaverImpl(const FlagSaverImpl&); // no copying! + void operator=(const FlagSaverImpl&); +}; + +FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) { + impl_->SaveFromRegistry(); +} + +void FlagSaver::Ignore() { + delete impl_; + impl_ = nullptr; +} + +FlagSaver::~FlagSaver() { + if (!impl_) return; + + impl_->RestoreToRegistry(); + delete impl_; +} + +// -------------------------------------------------------------------- +// 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; + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + FlagRegistryLock frl(registry); + + return registry->FindFlagLocked(name); +} + +CommandLineFlag* FindRetiredFlag(absl::string_view name) { + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + FlagRegistryLock frl(registry); + + return registry->FindRetiredFlagLocked(name); +} + +// -------------------------------------------------------------------- + +void ForEachFlagUnlocked(std::function visitor) { + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + for (FlagRegistry::FlagConstIterator i = registry->flags_.begin(); + i != registry->flags_.end(); ++i) { + visitor(i->second); + } +} + +void ForEachFlag(std::function visitor) { + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + FlagRegistryLock frl(registry); + ForEachFlagUnlocked(visitor); +} + +// -------------------------------------------------------------------- + +void GetAllFlags(std::vector* 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; +} + +// -------------------------------------------------------------------- + +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); + FlagRegistry::GlobalRegistry()->RegisterFlag(flag); + return true; +} + +// -------------------------------------------------------------------- + +bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) { + assert(!name.empty()); + CommandLineFlag* flag = flags_internal::FindRetiredFlag(name); + if (flag == nullptr) { + return false; + } + assert(type_is_bool); + *type_is_bool = flag->IsOfType(); + return true; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h new file mode 100644 index 00000000..d851d245 --- /dev/null +++ b/absl/flags/internal/registry.h @@ -0,0 +1,169 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_REGISTRY_H_ +#define ABSL_FLAGS_INTERNAL_REGISTRY_H_ + +#include +#include +#include + +#include "absl/base/macros.h" +#include "absl/flags/internal/commandlineflag.h" + +// -------------------------------------------------------------------- +// Global flags registry API. + +namespace absl { +inline namespace lts_2019_08_08 { +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() 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); + +// Executes specified visitor for each non-retired flag in the registry. +// Requires the caller hold the registry lock. +void ForEachFlagUnlocked(std::function visitor); +// Executes specified visitor for each non-retired flag in the registry. While +// callback are executed, the registry is locked and can't be changed. +void ForEachFlag(std::function visitor); + +//----------------------------------------------------------------------------- + +// Store the list of all flags in *OUTPUT, sorted by file. +void GetAllFlags(std::vector* OUTPUT); + +//----------------------------------------------------------------------------- + +bool RegisterCommandLineFlag(CommandLineFlag*); + +//----------------------------------------------------------------------------- +// Retired registrations: +// +// Retired flag registrations are treated specially. A 'retired' flag is +// provided only for compatibility with automated invocations that still +// name it. A 'retired' flag: +// - is not bound to a C++ FLAGS_ reference. +// - has a type and a value, but that value is intentionally inaccessible. +// - does not appear in --help messages. +// - is fully supported by _all_ flag parsing routines. +// - consumes args normally, and complains about type mismatches in its +// argument. +// - emits a complaint but does not die (e.g. LOG(ERROR)) if it is +// accessed by name through the flags API for parsing or otherwise. +// +// The registrations for a flag happen in an unspecified order as the +// initializers for the namespace-scope objects of a program are run. +// Any number of weak registrations for a flag can weakly define the flag. +// One non-weak registration will upgrade the flag from weak to non-weak. +// Further weak registrations of a non-weak flag are ignored. +// +// This mechanism is designed to support moving dead flags into a +// 'graveyard' library. An example migration: +// +// 0: Remove references to this FLAGS_flagname in the C++ codebase. +// 1: Register as 'retired' in old_lib. +// 2: Make old_lib depend on graveyard. +// 3: Add a redundant 'retired' registration to graveyard. +// 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("myflag"); +// +// Or to declare several at once: +// +// static bool dummies[] = { +// base::RetiredFlag("some_string_flag"), +// base::RetiredFlag("some_double_flag"), +// base::RetiredFlag("some_int32_flag") +// }; + +// Retire flag with name "name" and type indicated by ops. +bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops, + const char* name); + +// Registered a retired flag with name 'flag_name' and type 'T'. +template +inline bool RetiredFlag(const char* flag_name) { + return flags_internal::Retire(flags_internal::FlagOps, + flags_internal::FlagMarshallingOps, + flag_name); +} + +// If the flag is retired, returns true and indicates in |*type_is_bool| +// whether the type of the retired flag is a bool. +// Only to be called by code that needs to explicitly ignore retired flags. +bool IsRetiredFlag(absl::string_view name, bool* type_is_bool); + +//----------------------------------------------------------------------------- +// Saves the states (value, default value, whether the user has set +// the flag, registered validators, etc) of all flags, and restores +// them when the FlagSaver is destroyed. +// +// This class is thread-safe. However, its destructor writes to +// exactly the set of flags that have changed value during its +// lifetime, so concurrent _direct_ access to those flags +// (i.e. FLAGS_foo instead of {Get,Set}CommandLineOption()) is unsafe. + +class FlagSaver { + public: + FlagSaver(); + ~FlagSaver(); + + FlagSaver(const FlagSaver&) = delete; + void operator=(const FlagSaver&) = delete; + + // Prevents saver from restoring the saved state of flags. + void Ignore(); + + private: + class FlagSaverImpl* impl_; // we use pimpl here to keep API steady +}; + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_REGISTRY_H_ diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc new file mode 100644 index 00000000..6b91b261 --- /dev/null +++ b/absl/flags/internal/type_erased.cc @@ -0,0 +1,108 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/type_erased.h" + +#include "absl/base/internal/raw_logging.h" +#include "absl/flags/config.h" +#include "absl/flags/usage_config.h" +#include "absl/strings/str_cat.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +bool GetCommandLineOption(absl::string_view name, std::string* value) { + if (name.empty()) return false; + assert(value); + + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + if (flag == nullptr || flag->IsRetired()) { + return false; + } + + *value = flag->CurrentValue(); + return true; +} + +bool 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); +} + +bool SetCommandLineOptionWithMode(absl::string_view name, + absl::string_view value, + FlagSettingMode set_mode) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + + if (!flag || flag->IsRetired()) return false; + + std::string error; + if (!flag->SetFromString(value, set_mode, kProgrammaticChange, &error)) { + // Errors here are all of the form: the provided name was a recognized + // flag, but the value was invalid (bad type, or validation failed). + flags_internal::ReportUsageError(error, false); + return false; + } + + return true; +} + +// -------------------------------------------------------------------- + +bool IsValidFlagValue(absl::string_view name, absl::string_view value) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + + return flag != nullptr && + (flag->IsRetired() || flag->ValidateInputValue(value)); +} + +// -------------------------------------------------------------------- + +bool SpecifiedOnCommandLine(absl::string_view name) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + if (flag != nullptr && !flag->IsRetired()) { + return flag->IsSpecifiedOnCommandLine(); + } + return false; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/internal/type_erased.h b/absl/flags/internal/type_erased.h new file mode 100644 index 00000000..c9de6de9 --- /dev/null +++ b/absl/flags/internal/type_erased.h @@ -0,0 +1,99 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ +#define ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ + +#include + +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/registry.h" + +// -------------------------------------------------------------------- +// Registry interfaces operating on type erased handles. + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// If a flag named "name" exists, store its current value in *OUTPUT +// and return true. Else return false without changing *OUTPUT. +// Thread-safe. +bool GetCommandLineOption(absl::string_view name, std::string* value); + +// 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. +// Thread-safe. +bool SetCommandLineOption(absl::string_view name, absl::string_view value); + +bool SetCommandLineOptionWithMode(absl::string_view name, + absl::string_view value, + FlagSettingMode set_mode); + +//----------------------------------------------------------------------------- + +// Returns true iff all of the following conditions are true: +// (a) "name" names a registered flag +// (b) "value" can be parsed succesfully according to the type of the flag +// (c) parsed value passes any validator associated with the flag +bool IsValidFlagValue(absl::string_view name, absl::string_view value); + +//----------------------------------------------------------------------------- + +// Returns true iff a flag named "name" was specified on the command line +// (either directly, or via one of --flagfile or --fromenv or --tryfromenv). +// +// Any non-command-line modification of the flag does not affect the +// result of this function. So for example, if a flag was passed on +// the command line but then reset via SET_FLAGS_DEFAULT, this +// function will still return true. +bool SpecifiedOnCommandLine(absl::string_view name); + +//----------------------------------------------------------------------------- + +// If a flag with specified "name" exists and has type T, store +// its current value in *dst and return true. Else return false +// without touching *dst. T must obey all of the requirements for +// types passed to DEFINE_FLAG. +template +inline bool GetByName(absl::string_view name, T* dst) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + if (!flag) return false; + + if (auto val = flag->Get()) { + *dst = *val; + return true; + } + + return false; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // 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 new file mode 100644 index 00000000..ac749a60 --- /dev/null +++ b/absl/flags/internal/type_erased_test.cc @@ -0,0 +1,147 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/type_erased.h" + +#include + +#include "gtest/gtest.h" +#include "absl/flags/flag.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"); +ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help"); + +namespace { + +namespace flags = absl::flags_internal; + +class TypeErasedTest : public testing::Test { + protected: + void SetUp() override { flag_saver_ = absl::make_unique(); } + void TearDown() override { flag_saver_.reset(); } + + private: + std::unique_ptr flag_saver_; +}; + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestGetCommandLineOption) { + std::string value; + EXPECT_TRUE(flags::GetCommandLineOption("int_flag", &value)); + EXPECT_EQ(value, "1"); + + EXPECT_TRUE(flags::GetCommandLineOption("string_flag", &value)); + EXPECT_EQ(value, "dflt"); + + EXPECT_FALSE(flags::GetCommandLineOption("bool_retired_flag", &value)); + + EXPECT_FALSE(flags::GetCommandLineOption("unknown_flag", &value)); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestSetCommandLineOption) { + EXPECT_TRUE(flags::SetCommandLineOption("int_flag", "101")); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); + + EXPECT_TRUE(flags::SetCommandLineOption("string_flag", "asdfgh")); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); + + EXPECT_FALSE(flags::SetCommandLineOption("bool_retired_flag", "true")); + + EXPECT_FALSE(flags::SetCommandLineOption("unknown_flag", "true")); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_VALUE) { + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", + flags::SET_FLAGS_VALUE)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); + + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", + flags::SET_FLAGS_VALUE)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true", + flags::SET_FLAGS_VALUE)); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true", + flags::SET_FLAGS_VALUE)); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAG_IF_DEFAULT) { + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", + flags::SET_FLAG_IF_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); + + // This semantic is broken. We return true instead of false. Value is not + // updated. + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202", + flags::SET_FLAG_IF_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); + + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", + flags::SET_FLAG_IF_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true", + flags::SET_FLAG_IF_DEFAULT)); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true", + flags::SET_FLAG_IF_DEFAULT)); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_DEFAULT) { + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", + flags::SET_FLAGS_DEFAULT)); + + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", + flags::SET_FLAGS_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true", + flags::SET_FLAGS_DEFAULT)); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true", + flags::SET_FLAGS_DEFAULT)); + + // This should be successfull, since flag is still is not set + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202", + flags::SET_FLAG_IF_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 202); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestIsValidFlagValue) { + EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "57")); + EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "-101")); + EXPECT_FALSE(flags::IsValidFlagValue("int_flag", "1.1")); + + EXPECT_TRUE(flags::IsValidFlagValue("string_flag", "#%^#%^$%DGHDG$W%adsf")); + + EXPECT_TRUE(flags::IsValidFlagValue("bool_retired_flag", "true")); +} + +} // namespace diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc new file mode 100644 index 00000000..a9a8a21e --- /dev/null +++ b/absl/flags/internal/usage.cc @@ -0,0 +1,385 @@ +// +// 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/usage.h" + +#include +#include + +#include "absl/flags/flag.h" +#include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/program_name.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 " + "have two dashes]"); +ABSL_FLAG(bool, helpfull, false, "show help on all flags"); +ABSL_FLAG(bool, helpshort, false, + "show help on only the main module for this program"); +ABSL_FLAG(bool, helppackage, false, + "show help on all modules in the main package"); +ABSL_FLAG(bool, version, false, "show version and build info and exit"); +ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags"); +ABSL_FLAG(std::string, helpon, "", + "show help on the modules named by this flag value"); +ABSL_FLAG(std::string, helpmatch, "", + "show help on modules whose name contains the specified substr"); + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { +namespace { + +// 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: +// std::cout << XMLElement("title", "Milk & Cookies"); +// prints "Milk & Cookies" +class XMLElement { + public: + XMLElement(absl::string_view tag, absl::string_view txt) + : tag_(tag), txt_(txt) {} + + friend std::ostream& operator<<(std::ostream& out, + const XMLElement& xml_elem) { + out << "<" << xml_elem.tag_ << ">"; + + for (auto c : xml_elem.txt_) { + switch (c) { + case '"': + out << """; + break; + case '\'': + out << "'"; + break; + case '&': + out << "&"; + break; + case '<': + out << "<"; + break; + case '>': + out << ">"; + break; + default: + out << c; + break; + } + } + + return out << ""; + } + + private: + absl::string_view tag_; + absl::string_view txt_; +}; + +// -------------------------------------------------------------------- +// Helper class to pretty-print info about a flag. + +class FlagHelpPrettyPrinter { + public: + // Pretty printer holds on to the std::ostream& reference to direct an output + // to that stream. + FlagHelpPrettyPrinter(int max_line_len, std::ostream* out) + : out_(*out), + max_line_len_(max_line_len), + line_len_(0), + first_line_(true) {} + + void Write(absl::string_view str, bool wrap_line = false) { + // Empty std::string - do nothing. + if (str.empty()) return; + + std::vector tokens; + if (wrap_line) { + for (auto line : absl::StrSplit(str, absl::ByAnyChar("\n\r"))) { + if (!tokens.empty()) { + // Keep line separators in the input std::string. + tokens.push_back("\n"); + } + for (auto token : + absl::StrSplit(line, absl::ByAnyChar(" \t"), absl::SkipEmpty())) { + tokens.push_back(token); + } + } + } else { + tokens.push_back(str); + } + + for (auto token : tokens) { + bool new_line = (line_len_ == 0); + + // Respect line separators in the input std::string. + if (token == "\n") { + EndLine(); + continue; + } + + // Write the token, ending the std::string first if necessary/possible. + if (!new_line && (line_len_ + token.size() >= max_line_len_)) { + EndLine(); + new_line = true; + } + + if (new_line) { + StartLine(); + } else { + out_ << ' '; + ++line_len_; + } + + out_ << token; + line_len_ += token.size(); + } + } + + void StartLine() { + if (first_line_) { + out_ << " "; + line_len_ = 4; + first_line_ = false; + } else { + out_ << " "; + line_len_ = 6; + } + } + void EndLine() { + out_ << '\n'; + line_len_ = 0; + } + + private: + std::ostream& out_; + const int max_line_len_; + int line_len_; + bool first_line_; +}; + +void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, + std::ostream* out) { + FlagHelpPrettyPrinter printer(80, out); // Max line length is 80. + + // 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(), ";")); + } + + // The listed default value will be the actual default from the flag + // definition in the originating source file, unless the value has + // subsequently been modified using SetCommandLineOption() with mode + // SET_FLAGS_DEFAULT. + std::string dflt_val = flag.DefaultValue(); + if (flag.IsOfType()) { + dflt_val = absl::StrCat("\"", dflt_val, "\""); + } + printer.Write(absl::StrCat("default: ", dflt_val, ";")); + + if (flag.IsModified()) { + std::string curr_val = flag.CurrentValue(); + if (flag.IsOfType()) { + curr_val = absl::StrCat("\"", curr_val, "\""); + } + printer.Write(absl::StrCat("currently: ", curr_val, ";")); + } + + printer.EndLine(); +} + +// Shows help for every filename which matches any of the filters +// If filters are empty, shows help for every file. +// If a flag's help message has been stripped (e.g. by adding '#define +// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help' +// and its variants. +void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, + HelpFormat format, absl::string_view program_usage_message) { + if (format == HelpFormat::kHumanReadable) { + out << flags_internal::ShortProgramInvocationName() << ": " + << program_usage_message << "\n\n"; + } else { + // XML schema is not a part of our public API for now. + out << "\n" + // The document. + << "\n" + // The program name and usage. + << XMLElement("program", flags_internal::ShortProgramInvocationName()) + << '\n' + << XMLElement("usage", program_usage_message) << '\n'; + } + + // Map of package name to + // map of file name to + // vector of flags in the file. + // This map is used to output matching flags grouped by package and file + // name. + std::map>> + matching_flags; + + flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { + std::string flag_filename = flag->Filename(); + + // Ignore retired flags. + if (flag->IsRetired()) return; + + // If the flag has been stripped, pretend that it doesn't exist. + if (flag->Help() == flags_internal::kStrippedFlagHelp) return; + + // Make sure flag satisfies the filter + if (!filter_cb || !filter_cb(flag_filename)) return; + + matching_flags[std::string(flags_internal::Package(flag_filename))] + [flag_filename] + .push_back(flag); + }); + + absl::string_view + package_separator; // controls blank lines between packages. + absl::string_view file_separator; // controls blank lines between files. + for (const auto& package : matching_flags) { + if (format == HelpFormat::kHumanReadable) { + out << package_separator; + package_separator = "\n\n"; + } + + file_separator = ""; + for (const auto& flags_in_file : package.second) { + if (format == HelpFormat::kHumanReadable) { + out << file_separator << " Flags from " << flags_in_file.first + << ":\n"; + file_separator = "\n"; + } + + for (const auto* flag : flags_in_file.second) { + flags_internal::FlagHelp(out, *flag, format); + } + } + } + + if (format == HelpFormat::kHumanReadable) { + if (filter_cb && matching_flags.empty()) { + out << " No modules matched: use -helpfull\n"; + } + } else { + // The end of the document. + out << "\n"; + } +} + +} // namespace + +// -------------------------------------------------------------------- +// Produces the help message describing specific flag. +void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, + HelpFormat format) { + if (format == HelpFormat::kHumanReadable) + flags_internal::FlagHelpHumanReadable(flag, &out); +} + +// -------------------------------------------------------------------- +// Produces the help messages for all flags matching the filter. +// If filter is empty produces help messages for all flags. +void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format, + absl::string_view program_usage_message) { + flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) { + return filter.empty() || filename.find(filter) != absl::string_view::npos; + }; + flags_internal::FlagsHelpImpl(out, filter_cb, format, program_usage_message); +} + +// -------------------------------------------------------------------- +// Checks all the 'usage' command line flags to see if any have been set. +// If so, handles them appropriately. +int HandleUsageFlags(std::ostream& out, + absl::string_view program_usage_message) { + if (absl::GetFlag(FLAGS_helpshort)) { + flags_internal::FlagsHelpImpl( + out, flags_internal::GetUsageConfig().contains_helpshort_flags, + HelpFormat::kHumanReadable, program_usage_message); + return 1; + } + + if (absl::GetFlag(FLAGS_helpfull)) { + // show all options + flags_internal::FlagsHelp(out, "", HelpFormat::kHumanReadable, + program_usage_message); + return 1; + } + + if (!absl::GetFlag(FLAGS_helpon).empty()) { + flags_internal::FlagsHelp( + out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."), + HelpFormat::kHumanReadable, program_usage_message); + return 1; + } + + if (!absl::GetFlag(FLAGS_helpmatch).empty()) { + flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch), + HelpFormat::kHumanReadable, + program_usage_message); + return 1; + } + + if (absl::GetFlag(FLAGS_help)) { + flags_internal::FlagsHelpImpl( + out, flags_internal::GetUsageConfig().contains_help_flags, + HelpFormat::kHumanReadable, program_usage_message); + + out << "\nTry --helpfull to get a list of all flags.\n"; + + return 1; + } + + if (absl::GetFlag(FLAGS_helppackage)) { + flags_internal::FlagsHelpImpl( + out, flags_internal::GetUsageConfig().contains_helppackage_flags, + HelpFormat::kHumanReadable, program_usage_message); + + out << "\nTry --helpfull to get a list of all flags.\n"; + + return 1; + } + + if (absl::GetFlag(FLAGS_version)) { + if (flags_internal::GetUsageConfig().version_string) + out << flags_internal::GetUsageConfig().version_string(); + // Unlike help, we may be asking for version in a script, so return 0 + return 0; + } + + if (absl::GetFlag(FLAGS_only_check_args)) { + return 0; + } + + return -1; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h new file mode 100644 index 00000000..f3794afe --- /dev/null +++ b/absl/flags/internal/usage.h @@ -0,0 +1,80 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_USAGE_H_ +#define ABSL_FLAGS_INTERNAL_USAGE_H_ + +#include +#include + +#include "absl/flags/declare.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Usage reporting interfaces + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// The format to report the help messages in. +enum class HelpFormat { + kHumanReadable, +}; + +// Outputs the help message describing specific flag. +void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, + HelpFormat format = HelpFormat::kHumanReadable); + +// Produces the help messages for all flags matching the filter. A flag matches +// the filter if it is defined in a file with a filename which includes +// filter string as a substring. You can use '/' and '.' to restrict the +// matching to a specific file names. For example: +// FlagsHelp(out, "/path/to/file."); +// restricts help to only flags which resides in files named like: +// .../path/to/file. +// for any extension 'ext'. If the filter is empty this function produces help +// messages for all flags. +void FlagsHelp(std::ostream& out, absl::string_view filter, + HelpFormat format, absl::string_view program_usage_message); + +// -------------------------------------------------------------------- + +// If any of the 'usage' related command line flags (listed on the bottom of +// this file) has been set this routine produces corresponding help message in +// the specified output stream and returns: +// 0 - if "version" or "only_check_flags" flags were set and handled. +// 1 - if some other 'usage' related flag was set and handled. +// -1 - if no usage flags were set on a commmand line. +// Non negative return values are expected to be used as an exit code for a +// binary. +int HandleUsageFlags(std::ostream& out, + absl::string_view program_usage_message); + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +ABSL_DECLARE_FLAG(bool, help); +ABSL_DECLARE_FLAG(bool, helpfull); +ABSL_DECLARE_FLAG(bool, helpshort); +ABSL_DECLARE_FLAG(bool, helppackage); +ABSL_DECLARE_FLAG(bool, version); +ABSL_DECLARE_FLAG(bool, only_check_args); +ABSL_DECLARE_FLAG(std::string, helpon); +ABSL_DECLARE_FLAG(std::string, helpmatch); + +#endif // ABSL_FLAGS_INTERNAL_USAGE_H_ diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc new file mode 100644 index 00000000..781e1d2b --- /dev/null +++ b/absl/flags/internal/usage_test.cc @@ -0,0 +1,404 @@ +// +// 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/usage.h" + +#include + +#include "gtest/gtest.h" +#include "absl/flags/flag.h" +#include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/program_name.h" +#include "absl/flags/parse.h" +#include "absl/flags/usage.h" +#include "absl/flags/usage_config.h" +#include "absl/memory/memory.h" +#include "absl/strings/match.h" + +ABSL_FLAG(int, usage_reporting_test_flag_01, 101, + "usage_reporting_test_flag_01 help message"); +ABSL_FLAG(bool, usage_reporting_test_flag_02, false, + "usage_reporting_test_flag_02 help message"); +ABSL_FLAG(double, usage_reporting_test_flag_03, 1.03, + "usage_reporting_test_flag_03 help message"); +ABSL_FLAG(int64_t, usage_reporting_test_flag_04, 1000000000000004L, + "usage_reporting_test_flag_04 help message"); + +static const char kTestUsageMessage[] = "Custom usage message"; + +struct UDT { + UDT() = default; + UDT(const UDT&) = default; +}; +bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } +std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; } + +ABSL_FLAG(UDT, usage_reporting_test_flag_05, {}, + "usage_reporting_test_flag_05 help message"); + +ABSL_FLAG( + std::string, usage_reporting_test_flag_06, {}, + "usage_reporting_test_flag_06 help message.\n" + "\n" + "Some more help.\n" + "Even more long long long long long long long long long long long long " + "help message."); + +namespace { + +namespace flags = absl::flags_internal; + +static std::string NormalizeFileName(absl::string_view fname) { +#ifdef _WIN32 + std::string normalized(fname); + std::replace(normalized.begin(), normalized.end(), '\\', '/'); + fname = normalized; +#endif + + auto absl_pos = fname.find("/absl/"); + if (absl_pos != absl::string_view::npos) { + fname = fname.substr(absl_pos + 1); + } + return std::string(fname); +} + +class UsageReportingTest : public testing::Test { + protected: + UsageReportingTest() { + // Install default config for the use on this unit test. + // Binary may install a custom config before tests are run. + absl::FlagsUsageConfig default_config; + default_config.normalize_filename = &NormalizeFileName; + absl::SetFlagsUsageConfig(default_config); + } + + private: + flags::FlagSaver flag_saver_; +}; + +// -------------------------------------------------------------------- + +using UsageReportingDeathTest = UsageReportingTest; + +TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) { + EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage); + +#ifndef _WIN32 + // TODO(rogeeff): figure out why this does not work on Windows. + EXPECT_DEATH(absl::SetProgramUsageMessage("custom usage message"), + ".*SetProgramUsageMessage\\(\\) called twice.*"); +#endif +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_01"); + std::stringstream test_buf; + + 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); + default: 101; +)"); +} + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_02"); + std::stringstream test_buf; + + 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); + default: false; +)"); +} + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_03"); + std::stringstream test_buf; + + 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); + default: 1.03; +)"); +} + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_04"); + std::stringstream test_buf; + + 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); + default: 1000000000000004; +)"); +} + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_05"); + std::stringstream test_buf; + + 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); + default: UDT{}; +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestFlagsHelpHRF) { + std::string usage_test_flags_out = + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; +)"; + + std::stringstream test_buf_01; + flags::FlagsHelp(test_buf_01, "usage_test.cc", + flags::HelpFormat::kHumanReadable, kTestUsageMessage); + EXPECT_EQ(test_buf_01.str(), usage_test_flags_out); + + std::stringstream test_buf_02; + flags::FlagsHelp(test_buf_02, "flags/internal/usage_test.cc", + flags::HelpFormat::kHumanReadable, kTestUsageMessage); + EXPECT_EQ(test_buf_02.str(), usage_test_flags_out); + + std::stringstream test_buf_03; + flags::FlagsHelp(test_buf_03, "usage_test", flags::HelpFormat::kHumanReadable, + kTestUsageMessage); + EXPECT_EQ(test_buf_03.str(), usage_test_flags_out); + + std::stringstream test_buf_04; + flags::FlagsHelp(test_buf_04, "flags/invalid_file_name.cc", + flags::HelpFormat::kHumanReadable, kTestUsageMessage); + EXPECT_EQ(test_buf_04.str(), + R"(usage_test: Custom usage message + + No modules matched: use -helpfull +)"); + + std::stringstream test_buf_05; + flags::FlagsHelp(test_buf_05, "", flags::HelpFormat::kHumanReadable, + kTestUsageMessage); + std::string test_out = test_buf_05.str(); + absl::string_view test_out_str(test_out); + EXPECT_TRUE( + absl::StartsWith(test_out_str, "usage_test: Custom usage message")); + EXPECT_TRUE(absl::StrContains( + test_out_str, "Flags from absl/flags/internal/usage_test.cc:")); + EXPECT_TRUE(absl::StrContains(test_out_str, + "Flags from absl/flags/internal/usage.cc:")); + EXPECT_TRUE( + absl::StrContains(test_out_str, "-usage_reporting_test_flag_01 ")); + EXPECT_TRUE(absl::StrContains(test_out_str, "-help (show help")) + << test_out_str; +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestNoUsageFlags) { + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), -1); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_helpshort) { + absl::SetFlag(&FLAGS_helpshort, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); + EXPECT_EQ(test_buf.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_help) { + absl::SetFlag(&FLAGS_help, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); + EXPECT_EQ(test_buf.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; + +Try --helpfull to get a list of all flags. +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_helppackage) { + absl::SetFlag(&FLAGS_helppackage, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); + EXPECT_EQ(test_buf.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; + +Try --helpfull to get a list of all flags. +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_version) { + absl::SetFlag(&FLAGS_version, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0); +#ifndef NDEBUG + EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n"); +#else + EXPECT_EQ(test_buf.str(), "usage_test\n"); +#endif +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) { + absl::SetFlag(&FLAGS_only_check_args, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0); + EXPECT_EQ(test_buf.str(), ""); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_helpon) { + absl::SetFlag(&FLAGS_helpon, "bla-bla"); + + std::stringstream test_buf_01; + EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1); + EXPECT_EQ(test_buf_01.str(), + R"(usage_test: Custom usage message + + No modules matched: use -helpfull +)"); + + absl::SetFlag(&FLAGS_helpon, "usage_test"); + + std::stringstream test_buf_02; + EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1); + EXPECT_EQ(test_buf_02.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; +)"); +} + +// -------------------------------------------------------------------- + +} // namespace + +int main(int argc, char* argv[]) { + absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc + flags::SetProgramInvocationName("usage_test"); + absl::SetProgramUsageMessage(kTestUsageMessage); + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/absl/flags/marshalling.cc b/absl/flags/marshalling.cc new file mode 100644 index 00000000..71b01d77 --- /dev/null +++ b/absl/flags/marshalling.cc @@ -0,0 +1,191 @@ +// +// 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/marshalling.h" + +#include + +#include "absl/base/macros.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" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// -------------------------------------------------------------------- +// AbslParseFlag specializations for boolean type. + +bool AbslParseFlag(absl::string_view text, bool* dst, std::string*) { + const char* kTrue[] = {"1", "t", "true", "y", "yes"}; + const char* kFalse[] = {"0", "f", "false", "n", "no"}; + static_assert(sizeof(kTrue) == sizeof(kFalse), "true_false_equal"); + + text = absl::StripAsciiWhitespace(text); + + for (size_t i = 0; i < ABSL_ARRAYSIZE(kTrue); ++i) { + if (absl::EqualsIgnoreCase(text, kTrue[i])) { + *dst = true; + return true; + } else if (absl::EqualsIgnoreCase(text, kFalse[i])) { + *dst = false; + return true; + } + } + return false; // didn't match a legal input +} + +// -------------------------------------------------------------------- +// AbslParseFlag for integral types. + +// Return the base to use for parsing text as an integer. Leading 0x +// puts us in base 16. But leading 0 does not put us in base 8. It +// caused too many bugs when we had that behavior. +static int NumericBase(absl::string_view text) { + const bool hex = (text.size() >= 2 && text[0] == '0' && + (text[1] == 'x' || text[1] == 'X')); + return hex ? 16 : 10; +} + +template +inline bool ParseFlagImpl(absl::string_view text, IntType* dst) { + text = absl::StripAsciiWhitespace(text); + + return absl::numbers_internal::safe_strtoi_base(text, dst, NumericBase(text)); +} + +bool AbslParseFlag(absl::string_view text, short* dst, std::string*) { + int val; + if (!ParseFlagImpl(text, &val)) return false; + if (static_cast(val) != val) // worked, but number out of range + return false; + *dst = static_cast(val); + return true; +} + +bool AbslParseFlag(absl::string_view text, unsigned short* dst, std::string*) { + unsigned int val; + if (!ParseFlagImpl(text, &val)) return false; + if (static_cast(val) != + val) // worked, but number out of range + return false; + *dst = static_cast(val); + return true; +} + +bool AbslParseFlag(absl::string_view text, int* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, unsigned int* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, long* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, unsigned long* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, long long* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, unsigned long long* dst, + std::string*) { + return ParseFlagImpl(text, dst); +} + +// -------------------------------------------------------------------- +// AbslParseFlag for floating point types. + +bool AbslParseFlag(absl::string_view text, float* dst, std::string*) { + return absl::SimpleAtof(text, dst); +} + +bool AbslParseFlag(absl::string_view text, double* dst, std::string*) { + return absl::SimpleAtod(text, dst); +} + +// -------------------------------------------------------------------- +// AbslParseFlag for strings. + +bool AbslParseFlag(absl::string_view text, std::string* dst, std::string*) { + dst->assign(text.data(), text.size()); + return true; +} + +// -------------------------------------------------------------------- +// AbslParseFlag for vector of strings. + +bool AbslParseFlag(absl::string_view text, std::vector* dst, + std::string*) { + // An empty flag value corresponds to an empty vector, not a vector + // with a single, empty std::string. + if (text.empty()) { + dst->clear(); + return true; + } + *dst = absl::StrSplit(text, ',', absl::AllowEmpty()); + return true; +} + +// -------------------------------------------------------------------- +// AbslUnparseFlag specializations for various builtin flag types. + +std::string Unparse(bool v) { return v ? "true" : "false"; } +std::string Unparse(short v) { return absl::StrCat(v); } +std::string Unparse(unsigned short v) { return absl::StrCat(v); } +std::string Unparse(int v) { return absl::StrCat(v); } +std::string Unparse(unsigned int v) { return absl::StrCat(v); } +std::string Unparse(long v) { return absl::StrCat(v); } +std::string Unparse(unsigned long v) { return absl::StrCat(v); } +std::string Unparse(long long v) { return absl::StrCat(v); } +std::string Unparse(unsigned long long v) { return absl::StrCat(v); } +template +std::string UnparseFloatingPointVal(T v) { + // digits10 is guaranteed to roundtrip correctly in std::string -> value -> std::string + // conversions, but may not be enough to represent all the values correctly. + std::string digit10_str = + absl::StrFormat("%.*g", std::numeric_limits::digits10, v); + if (std::isnan(v) || std::isinf(v)) return digit10_str; + + T roundtrip_val = 0; + std::string err; + if (absl::ParseFlag(digit10_str, &roundtrip_val, &err) && + roundtrip_val == v) { + return digit10_str; + } + + // max_digits10 is the number of base-10 digits that are necessary to uniquely + // represent all distinct values. + return absl::StrFormat("%.*g", std::numeric_limits::max_digits10, v); +} +std::string Unparse(float v) { return UnparseFloatingPointVal(v); } +std::string Unparse(double v) { return UnparseFloatingPointVal(v); } +std::string AbslUnparseFlag(absl::string_view v) { return std::string(v); } +std::string AbslUnparseFlag(const std::vector& v) { + return absl::StrJoin(v, ","); +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h new file mode 100644 index 00000000..5598d444 --- /dev/null +++ b/absl/flags/marshalling.h @@ -0,0 +1,255 @@ +// +// 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: marshalling.h +// ----------------------------------------------------------------------------- +// +// This header file defines the API for extending Abseil flag support to +// custom types, and defines the set of overloads for fundamental types. +// +// Out of the box, the Abseil flags library supports the following types: +// +// * `bool` +// * `int16_t` +// * `uint16_t` +// * `int32_t` +// * `uint32_t` +// * `int64_t` +// * `uint64_t` +// * `float` +// * `double` +// * `std::string` +// * `std::vector` +// +// 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. +// +// The Abseil time library provides the following support for civil time values: +// +// * `absl::CivilSecond` +// * `absl::CivilMinute` +// * `absl::CivilHour` +// * `absl::CivilDay` +// * `absl::CivilMonth` +// * `absl::CivilYear` +// +// and also provides support for the following absolute time values: +// +// * `absl::Duration` +// * `absl::Time` +// +// Additional support for Abseil types will be noted here as it is added. +// +// You can also provide your own custom flags by adding overloads for +// `AbslParseFlag()` and `AbslUnparseFlag()` to your type definitions. (See +// below.) +// +// ----------------------------------------------------------------------------- +// Adding Type Support for Abseil Flags +// ----------------------------------------------------------------------------- +// +// To add support for your user-defined type, add overloads of `AbslParseFlag()` +// and `AbslUnparseFlag()` as free (non-member) functions to your type. If `T` +// is a class type, these functions can be friend function definitions. These +// overloads must be added to the same namespace where the type is defined, so +// that they can be discovered by Argument-Dependent Lookup (ADL). +// +// Example: +// +// namespace foo { +// +// enum OutputMode { kPlainText, kHtml }; +// +// // AbslParseFlag converts from a string to OutputMode. +// // Must be in same namespace as OutputMode. +// +// // Parses an OutputMode from the command line flag value `text. Returns +// // `true` and sets `*mode` on success; returns `false` and sets `*error` +// // on failure. +// bool AbslParseFlag(absl::string_view text, +// OutputMode* mode, +// std::string* error) { +// if (text == "plaintext") { +// *mode = kPlainText; +// return true; +// } +// if (text == "html") { +// *mode = kHtml; +// return true; +// } +// *error = "unknown value for enumeration"; +// return false; +// } +// +// // AbslUnparseFlag converts from an OutputMode to a string. +// // Must be in same namespace as OutputMode. +// +// // Returns a textual flag value corresponding to the OutputMode `mode`. +// std::string AbslUnparseFlag(OutputMode mode) { +// switch (mode) { +// case kPlainText: return "plaintext"; +// case kHtml: return "html"; +// } +// return absl::StrCat(mode); +// } +// +// Notice that neither `AbslParseFlag()` nor `AbslUnparseFlag()` are class +// members, but free functions. `AbslParseFlag/AbslUnparseFlag()` overloads +// for a type should only be declared in the same file and namespace as said +// type. The proper `AbslParseFlag/AbslUnparseFlag()` implementations for a +// given type will be discovered via Argument-Dependent Lookup (ADL). +// +// `AbslParseFlag()` may need, in turn, to parse simpler constituent types +// using `absl::ParseFlag()`. For example, a custom struct `MyFlagType` +// consisting of a `std::pair` would add an `AbslParseFlag()` +// overload for its `MyFlagType` like so: +// +// Example: +// +// namespace my_flag_type { +// +// struct MyFlagType { +// std::pair my_flag_data; +// }; +// +// bool AbslParseFlag(absl::string_view text, MyFlagType* flag, +// std::string* err); +// +// std::string AbslUnparseFlag(const MyFlagType&); +// +// // Within the implementation, `AbslParseFlag()` will, in turn invoke +// // `absl::ParseFlag()` on its constituent `int` and `std::string` types +// // (which have built-in Abseil flag support. +// +// bool AbslParseFlag(absl::string_view text, MyFlagType* flag, +// std::string* err) { +// std::pair tokens = +// absl::StrSplit(text, ','); +// if (!absl::ParseFlag(tokens.first, &flag->my_flag_data.first, err)) +// return false; +// if (!absl::ParseFlag(tokens.second, &flag->my_flag_data.second, err)) +// return false; +// return true; +// } +// +// // Similarly, for unparsing, we can simply invoke `absl::UnparseFlag()` on +// // the constituent types. +// std::string AbslUnparseFlag(const MyFlagType& flag) { +// return absl::StrCat(absl::UnparseFlag(flag.my_flag_data.first), +// ",", +// absl::UnparseFlag(flag.my_flag_data.second)); +// } +#ifndef ABSL_FLAGS_MARSHALLING_H_ +#define ABSL_FLAGS_MARSHALLING_H_ + +#include +#include + +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types. +bool AbslParseFlag(absl::string_view, bool*, std::string*); +bool AbslParseFlag(absl::string_view, short*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned short*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, int*, std::string*); // NOLINT +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, float*, std::string*); +bool AbslParseFlag(absl::string_view, double*, std::string*); +bool AbslParseFlag(absl::string_view, std::string*, std::string*); +bool AbslParseFlag(absl::string_view, std::vector*, std::string*); + +template +bool InvokeParseFlag(absl::string_view input, T* dst, std::string* err) { + // Comment on next line provides a good compiler error message if T + // does not have AbslParseFlag(absl::string_view, T*, std::string*). + return AbslParseFlag(input, dst, err); // Is T missing AbslParseFlag? +} + +// Strings and std:: containers do not have the same overload resolution +// considerations as fundamental types. Naming these 'AbslUnparseFlag' means we +// can avoid the need for additional specializations of Unparse (below). +std::string AbslUnparseFlag(absl::string_view v); +std::string AbslUnparseFlag(const std::vector&); + +template +std::string Unparse(const T& v) { + // Comment on next line provides a good compiler error message if T does not + // have UnparseFlag. + return AbslUnparseFlag(v); // Is T missing AbslUnparseFlag? +} + +// Overloads for builtin types. +std::string Unparse(bool v); +std::string Unparse(short v); // NOLINT +std::string Unparse(unsigned short v); // NOLINT +std::string Unparse(int v); // NOLINT +std::string Unparse(unsigned int v); // NOLINT +std::string Unparse(long v); // NOLINT +std::string Unparse(unsigned long v); // NOLINT +std::string Unparse(long long v); // NOLINT +std::string Unparse(unsigned long long v); // NOLINT +std::string Unparse(float v); +std::string Unparse(double v); + +} // namespace flags_internal + +// ParseFlag() +// +// Parses a string value into a flag value of type `T`. Do not add overloads of +// this function for your type directly; instead, add an `AbslParseFlag()` +// free function as documented above. +// +// Some implementations of `AbslParseFlag()` for types which consist of other, +// constituent types which already have Abseil flag support, may need to call +// `absl::ParseFlag()` on those consituent string values. (See above.) +template +inline bool ParseFlag(absl::string_view input, T* dst, std::string* error) { + return flags_internal::InvokeParseFlag(input, dst, error); +} + +// UnparseFlag() +// +// Unparses a flag value of type `T` into a string value. Do not add overloads +// of this function for your type directly; instead, add an `AbslUnparseFlag()` +// free function as documented above. +// +// Some implementations of `AbslUnparseFlag()` for types which consist of other, +// constituent types which already have Abseil flag support, may want to call +// `absl::UnparseFlag()` on those constituent types. (See above.) +template +inline std::string UnparseFlag(const T& v) { + return flags_internal::Unparse(v); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_MARSHALLING_H_ diff --git a/absl/flags/marshalling_test.cc b/absl/flags/marshalling_test.cc new file mode 100644 index 00000000..37cd1940 --- /dev/null +++ b/absl/flags/marshalling_test.cc @@ -0,0 +1,899 @@ +// +// 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/marshalling.h" + +#include + +#include "gtest/gtest.h" + +namespace { + +TEST(MarshallingTest, TestBoolParsing) { + std::string err; + bool value; + + // True values. + EXPECT_TRUE(absl::ParseFlag("True", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("true", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("TRUE", &value, &err)); + EXPECT_TRUE(value); + + EXPECT_TRUE(absl::ParseFlag("Yes", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("yes", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("YES", &value, &err)); + EXPECT_TRUE(value); + + EXPECT_TRUE(absl::ParseFlag("t", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("T", &value, &err)); + EXPECT_TRUE(value); + + EXPECT_TRUE(absl::ParseFlag("y", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("Y", &value, &err)); + EXPECT_TRUE(value); + + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_TRUE(value); + + // False values. + EXPECT_TRUE(absl::ParseFlag("False", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("false", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("FALSE", &value, &err)); + EXPECT_FALSE(value); + + EXPECT_TRUE(absl::ParseFlag("No", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("no", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("NO", &value, &err)); + EXPECT_FALSE(value); + + EXPECT_TRUE(absl::ParseFlag("f", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("F", &value, &err)); + EXPECT_FALSE(value); + + EXPECT_TRUE(absl::ParseFlag("n", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("N", &value, &err)); + EXPECT_FALSE(value); + + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_FALSE(value); + + // Whitespace handling. + EXPECT_TRUE(absl::ParseFlag(" true", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("true ", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag(" true ", &value, &err)); + EXPECT_TRUE(value); + + // Invalid input. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("11", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("tt", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt16Parsing) { + std::string err; + int16_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("-1", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("-18765", &value, &err)); + EXPECT_EQ(value, -18765); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("-001", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err)); + EXPECT_EQ(value, 564); + // TODO(rogeeff): fix below validations + EXPECT_FALSE(absl::ParseFlag("-0x7FFD", &value, &err)); + EXPECT_NE(value, -3); + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x22 ", &value, &err)); + EXPECT_EQ(value, 34); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("40000", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint16Parsing) { + std::string err; + uint16_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("001", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err)); + EXPECT_EQ(value, 564); + // TODO(rogeeff): fix below validations + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x22 ", &value, &err)); + EXPECT_EQ(value, 34); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("70000", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("-1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt32Parsing) { + std::string err; + int32_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("-1", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("-98765", &value, &err)); + EXPECT_EQ(value, -98765); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("-001", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err)); + EXPECT_EQ(value, 564); + // TODO(rogeeff): fix below validations + EXPECT_FALSE(absl::ParseFlag("-0x7FFFFFFD", &value, &err)); + EXPECT_NE(value, -3); + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x22 ", &value, &err)); + EXPECT_EQ(value, 34); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("70000000000", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint32Parsing) { + std::string err; + uint32_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err)); + EXPECT_EQ(value, 564); + EXPECT_TRUE(absl::ParseFlag("0xFFFFFFFD", &value, &err)); + EXPECT_EQ(value, 4294967293); + // TODO(rogeeff): fix below validations + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x22 ", &value, &err)); + EXPECT_EQ(value, 34); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("140000000000", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("-1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt64Parsing) { + std::string err; + int64_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("-1", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("-98765", &value, &err)); + EXPECT_EQ(value, -98765); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("001", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0XFFFAAABBBCCCDDD", &value, &err)); + EXPECT_EQ(value, 1152827684197027293); + // TODO(rogeeff): fix below validation + EXPECT_FALSE(absl::ParseFlag("-0x7FFFFFFFFFFFFFFE", &value, &err)); + EXPECT_NE(value, -2); + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x7F ", &value, &err)); + EXPECT_EQ(value, 127); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("0xFFFFFFFFFFFFFFFFFF", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUInt64Parsing) { + std::string err; + uint64_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("+13", &value, &err)); + EXPECT_EQ(value, 13); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("001", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0000300", &value, &err)); + EXPECT_EQ(value, 300); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0XFFFF", &value, &err)); + EXPECT_EQ(value, 65535); + // TODO(rogeeff): fix below validation + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("0xFFFFFFFFFFFFFFFFFF", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("-1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestFloatParsing) { + std::string err; + float value; + + // Ordinary values. + EXPECT_TRUE(absl::ParseFlag("1.3", &value, &err)); + EXPECT_FLOAT_EQ(value, 1.3f); + EXPECT_TRUE(absl::ParseFlag("-0.1", &value, &err)); + EXPECT_DOUBLE_EQ(value, -0.1f); + EXPECT_TRUE(absl::ParseFlag("+0.01", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.01f); + + // Scientific values. + EXPECT_TRUE(absl::ParseFlag("1.2e3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.2e3f); + EXPECT_TRUE(absl::ParseFlag("9.8765402e-37", &value, &err)); + EXPECT_DOUBLE_EQ(value, 9.8765402e-37f); + EXPECT_TRUE(absl::ParseFlag("0.11e+3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.11e+3f); + EXPECT_TRUE(absl::ParseFlag("1.e-2300", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.f); + EXPECT_TRUE(absl::ParseFlag("1.e+2300", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01.6", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.6f); + EXPECT_TRUE(absl::ParseFlag("000.0001", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.0001f); + + // Trailing zero values. + EXPECT_TRUE(absl::ParseFlag("-5.1000", &value, &err)); + EXPECT_DOUBLE_EQ(value, -5.1f); + + // Exceptional values. + EXPECT_TRUE(absl::ParseFlag("NaN", &value, &err)); + EXPECT_TRUE(std::isnan(value)); + EXPECT_TRUE(absl::ParseFlag("Inf", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + + // Hex values + EXPECT_TRUE(absl::ParseFlag("0x10.23p12", &value, &err)); + EXPECT_DOUBLE_EQ(value, 66096.f); + EXPECT_TRUE(absl::ParseFlag("-0xF1.A3p-2", &value, &err)); + EXPECT_NEAR(value, -60.4092f, 5e-5f); + EXPECT_TRUE(absl::ParseFlag("+0x0.0AAp-12", &value, &err)); + EXPECT_NEAR(value, 1.01328e-05f, 5e-11f); + EXPECT_TRUE(absl::ParseFlag("0x.01p1", &value, &err)); + EXPECT_NEAR(value, 0.0078125f, 5e-8f); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10.1 ", &value, &err)); + EXPECT_DOUBLE_EQ(value, 10.1f); + EXPECT_TRUE(absl::ParseFlag(" 2.34", &value, &err)); + EXPECT_DOUBLE_EQ(value, 2.34f); + EXPECT_TRUE(absl::ParseFlag(" 5.7 ", &value, &err)); + EXPECT_DOUBLE_EQ(value, 5.7f); + EXPECT_TRUE(absl::ParseFlag(" -0xE0.F3p01 ", &value, &err)); + EXPECT_NEAR(value, -449.8984375f, 5e-8f); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2.3xxx", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("0x0.1pAA", &value, &err)); + // TODO(rogeeff): below assertion should fail + EXPECT_TRUE(absl::ParseFlag("0x0.1", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestDoubleParsing) { + std::string err; + double value; + + // Ordinary values. + EXPECT_TRUE(absl::ParseFlag("1.3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.3); + EXPECT_TRUE(absl::ParseFlag("-0.1", &value, &err)); + EXPECT_DOUBLE_EQ(value, -0.1); + EXPECT_TRUE(absl::ParseFlag("+0.01", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.01); + + // Scientific values. + EXPECT_TRUE(absl::ParseFlag("1.2e3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.2e3); + EXPECT_TRUE(absl::ParseFlag("9.00000002e-123", &value, &err)); + EXPECT_DOUBLE_EQ(value, 9.00000002e-123); + EXPECT_TRUE(absl::ParseFlag("0.11e+3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.11e+3); + EXPECT_TRUE(absl::ParseFlag("1.e-2300", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("1.e+2300", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01.6", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.6); + EXPECT_TRUE(absl::ParseFlag("000.0001", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.0001); + + // Trailing zero values. + EXPECT_TRUE(absl::ParseFlag("-5.1000", &value, &err)); + EXPECT_DOUBLE_EQ(value, -5.1); + + // Exceptional values. + EXPECT_TRUE(absl::ParseFlag("NaN", &value, &err)); + EXPECT_TRUE(std::isnan(value)); + EXPECT_TRUE(absl::ParseFlag("nan", &value, &err)); + EXPECT_TRUE(std::isnan(value)); + EXPECT_TRUE(absl::ParseFlag("Inf", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + EXPECT_TRUE(absl::ParseFlag("inf", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + + // Hex values + EXPECT_TRUE(absl::ParseFlag("0x10.23p12", &value, &err)); + EXPECT_DOUBLE_EQ(value, 66096); + EXPECT_TRUE(absl::ParseFlag("-0xF1.A3p-2", &value, &err)); + EXPECT_NEAR(value, -60.4092, 5e-5); + EXPECT_TRUE(absl::ParseFlag("+0x0.0AAp-12", &value, &err)); + EXPECT_NEAR(value, 1.01328e-05, 5e-11); + EXPECT_TRUE(absl::ParseFlag("0x.01p1", &value, &err)); + EXPECT_NEAR(value, 0.0078125, 5e-8); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10.1 ", &value, &err)); + EXPECT_DOUBLE_EQ(value, 10.1); + EXPECT_TRUE(absl::ParseFlag(" 2.34", &value, &err)); + EXPECT_DOUBLE_EQ(value, 2.34); + EXPECT_TRUE(absl::ParseFlag(" 5.7 ", &value, &err)); + EXPECT_DOUBLE_EQ(value, 5.7); + EXPECT_TRUE(absl::ParseFlag(" -0xE0.F3p01 ", &value, &err)); + EXPECT_NEAR(value, -449.8984375, 5e-8); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2.3xxx", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("0x0.1pAA", &value, &err)); + // TODO(rogeeff): below assertion should fail + EXPECT_TRUE(absl::ParseFlag("0x0.1", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestStringParsing) { + std::string err; + std::string value; + + EXPECT_TRUE(absl::ParseFlag("", &value, &err)); + EXPECT_EQ(value, ""); + EXPECT_TRUE(absl::ParseFlag(" ", &value, &err)); + EXPECT_EQ(value, " "); + EXPECT_TRUE(absl::ParseFlag(" ", &value, &err)); + EXPECT_EQ(value, " "); + EXPECT_TRUE(absl::ParseFlag("\n", &value, &err)); + EXPECT_EQ(value, "\n"); + EXPECT_TRUE(absl::ParseFlag("\t", &value, &err)); + EXPECT_EQ(value, "\t"); + EXPECT_TRUE(absl::ParseFlag("asdfg", &value, &err)); + EXPECT_EQ(value, "asdfg"); + EXPECT_TRUE(absl::ParseFlag("asdf ghjk", &value, &err)); + EXPECT_EQ(value, "asdf ghjk"); + EXPECT_TRUE(absl::ParseFlag("a\nb\nc", &value, &err)); + EXPECT_EQ(value, "a\nb\nc"); + EXPECT_TRUE(absl::ParseFlag("asd\0fgh", &value, &err)); + EXPECT_EQ(value, "asd"); + EXPECT_TRUE(absl::ParseFlag("\\\\", &value, &err)); + EXPECT_EQ(value, "\\\\"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestVectorOfStringParsing) { + std::string err; + std::vector value; + + EXPECT_TRUE(absl::ParseFlag("", &value, &err)); + EXPECT_EQ(value, std::vector{}); + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, std::vector({"1"})); + EXPECT_TRUE(absl::ParseFlag("a,b", &value, &err)); + EXPECT_EQ(value, std::vector({"a", "b"})); + EXPECT_TRUE(absl::ParseFlag("a,b,c,", &value, &err)); + EXPECT_EQ(value, std::vector({"a", "b", "c", ""})); + EXPECT_TRUE(absl::ParseFlag("a,,", &value, &err)); + EXPECT_EQ(value, std::vector({"a", "", ""})); + EXPECT_TRUE(absl::ParseFlag(",", &value, &err)); + EXPECT_EQ(value, std::vector({"", ""})); + EXPECT_TRUE(absl::ParseFlag("a, b,c ", &value, &err)); + EXPECT_EQ(value, std::vector({"a", " b", "c "})); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestBoolUnparsing) { + EXPECT_EQ(absl::UnparseFlag(true), "true"); + EXPECT_EQ(absl::UnparseFlag(false), "false"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt16Unparsing) { + int16_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = -1; + EXPECT_EQ(absl::UnparseFlag(value), "-1"); + value = 9876; + EXPECT_EQ(absl::UnparseFlag(value), "9876"); + value = -987; + EXPECT_EQ(absl::UnparseFlag(value), "-987"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint16Unparsing) { + uint16_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = 19876; + EXPECT_EQ(absl::UnparseFlag(value), "19876"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt32Unparsing) { + int32_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = -1; + EXPECT_EQ(absl::UnparseFlag(value), "-1"); + value = 12345; + EXPECT_EQ(absl::UnparseFlag(value), "12345"); + value = -987; + EXPECT_EQ(absl::UnparseFlag(value), "-987"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint32Unparsing) { + uint32_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = 1234500; + EXPECT_EQ(absl::UnparseFlag(value), "1234500"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt64Unparsing) { + int64_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = -1; + EXPECT_EQ(absl::UnparseFlag(value), "-1"); + value = 123456789L; + EXPECT_EQ(absl::UnparseFlag(value), "123456789"); + value = -987654321L; + EXPECT_EQ(absl::UnparseFlag(value), "-987654321"); + value = 0x7FFFFFFFFFFFFFFF; + EXPECT_EQ(absl::UnparseFlag(value), "9223372036854775807"); + value = 0xFFFFFFFFFFFFFFFF; + EXPECT_EQ(absl::UnparseFlag(value), "-1"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint64Unparsing) { + uint64_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = 123456789L; + EXPECT_EQ(absl::UnparseFlag(value), "123456789"); + value = 0xFFFFFFFFFFFFFFFF; + EXPECT_EQ(absl::UnparseFlag(value), "18446744073709551615"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestFloatUnparsing) { + float value; + + value = 1.1f; + EXPECT_EQ(absl::UnparseFlag(value), "1.1"); + value = 0.01f; + EXPECT_EQ(absl::UnparseFlag(value), "0.01"); + value = 1.23e-2f; + EXPECT_EQ(absl::UnparseFlag(value), "0.0123"); + value = -0.71f; + EXPECT_EQ(absl::UnparseFlag(value), "-0.71"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestDoubleUnparsing) { + double value; + + value = 1.1; + EXPECT_EQ(absl::UnparseFlag(value), "1.1"); + value = 0.01; + EXPECT_EQ(absl::UnparseFlag(value), "0.01"); + value = 1.23e-2; + EXPECT_EQ(absl::UnparseFlag(value), "0.0123"); + value = -0.71; + EXPECT_EQ(absl::UnparseFlag(value), "-0.71"); + value = -0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = std::nan(""); + EXPECT_EQ(absl::UnparseFlag(value), "nan"); + value = std::numeric_limits::infinity(); + EXPECT_EQ(absl::UnparseFlag(value), "inf"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestStringUnparsing) { + EXPECT_EQ(absl::UnparseFlag(""), ""); + EXPECT_EQ(absl::UnparseFlag(" "), " "); + EXPECT_EQ(absl::UnparseFlag("qwerty"), "qwerty"); + EXPECT_EQ(absl::UnparseFlag("ASDFGH"), "ASDFGH"); + EXPECT_EQ(absl::UnparseFlag("\n\t "), "\n\t "); +} + +// -------------------------------------------------------------------- + +template +void TestRoundtrip(T v) { + T new_v; + std::string err; + EXPECT_TRUE(absl::ParseFlag(absl::UnparseFlag(v), &new_v, &err)); + EXPECT_EQ(new_v, v); +} + +TEST(MarshallingTest, TestFloatRoundTrip) { + TestRoundtrip(0.1f); + TestRoundtrip(0.12f); + TestRoundtrip(0.123f); + TestRoundtrip(0.1234f); + TestRoundtrip(0.12345f); + TestRoundtrip(0.123456f); + TestRoundtrip(0.1234567f); + TestRoundtrip(0.12345678f); + + TestRoundtrip(0.1e20f); + TestRoundtrip(0.12e20f); + TestRoundtrip(0.123e20f); + TestRoundtrip(0.1234e20f); + TestRoundtrip(0.12345e20f); + TestRoundtrip(0.123456e20f); + TestRoundtrip(0.1234567e20f); + TestRoundtrip(0.12345678e20f); + + TestRoundtrip(0.1e-20f); + TestRoundtrip(0.12e-20f); + TestRoundtrip(0.123e-20f); + TestRoundtrip(0.1234e-20f); + TestRoundtrip(0.12345e-20f); + TestRoundtrip(0.123456e-20f); + TestRoundtrip(0.1234567e-20f); + TestRoundtrip(0.12345678e-20f); +} + +TEST(MarshallingTest, TestDoubleRoundTrip) { + TestRoundtrip(0.1); + TestRoundtrip(0.12); + TestRoundtrip(0.123); + TestRoundtrip(0.1234); + TestRoundtrip(0.12345); + TestRoundtrip(0.123456); + TestRoundtrip(0.1234567); + TestRoundtrip(0.12345678); + TestRoundtrip(0.123456789); + TestRoundtrip(0.1234567891); + TestRoundtrip(0.12345678912); + TestRoundtrip(0.123456789123); + TestRoundtrip(0.1234567891234); + TestRoundtrip(0.12345678912345); + TestRoundtrip(0.123456789123456); + TestRoundtrip(0.1234567891234567); + TestRoundtrip(0.12345678912345678); + + TestRoundtrip(0.1e50); + TestRoundtrip(0.12e50); + TestRoundtrip(0.123e50); + TestRoundtrip(0.1234e50); + TestRoundtrip(0.12345e50); + TestRoundtrip(0.123456e50); + TestRoundtrip(0.1234567e50); + TestRoundtrip(0.12345678e50); + TestRoundtrip(0.123456789e50); + TestRoundtrip(0.1234567891e50); + TestRoundtrip(0.12345678912e50); + TestRoundtrip(0.123456789123e50); + TestRoundtrip(0.1234567891234e50); + TestRoundtrip(0.12345678912345e50); + TestRoundtrip(0.123456789123456e50); + TestRoundtrip(0.1234567891234567e50); + TestRoundtrip(0.12345678912345678e50); + + TestRoundtrip(0.1e-50); + TestRoundtrip(0.12e-50); + TestRoundtrip(0.123e-50); + TestRoundtrip(0.1234e-50); + TestRoundtrip(0.12345e-50); + TestRoundtrip(0.123456e-50); + TestRoundtrip(0.1234567e-50); + TestRoundtrip(0.12345678e-50); + TestRoundtrip(0.123456789e-50); + TestRoundtrip(0.1234567891e-50); + TestRoundtrip(0.12345678912e-50); + TestRoundtrip(0.123456789123e-50); + TestRoundtrip(0.1234567891234e-50); + TestRoundtrip(0.12345678912345e-50); + TestRoundtrip(0.123456789123456e-50); + TestRoundtrip(0.1234567891234567e-50); + TestRoundtrip(0.12345678912345678e-50); +} + +} // namespace diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc new file mode 100644 index 00000000..fd80a0c8 --- /dev/null +++ b/absl/flags/parse.cc @@ -0,0 +1,755 @@ +// +// 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/parse.h" + +#include + +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +#include "absl/flags/flag.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/str_cat.h" +#include "absl/strings/strip.h" +#include "absl/synchronization/mutex.h" + +// -------------------------------------------------------------------- + +namespace absl { +inline namespace lts_2019_08_08 { +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_CONST_INIT bool fromenv_needs_processing + GUARDED_BY(processing_checks_guard) = false; +ABSL_CONST_INIT bool tryfromenv_needs_processing + GUARDED_BY(processing_checks_guard) = false; + +} // namespace +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +ABSL_FLAG(std::vector, flagfile, {}, + "comma-separated list of files to load flags from") + .OnUpdate([]() { + if (absl::GetFlag(FLAGS_flagfile).empty()) return; + + absl::MutexLock l(&absl::flags_internal::processing_checks_guard); + + // Setting this flag twice before it is handled most likely an internal + // error and should be reviewed by developers. + if (absl::flags_internal::flagfile_needs_processing) { + ABSL_INTERNAL_LOG(WARNING, "flagfile set twice before it is handled"); + } + + absl::flags_internal::flagfile_needs_processing = true; + }); +ABSL_FLAG(std::vector, fromenv, {}, + "comma-separated list of flags to set from the environment" + " [use 'export FLAGS_flag1=value']") + .OnUpdate([]() { + if (absl::GetFlag(FLAGS_fromenv).empty()) return; + + absl::MutexLock l(&absl::flags_internal::processing_checks_guard); + + // Setting this flag twice before it is handled most likely an internal + // error and should be reviewed by developers. + if (absl::flags_internal::fromenv_needs_processing) { + ABSL_INTERNAL_LOG(WARNING, "fromenv set twice before it is handled."); + } + + absl::flags_internal::fromenv_needs_processing = true; + }); +ABSL_FLAG(std::vector, tryfromenv, {}, + "comma-separated list of flags to try to set from the environment if " + "present") + .OnUpdate([]() { + if (absl::GetFlag(FLAGS_tryfromenv).empty()) return; + + absl::MutexLock l(&absl::flags_internal::processing_checks_guard); + + // Setting this flag twice before it is handled most likely an internal + // error and should be reviewed by developers. + if (absl::flags_internal::tryfromenv_needs_processing) { + ABSL_INTERNAL_LOG(WARNING, + "tryfromenv set twice before it is handled."); + } + + absl::flags_internal::tryfromenv_needs_processing = true; + }); + +ABSL_FLAG(std::vector, undefok, {}, + "comma-separated list of flag names that it is okay to specify " + "on the command line even if the program does not define a flag " + "with that name"); + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +namespace { + +class ArgsList { + public: + ArgsList() : next_arg_(0) {} + ArgsList(int argc, char* argv[]) : args_(argv, argv + argc), next_arg_(0) {} + explicit ArgsList(const std::vector& args) + : args_(args), next_arg_(0) {} + + // Returns success status: true if parsing successful, false otherwise. + bool ReadFromFlagfile(const std::string& flag_file_name); + + int Size() const { return args_.size() - next_arg_; } + int FrontIndex() const { return next_arg_; } + absl::string_view Front() const { return args_[next_arg_]; } + void PopFront() { next_arg_++; } + + private: + std::vector args_; + int next_arg_; +}; + +bool ArgsList::ReadFromFlagfile(const std::string& flag_file_name) { + std::ifstream flag_file(flag_file_name); + + if (!flag_file) { + flags_internal::ReportUsageError( + absl::StrCat("Can't open flagfile ", flag_file_name), true); + + return false; + } + + // This argument represents fake argv[0], which should be present in all arg + // lists. + args_.push_back(""); + + std::string line; + bool success = true; + + while (std::getline(flag_file, line)) { + absl::string_view stripped = absl::StripLeadingAsciiWhitespace(line); + + if (stripped.empty() || stripped[0] == '#') { + // Comment or empty line; just ignore. + continue; + } + + if (stripped[0] == '-') { + if (stripped == "--") { + flags_internal::ReportUsageError( + "Flagfile can't contain position arguments or --", true); + + success = false; + break; + } + + args_.push_back(std::string(stripped)); + continue; + } + + flags_internal::ReportUsageError( + absl::StrCat("Unexpected line in the flagfile ", flag_file_name, ": ", + line), + true); + + success = false; + } + + return success; +} + +// -------------------------------------------------------------------- + +// Reads the environment variable with name `name` and stores results in +// `value`. If variable is not present in environment returns false, otherwise +// returns true. +bool GetEnvVar(const char* var_name, std::string* var_value) { +#ifdef _WIN32 + char buf[1024]; + auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf)); + if (get_res >= sizeof(buf)) { + return false; + } + + if (get_res == 0) { + return false; + } + + *var_value = std::string(buf, get_res); +#else + const char* val = ::getenv(var_name); + if (val == nullptr) { + return false; + } + + *var_value = val; +#endif + + return true; +} + +// -------------------------------------------------------------------- + +// Returns: +// Flag name or empty if arg= -- +// Flag value after = in --flag=value (empty if --foo) +// "Is empty value" status. True if arg= --foo=, false otherwise. This is +// required to separate --foo from --foo=. +// For example: +// arg return values +// "--foo=bar" -> {"foo", "bar", false}. +// "--foo" -> {"foo", "", false}. +// "--foo=" -> {"foo", "", true}. +std::tuple SplitNameAndValue( + absl::string_view arg) { + // Allow -foo and --foo + absl::ConsumePrefix(&arg, "-"); + + if (arg.empty()) { + return std::make_tuple("", "", false); + } + + auto equal_sign_pos = arg.find("="); + + absl::string_view flag_name = arg.substr(0, equal_sign_pos); + + absl::string_view value; + bool is_empty_value = false; + + if (equal_sign_pos != absl::string_view::npos) { + value = arg.substr(equal_sign_pos + 1); + is_empty_value = value.empty(); + } + + return std::make_tuple(flag_name, value, is_empty_value); +} + +// -------------------------------------------------------------------- + +// Returns: +// found flag or nullptr +// is negative in case of --nofoo +std::tuple LocateFlag(absl::string_view flag_name) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(flag_name); + bool is_negative = false; + + if (!flag && absl::ConsumePrefix(&flag_name, "no")) { + flag = flags_internal::FindCommandLineFlag(flag_name); + is_negative = true; + } + + return std::make_tuple(flag, is_negative); +} + +// -------------------------------------------------------------------- + +// Verify that default values of typed flags must be convertible to string and +// back. +void CheckDefaultValuesParsingRoundtrip() { +#ifndef NDEBUG + flags_internal::ForEachFlag([&](CommandLineFlag* flag) { + if (flag->IsRetired()) return; + +#define IGNORE_TYPE(T) \ + if (flag->IsOfType()) return; + + ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(IGNORE_TYPE) + IGNORE_TYPE(std::string) + IGNORE_TYPE(std::vector) +#undef IGNORE_TYPE + + flag->CheckDefaultValueParsingRoundtrip(); + }); +#endif +} + +// -------------------------------------------------------------------- + +// Returns success status, which is true if we successfully read all flag files, +// in which case new ArgLists are appended to the input_args in a reverse order +// of file names in the input flagfiles list. This order ensures that flags from +// the first flagfile in the input list are processed before the second flagfile +// etc. +bool ReadFlagfiles(const std::vector& flagfiles, + std::vector* input_args) { + bool success = true; + for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) { + ArgsList al; + + if (al.ReadFromFlagfile(*it)) { + input_args->push_back(al); + } else { + success = false; + } + } + + return success; +} + +// Returns success status, which is true if were able to locate all environment +// variables correctly or if fail_on_absent_in_env is false. The environment +// variable names are expected to be of the form `FLAGS_`, where +// `flag_name` is a string from the input flag_names list. If successful we +// append a single ArgList at the end of the input_args. +bool ReadFlagsFromEnv(const std::vector& flag_names, + std::vector* input_args, + bool fail_on_absent_in_env) { + bool success = true; + std::vector args; + + // This argument represents fake argv[0], which should be present in all arg + // lists. + args.push_back(""); + + for (const auto& flag_name : flag_names) { + // Avoid infinite recursion. + if (flag_name == "fromenv" || flag_name == "tryfromenv") { + flags_internal::ReportUsageError( + absl::StrCat("Infinite recursion on flag ", flag_name), true); + + success = false; + continue; + } + + const std::string envname = absl::StrCat("FLAGS_", flag_name); + std::string envval; + if (!GetEnvVar(envname.c_str(), &envval)) { + if (fail_on_absent_in_env) { + flags_internal::ReportUsageError( + absl::StrCat(envname, " not found in environment"), true); + + success = false; + } + + continue; + } + + args.push_back(absl::StrCat("--", flag_name, "=", envval)); + } + + if (success) { + input_args->emplace_back(args); + } + + return success; +} + +// -------------------------------------------------------------------- + +// Returns success status, which is true if were able to handle all generator +// flags (flagfile, fromenv, tryfromemv) successfully. +bool HandleGeneratorFlags(std::vector* input_args, + std::vector* flagfile_value) { + bool success = true; + + absl::MutexLock l(&flags_internal::processing_checks_guard); + + // flagfile could have been set either on a command line or + // programmatically before invoking ParseCommandLine. Note that we do not + // actually process arguments specified in the flagfile, but instead + // create a secondary arguments list to be processed along with the rest + // of the comamnd line arguments. Since we always the process most recently + // created list of arguments first, this will result in flagfile argument + // being processed before any other argument in the command line. If + // FLAGS_flagfile contains more than one file name we create multiple new + // levels of arguments in a reverse order of file names. Thus we always + // process arguments from first file before arguments containing in a + // second file, etc. If flagfile contains another + // --flagfile inside of it, it will produce new level of arguments and + // processed before the rest of the flagfile. We are also collecting all + // flagfiles set on original command line. Unlike the rest of the flags, + // this flag can be set multiple times and is expected to be handled + // multiple times. We are collecting them all into a single list and set + // the value of FLAGS_flagfile to that value at the end of the parsing. + if (flags_internal::flagfile_needs_processing) { + auto flagfiles = absl::GetFlag(FLAGS_flagfile); + + if (input_args->size() == 1) { + flagfile_value->insert(flagfile_value->end(), flagfiles.begin(), + flagfiles.end()); + } + + success &= ReadFlagfiles(flagfiles, input_args); + + flags_internal::flagfile_needs_processing = false; + } + + // Similar to flagfile fromenv/tryfromemv can be set both + // programmatically and at runtime on a command line. Unlike flagfile these + // can't be recursive. + if (flags_internal::fromenv_needs_processing) { + auto flags_list = absl::GetFlag(FLAGS_fromenv); + + success &= ReadFlagsFromEnv(flags_list, input_args, true); + + flags_internal::fromenv_needs_processing = false; + } + + if (flags_internal::tryfromenv_needs_processing) { + auto flags_list = absl::GetFlag(FLAGS_tryfromenv); + + success &= ReadFlagsFromEnv(flags_list, input_args, false); + + flags_internal::tryfromenv_needs_processing = false; + } + + return success; +} + +// -------------------------------------------------------------------- + +void ResetGeneratorFlags(const std::vector& flagfile_value) { + // Setting flagfile to the value which collates all the values set on a + // command line and programmatically. So if command line looked like + // --flagfile=f1 --flagfile=f2 the final value of the FLAGS_flagfile flag is + // going to be {"f1", "f2"} + if (!flagfile_value.empty()) { + absl::SetFlag(&FLAGS_flagfile, flagfile_value); + absl::MutexLock l(&flags_internal::processing_checks_guard); + flags_internal::flagfile_needs_processing = false; + } + + // fromenv/tryfromenv are set to value. + if (!absl::GetFlag(FLAGS_fromenv).empty()) { + absl::SetFlag(&FLAGS_fromenv, {}); + } + if (!absl::GetFlag(FLAGS_tryfromenv).empty()) { + absl::SetFlag(&FLAGS_tryfromenv, {}); + } + + absl::MutexLock l(&flags_internal::processing_checks_guard); + flags_internal::fromenv_needs_processing = false; + flags_internal::tryfromenv_needs_processing = false; +} + +// -------------------------------------------------------------------- + +// Returns: +// success status +// deduced value +// We are also mutating curr_list in case if we need to get a hold of next +// argument in the input. +std::tuple DeduceFlagValue(const CommandLineFlag& flag, + absl::string_view value, + bool is_negative, + bool is_empty_value, + ArgsList* curr_list) { + // Value is either an argument suffix after `=` in "--foo=" + // or separate argument in case of "--foo" "". + + // boolean flags have these forms: + // --foo + // --nofoo + // --foo=true + // --foo=false + // --nofoo= is not supported + // --foo is not supported + + // non boolean flags have these forms: + // --foo= + // --foo + // --nofoo is not supported + + if (flag.IsOfType()) { + if (value.empty()) { + if (is_empty_value) { + // "--bool_flag=" case + flags_internal::ReportUsageError( + absl::StrCat( + "Missing the value after assignment for the boolean flag '", + flag.Name(), "'"), + true); + return std::make_tuple(false, ""); + } + + // "--bool_flag" case + value = is_negative ? "0" : "1"; + } else if (is_negative) { + // "--nobool_flag=Y" case + flags_internal::ReportUsageError( + absl::StrCat("Negative form with assignment is not valid for the " + "boolean flag '", + flag.Name(), "'"), + true); + return std::make_tuple(false, ""); + } + } else if (is_negative) { + // "--noint_flag=1" case + flags_internal::ReportUsageError( + absl::StrCat("Negative form is not valid for the flag '", flag.Name(), + "'"), + true); + return std::make_tuple(false, ""); + } else if (value.empty() && (!is_empty_value)) { + if (curr_list->Size() == 1) { + // "--int_flag" case + flags_internal::ReportUsageError( + absl::StrCat("Missing the value for the flag '", flag.Name(), "'"), + true); + return std::make_tuple(false, ""); + } + + // "--int_flag" "10" case + curr_list->PopFront(); + value = curr_list->Front(); + + // Heuristic to detect the case where someone treats a std::string arg + // like a bool or just forgets to pass a value: + // --my_string_var --foo=bar + // We look for a flag of std::string type, whose value begins with a + // dash and corresponds to known flag or standalone --. + if (value[0] == '-' && flag.IsOfType()) { + auto maybe_flag_name = std::get<0>(SplitNameAndValue(value.substr(1))); + + if (maybe_flag_name.empty() || + std::get<0>(LocateFlag(maybe_flag_name)) != nullptr) { + // "--string_flag" "--known_flag" case + ABSL_INTERNAL_LOG( + WARNING, + absl::StrCat("Did you really mean to set flag '", flag.Name(), + "' to the value '", value, "'?")); + } + } + } + + return std::make_tuple(true, value); +} + +// -------------------------------------------------------------------- + +bool CanIgnoreUndefinedFlag(absl::string_view flag_name) { + auto undefok = absl::GetFlag(FLAGS_undefok); + if (std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) { + return true; + } + + if (absl::ConsumePrefix(&flag_name, "no") && + std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) { + return true; + } + + return false; +} + +} // namespace + +// -------------------------------------------------------------------- + +std::vector ParseCommandLineImpl(int argc, char* argv[], + ArgvListAction arg_list_act, + UsageFlagsAction usage_flag_act, + OnUndefinedFlag on_undef_flag) { + ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]"); + + // This routine does not return anything since we abort on failure. + CheckDefaultValuesParsingRoundtrip(); + + std::vector flagfile_value; + + std::vector input_args; + input_args.push_back(ArgsList(argc, argv)); + + std::vector output_args; + std::vector positional_args; + output_args.reserve(argc); + + // This is the list of undefined flags. The element of the list is the pair + // consisting of boolean indicating if flag came from command line (vs from + // some flag file we've read) and flag name. + // TODO(rogeeff): Eliminate the first element in the pair after cleanup. + std::vector> undefined_flag_names; + + // Set program invocation name if it is not set before. + if (ProgramInvocationName() == "UNKNOWN") { + flags_internal::SetProgramInvocationName(argv[0]); + } + output_args.push_back(argv[0]); + + // Iterate through the list of the input arguments. First level are arguments + // originated from argc/argv. Following levels are arguments originated from + // recursive parsing of flagfile(s). + bool success = true; + while (!input_args.empty()) { + // 10. First we process the built-in generator flags. + success &= HandleGeneratorFlags(&input_args, &flagfile_value); + + // 30. Select top-most (most recent) arguments list. If it is empty drop it + // and re-try. + ArgsList& curr_list = input_args.back(); + + curr_list.PopFront(); + + if (curr_list.Size() == 0) { + input_args.pop_back(); + continue; + } + + // 40. Pick up the front remaining argument in the current list. If current + // stack of argument lists contains only one element - we are processing an + // argument from the original argv. + absl::string_view arg(curr_list.Front()); + bool arg_from_argv = input_args.size() == 1; + + // 50. If argument does not start with - or is just "-" - this is + // positional argument. + if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) { + ABSL_INTERNAL_CHECK(arg_from_argv, + "Flagfile cannot contain positional argument"); + + positional_args.push_back(argv[curr_list.FrontIndex()]); + continue; + } + + if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs)) { + output_args.push_back(argv[curr_list.FrontIndex()]); + } + + // 60. Split the current argument on '=' to figure out the argument + // name and value. If flag name is empty it means we've got "--". value + // can be empty either if there were no '=' in argument std::string at all or + // an argument looked like "--foo=". In a latter case is_empty_value is + // true. + absl::string_view flag_name; + absl::string_view value; + bool is_empty_value = false; + + std::tie(flag_name, value, is_empty_value) = SplitNameAndValue(arg); + + // 70. "--" alone means what it does for GNU: stop flags parsing. We do + // not support positional arguments in flagfiles, so we just drop them. + if (flag_name.empty()) { + ABSL_INTERNAL_CHECK(arg_from_argv, + "Flagfile cannot contain positional argument"); + + curr_list.PopFront(); + break; + } + + // 80. Locate the flag based on flag name. Handle both --foo and --nofoo + CommandLineFlag* flag = nullptr; + bool is_negative = false; + std::tie(flag, is_negative) = LocateFlag(flag_name); + + if (flag == nullptr) { + if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) { + undefined_flag_names.emplace_back(arg_from_argv, + std::string(flag_name)); + } + continue; + } + + // 90. Deduce flag's value (from this or next argument) + auto curr_index = curr_list.FrontIndex(); + bool value_success = true; + std::tie(value_success, value) = + DeduceFlagValue(*flag, value, is_negative, is_empty_value, &curr_list); + success &= value_success; + + // If above call consumed an argument, it was a standalone value + if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs) && + (curr_index != curr_list.FrontIndex())) { + output_args.push_back(argv[curr_list.FrontIndex()]); + } + + // 100. Set the located flag to a new new value, unless it is retired. + // Setting retired flag fails, but we ignoring it here. + if (flag->IsRetired()) continue; + + std::string error; + if (!flag->SetFromString(value, SET_FLAGS_VALUE, kCommandLine, &error)) { + flags_internal::ReportUsageError(error, true); + success = false; + } + } + + for (const auto& flag_name : undefined_flag_names) { + if (CanIgnoreUndefinedFlag(flag_name.second)) continue; + + flags_internal::ReportUsageError( + absl::StrCat("Unknown command line flag '", flag_name.second, "'"), + true); + + success = false; + } + +#if ABSL_FLAGS_STRIP_NAMES + if (!success) { + flags_internal::ReportUsageError( + "NOTE: command line flags are disabled in this build", true); + } +#endif + + if (!success) { + flags_internal::HandleUsageFlags(std::cout, + ProgramUsageMessage()); + std::exit(1); + } + + if (usage_flag_act == UsageFlagsAction::kHandleUsage) { + int exit_code = flags_internal::HandleUsageFlags( + std::cout, ProgramUsageMessage()); + + if (exit_code != -1) { + std::exit(exit_code); + } + } + + ResetGeneratorFlags(flagfile_value); + + // Reinstate positional args which were intermixed with flags in the arguments + // list. + for (auto arg : positional_args) { + output_args.push_back(arg); + } + + // All the remaining arguments are positional. + if (!input_args.empty()) { + for (int arg_index = input_args.back().FrontIndex(); arg_index < argc; + ++arg_index) { + output_args.push_back(argv[arg_index]); + } + } + + return output_args; +} + +} // namespace flags_internal + +// -------------------------------------------------------------------- + +std::vector ParseCommandLine(int argc, char* argv[]) { + return flags_internal::ParseCommandLineImpl( + argc, argv, flags_internal::ArgvListAction::kRemoveParsedArgs, + flags_internal::UsageFlagsAction::kHandleUsage, + flags_internal::OnUndefinedFlag::kAbortIfUndefined); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/parse.h b/absl/flags/parse.h new file mode 100644 index 00000000..469bd506 --- /dev/null +++ b/absl/flags/parse.h @@ -0,0 +1,60 @@ +// +// 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: parse.h +// ----------------------------------------------------------------------------- +// +// This file defines the main parsing function for Abseil flags: +// `absl::ParseCommandLine()`. + +#ifndef ABSL_FLAGS_PARSE_H_ +#define ABSL_FLAGS_PARSE_H_ + +#include +#include + +#include "absl/flags/internal/parse.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// ParseCommandLine() +// +// Parses the set of command-line arguments passed in the `argc` (argument +// count) and `argv[]` (argument vector) parameters from `main()`, assigning +// values to any defined Abseil flags. (Any arguments passed after the +// flag-terminating delimiter (`--`) are treated as positional arguments and +// ignored.) +// +// Any command-line flags (and arguments to those flags) are parsed into Abseil +// Flag values, if those flags are defined. Any undefined flags will either +// return an error, or be ignored if that flag is designated using `undefok` to +// indicate "undefined is OK." +// +// Any command-line positional arguments not part of any command-line flag (or +// arguments to a flag) are returned in a vector, with the program invocation +// name at position 0 of that vector. (Note that this includes positional +// arguments after the flag-terminating delimiter `--`.) +// +// After all flags and flag arguments are parsed, this function looks for any +// built-in usage flags (e.g. `--help`), and if any were specified, it reports +// help messages and then exits the program. +std::vector ParseCommandLine(int argc, char* argv[]); + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_PARSE_H_ diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc new file mode 100644 index 00000000..447a3bc7 --- /dev/null +++ b/absl/flags/parse_test.cc @@ -0,0 +1,858 @@ +// +// 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/parse.h" + +#include + +#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/flag.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/substitute.h" +#include "absl/types/span.h" + +#ifdef _WIN32 +#include +#endif + +namespace { + +using absl::base_internal::ScopedSetEnv; + +struct UDT { + UDT() = default; + UDT(const UDT&) = default; + UDT(int v) : value(v) {} // NOLINT + + int value; +}; + +bool AbslParseFlag(absl::string_view in, UDT* udt, std::string* err) { + if (in == "A") { + udt->value = 1; + return true; + } + if (in == "AAA") { + udt->value = 10; + return true; + } + + *err = "Use values A, AAA instead"; + return false; +} +std::string AbslUnparseFlag(const UDT& udt) { + return udt.value == 1 ? "A" : "AAA"; +} + +std::string GetTestTmpDirEnvVar(const char* const env_var_name) { +#ifdef _WIN32 + char buf[MAX_PATH]; + auto get_res = GetEnvironmentVariableA(env_var_name, buf, sizeof(buf)); + if (get_res >= sizeof(buf) || get_res == 0) { + return ""; + } + + return std::string(buf, get_res); +#else + const char* val = ::getenv(env_var_name); + if (val == nullptr) { + return ""; + } + + return val; +#endif +} + +const std::string& GetTestTempDir() { + static std::string* temp_dir_name = []() -> std::string* { + std::string* res = new std::string(GetTestTmpDirEnvVar("TEST_TMPDIR")); + + if (res->empty()) { + *res = GetTestTmpDirEnvVar("TMPDIR"); + } + + if (res->empty()) { +#ifdef _WIN32 + char temp_path_buffer[MAX_PATH]; + + 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()); + if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) { + *res = temp_dir_name; + } + } +#else + char temp_dir_template[] = "/tmp/parse_test.XXXXXX"; + if (auto* unique_name = ::mkdtemp(temp_dir_template)) { + *res = unique_name; + } +#endif + + if (res->empty()) { + ABSL_INTERNAL_LOG(FATAL, + "Failed to make temporary directory for data files"); + } + } + +#ifdef _WIN32 + *res += "\\"; +#else + *res += "/"; +#endif + + return res; + }(); + + return *temp_dir_name; +} + +struct FlagfileData { + const absl::string_view file_name; + const absl::Span file_lines; +}; + +// clang-format off +constexpr const char* const ff1_data[] = { + "# comment ", + " # comment ", + "", + " ", + "--int_flag=-1", + " --string_flag=q2w2 ", + " ## ", + " --double_flag=0.1", + "--bool_flag=Y " +}; + +constexpr const char* const ff2_data[] = { + "# Setting legacy flag", + "--legacy_int=1111", + "--legacy_bool", + "--nobool_flag", + "--legacy_str=aqsw", + "--int_flag=100", + " ## =============" +}; +// clang-format on + +// Builds flagfile flag in the flagfile_flag buffer and returns it. This +// function also creates a temporary flagfile based on FlagfileData input. +// We create a flagfile in a temporary directory with the name specified in +// FlagfileData and populate it with lines specifed in FlagfileData. If $0 is +// referenced in any of the lines in FlagfileData they are replaced with +// temporary directory location. This way we can test inclusion of one flagfile +// from another flagfile. +const char* GetFlagfileFlag(const std::vector& ffd, + std::string* flagfile_flag) { + *flagfile_flag = "--flagfile="; + absl::string_view separator; + for (const auto& flagfile_data : ffd) { + std::string flagfile_name = + absl::StrCat(GetTestTempDir(), flagfile_data.file_name); + + std::ofstream flagfile_out(flagfile_name); + for (auto line : flagfile_data.file_lines) { + flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n"; + } + + absl::StrAppend(flagfile_flag, separator, flagfile_name); + separator = ","; + } + + return flagfile_flag->c_str(); +} + +} // namespace + +ABSL_FLAG(int, int_flag, 1, ""); +ABSL_FLAG(double, double_flag, 1.1, ""); +ABSL_FLAG(std::string, string_flag, "a", ""); +ABSL_FLAG(bool, bool_flag, false, ""); +ABSL_FLAG(UDT, udt_flag, -1, ""); +ABSL_RETIRED_FLAG(int, legacy_int, 1, ""); +ABSL_RETIRED_FLAG(bool, legacy_bool, false, ""); +ABSL_RETIRED_FLAG(std::string, legacy_str, "l", ""); + +namespace { + +namespace flags = absl::flags_internal; +using testing::ElementsAreArray; + +class ParseTest : public testing::Test { + private: + flags::FlagSaver flag_saver_; +}; + +// -------------------------------------------------------------------- + +template +std::vector InvokeParse(const char* (&in_argv)[N]) { + return absl::ParseCommandLine(N, const_cast(in_argv)); +} + +// -------------------------------------------------------------------- + +template +void TestParse(const char* (&in_argv)[N], int int_flag_value, + double double_flag_val, absl::string_view string_flag_val, + bool bool_flag_val, int exp_position_args = 0) { + auto out_args = InvokeParse(in_argv); + + EXPECT_EQ(out_args.size(), 1 + exp_position_args); + EXPECT_STREQ(out_args[0], "testbin"); + + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), int_flag_value); + EXPECT_NEAR(absl::GetFlag(FLAGS_double_flag), double_flag_val, 0.0001); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), string_flag_val); + EXPECT_EQ(absl::GetFlag(FLAGS_bool_flag), bool_flag_val); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestEmptyArgv) { + const char* in_argv[] = {"testbin"}; + + auto out_args = InvokeParse(in_argv); + + EXPECT_EQ(out_args.size(), 1); + EXPECT_STREQ(out_args[0], "testbin"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidIntArg) { + const char* in_args1[] = { + "testbin", + "--int_flag=10", + }; + TestParse(in_args1, 10, 1.1, "a", false); + + const char* in_args2[] = { + "testbin", + "-int_flag=020", + }; + TestParse(in_args2, 20, 1.1, "a", false); + + const char* in_args3[] = { + "testbin", + "--int_flag", + "-30", + }; + TestParse(in_args3, -30, 1.1, "a", false); + + const char* in_args4[] = { + "testbin", + "-int_flag", + "0x21", + }; + TestParse(in_args4, 33, 1.1, "a", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidDoubleArg) { + const char* in_args1[] = { + "testbin", + "--double_flag=2.3", + }; + TestParse(in_args1, 1, 2.3, "a", false); + + const char* in_args2[] = { + "testbin", + "--double_flag=0x1.2", + }; + TestParse(in_args2, 1, 1.125, "a", false); + + const char* in_args3[] = { + "testbin", + "--double_flag", + "99.7", + }; + TestParse(in_args3, 1, 99.7, "a", false); + + const char* in_args4[] = { + "testbin", + "--double_flag", + "0x20.1", + }; + TestParse(in_args4, 1, 32.0625, "a", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidStringArg) { + const char* in_args1[] = { + "testbin", + "--string_flag=aqswde", + }; + TestParse(in_args1, 1, 1.1, "aqswde", false); + + const char* in_args2[] = { + "testbin", + "-string_flag=a=b=c", + }; + TestParse(in_args2, 1, 1.1, "a=b=c", false); + + const char* in_args3[] = { + "testbin", + "--string_flag", + "zaxscd", + }; + TestParse(in_args3, 1, 1.1, "zaxscd", false); + + const char* in_args4[] = { + "testbin", + "-string_flag", + "--int_flag", + }; + TestParse(in_args4, 1, 1.1, "--int_flag", false); + + const char* in_args5[] = { + "testbin", + "--string_flag", + "--no_a_flag=11", + }; + TestParse(in_args5, 1, 1.1, "--no_a_flag=11", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidBoolArg) { + const char* in_args1[] = { + "testbin", + "--bool_flag", + }; + TestParse(in_args1, 1, 1.1, "a", true); + + const char* in_args2[] = { + "testbin", + "--nobool_flag", + }; + TestParse(in_args2, 1, 1.1, "a", false); + + const char* in_args3[] = { + "testbin", + "--bool_flag=true", + }; + TestParse(in_args3, 1, 1.1, "a", true); + + const char* in_args4[] = { + "testbin", + "-bool_flag=false", + }; + TestParse(in_args4, 1, 1.1, "a", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidUDTArg) { + const char* in_args1[] = { + "testbin", + "--udt_flag=A", + }; + InvokeParse(in_args1); + + EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 1); + + const char* in_args2[] = {"testbin", "--udt_flag", "AAA"}; + InvokeParse(in_args2); + + EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 10); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidMultipleArg) { + const char* in_args1[] = { + "testbin", "--bool_flag", "--int_flag=2", + "--double_flag=0.1", "--string_flag=asd", + }; + TestParse(in_args1, 2, 0.1, "asd", true); + + const char* in_args2[] = { + "testbin", "--string_flag=", "--nobool_flag", "--int_flag", + "-011", "--double_flag", "-1e-2", + }; + TestParse(in_args2, -11, -0.01, "", false); + + const char* in_args3[] = { + "testbin", "--int_flag", "-0", "--string_flag", "\"\"", + "--bool_flag=true", "--double_flag=1e18", + }; + TestParse(in_args3, 0, 1e18, "\"\"", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestPositionalArgs) { + const char* in_args1[] = { + "testbin", + "p1", + "p2", + }; + TestParse(in_args1, 1, 1.1, "a", false, 2); + + auto out_args1 = InvokeParse(in_args1); + + EXPECT_STREQ(out_args1[1], "p1"); + EXPECT_STREQ(out_args1[2], "p2"); + + const char* in_args2[] = { + "testbin", + "--int_flag=2", + "p1", + }; + TestParse(in_args2, 2, 1.1, "a", false, 1); + + auto out_args2 = InvokeParse(in_args2); + + EXPECT_STREQ(out_args2[1], "p1"); + + const char* in_args3[] = {"testbin", "p1", "--int_flag=3", + "p2", "--bool_flag", "true"}; + TestParse(in_args3, 3, 1.1, "a", true, 3); + + auto out_args3 = InvokeParse(in_args3); + + EXPECT_STREQ(out_args3[1], "p1"); + EXPECT_STREQ(out_args3[2], "p2"); + EXPECT_STREQ(out_args3[3], "true"); + + const char* in_args4[] = { + "testbin", + "--", + "p1", + "p2", + }; + TestParse(in_args4, 3, 1.1, "a", true, 2); + + auto out_args4 = InvokeParse(in_args4); + + EXPECT_STREQ(out_args4[1], "p1"); + EXPECT_STREQ(out_args4[2], "p2"); + + const char* in_args5[] = { + "testbin", "p1", "--int_flag=4", "--", "--bool_flag", "false", "p2", + }; + TestParse(in_args5, 4, 1.1, "a", true, 4); + + auto out_args5 = InvokeParse(in_args5); + + EXPECT_STREQ(out_args5[1], "p1"); + EXPECT_STREQ(out_args5[2], "--bool_flag"); + EXPECT_STREQ(out_args5[3], "false"); + EXPECT_STREQ(out_args5[4], "p2"); +} + +// -------------------------------------------------------------------- + +using ParseDeathTest = ParseTest; + +TEST_F(ParseDeathTest, TestUndefinedArg) { + const char* in_args1[] = { + "testbin", + "--undefined_flag", + }; + EXPECT_DEATH(InvokeParse(in_args1), + "Unknown command line flag 'undefined_flag'"); + + const char* in_args2[] = { + "testbin", + "--noprefixed_flag", + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Unknown command line flag 'noprefixed_flag'"); + + const char* in_args3[] = { + "testbin", + "--Int_flag=1", + }; + EXPECT_DEATH(InvokeParse(in_args3), "Unknown command line flag 'Int_flag'"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) { + const char* in_args1[] = { + "testbin", + "--bool_flag=", + }; + EXPECT_DEATH( + InvokeParse(in_args1), + "Missing the value after assignment for the boolean flag 'bool_flag'"); + + const char* in_args2[] = { + "testbin", + "--nobool_flag=true", + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Negative form with assignment is not valid for the boolean " + "flag 'bool_flag'"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestInvalidNonBoolFlagFormat) { + const char* in_args1[] = { + "testbin", + "--nostring_flag", + }; + EXPECT_DEATH(InvokeParse(in_args1), + "Negative form is not valid for the flag 'string_flag'"); + + const char* in_args2[] = { + "testbin", + "--int_flag", + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Missing the value for the flag 'int_flag'"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) { + const char* in_args1[] = { + "testbin", + "--udt_flag=1", + }; + EXPECT_DEATH(InvokeParse(in_args1), + "Illegal value '1' specified for flag 'udt_flag'; Use values A, " + "AAA instead"); + + const char* in_args2[] = { + "testbin", + "--udt_flag", + "AA", + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Illegal value 'AA' specified for flag 'udt_flag'; Use values " + "A, AAA instead"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestLegacyFlags) { + const char* in_args1[] = { + "testbin", + "--legacy_int=11", + }; + TestParse(in_args1, 1, 1.1, "a", false); + + const char* in_args2[] = { + "testbin", + "--legacy_bool", + }; + TestParse(in_args2, 1, 1.1, "a", false); + + const char* in_args3[] = { + "testbin", "--legacy_int", "22", "--int_flag=2", + "--legacy_bool", "true", "--legacy_str", "--string_flag=qwe", + }; + TestParse(in_args3, 2, 1.1, "a", false, 1); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestSimpleValidFlagfile) { + std::string flagfile_flag; + + const char* in_args1[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, + &flagfile_flag), + }; + TestParse(in_args1, -1, 0.1, "q2w2 ", true); + + const char* in_args2[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}}, + &flagfile_flag), + }; + TestParse(in_args2, 100, 0.1, "q2w2 ", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidMultiFlagfile) { + std::string flagfile_flag; + + const char* in_args1[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}, + {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, + &flagfile_flag), + }; + TestParse(in_args1, -1, 0.1, "q2w2 ", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestFlagfileMixedWithRegularFlags) { + std::string flagfile_flag; + + const char* in_args1[] = { + "testbin", "--int_flag=3", + GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, + &flagfile_flag), + "-double_flag=0.2"}; + TestParse(in_args1, -1, 0.2, "q2w2 ", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestFlagfileInFlagfile) { + std::string flagfile_flag; + + constexpr const char* const ff3_data[] = { + "--flagfile=$0/parse_test.ff1", + "--flagfile=$0/parse_test.ff2", + }; + + const char* in_args1[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}}, + &flagfile_flag), + }; + TestParse(in_args1, 100, 0.1, "q2w2 ", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestInvalidFlagfiles) { + std::string flagfile_flag; + + constexpr const char* const ff4_data[] = { + "--unknown_flag=10" + }; + + const char* in_args1[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff4", + absl::MakeConstSpan(ff4_data)}}, &flagfile_flag), + }; + EXPECT_DEATH(InvokeParse(in_args1), + "Unknown command line flag 'unknown_flag'"); + + constexpr const char* const ff5_data[] = { + "--int_flag 10", + }; + + const char* in_args2[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff5", + absl::MakeConstSpan(ff5_data)}}, &flagfile_flag), + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Unknown command line flag 'int_flag 10'"); + + constexpr const char* const ff6_data[] = { + "--int_flag=10", "--", "arg1", "arg2", "arg3", + }; + + const char* in_args3[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}}, + &flagfile_flag), + }; + EXPECT_DEATH(InvokeParse(in_args3), + "Flagfile can't contain position arguments or --"); + + const char* in_args4[] = { + "testbin", + "--flagfile=invalid_flag_file", + }; + EXPECT_DEATH(InvokeParse(in_args4), "Can't open flagfile invalid_flag_file"); + + constexpr const char* const ff7_data[] = { + "--int_flag=10", + "*bin*", + "--str_flag=aqsw", + }; + + const char* in_args5[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}}, + &flagfile_flag), + }; + EXPECT_DEATH(InvokeParse(in_args5), + "Unexpected line in the flagfile .*: \\*bin\\*"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestReadingRequiredFlagsFromEnv) { + const char* in_args1[] = {"testbin", + "--fromenv=int_flag,bool_flag,string_flag"}; + + ScopedSetEnv set_int_flag("FLAGS_int_flag", "33"); + ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "True"); + ScopedSetEnv set_string_flag("FLAGS_string_flag", "AQ12"); + + TestParse(in_args1, 33, 1.1, "AQ12", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestReadingUnsetRequiredFlagsFromEnv) { + const char* in_args1[] = {"testbin", "--fromenv=int_flag"}; + + EXPECT_DEATH(InvokeParse(in_args1), + "FLAGS_int_flag not found in environment"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestRecursiveFlagsFromEnv) { + const char* in_args1[] = {"testbin", "--fromenv=tryfromenv"}; + + ScopedSetEnv set_tryfromenv("FLAGS_tryfromenv", "int_flag"); + + EXPECT_DEATH(InvokeParse(in_args1), "Infinite recursion on flag tryfromenv"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestReadingOptionalFlagsFromEnv) { + const char* in_args1[] = { + "testbin", "--tryfromenv=int_flag,bool_flag,string_flag,other_flag"}; + + ScopedSetEnv set_int_flag("FLAGS_int_flag", "17"); + ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "Y"); + + TestParse(in_args1, 17, 1.1, "a", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) { + const char* in_args1[] = { + "testbin", + "--bool_flag=T", + "--tryfromenv=int_flag,bool_flag", + "--int_flag=-21", + }; + + ScopedSetEnv set_int_flag("FLAGS_int_flag", "-15"); + ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "F"); + + TestParse(in_args1, -21, 1.1, "a", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestKeepParsedArgs) { + const char* in_args1[] = { + "testbin", "arg1", "--bool_flag", + "--int_flag=211", "arg2", "--double_flag=1.1", + "--string_flag", "asd", "--", + "arg3", "arg4", + }; + + auto out_args1 = InvokeParse(in_args1); + + EXPECT_THAT( + out_args1, + ElementsAreArray({absl::string_view("testbin"), absl::string_view("arg1"), + absl::string_view("arg2"), absl::string_view("arg3"), + absl::string_view("arg4")})); + + auto out_args2 = flags::ParseCommandLineImpl( + 11, const_cast(in_args1), flags::ArgvListAction::kKeepParsedArgs, + flags::UsageFlagsAction::kHandleUsage, + flags::OnUndefinedFlag::kAbortIfUndefined); + + EXPECT_THAT( + out_args2, + ElementsAreArray({absl::string_view("testbin"), + absl::string_view("--bool_flag"), + absl::string_view("--int_flag=211"), + absl::string_view("--double_flag=1.1"), + absl::string_view("--string_flag"), + absl::string_view("asd"), absl::string_view("--"), + absl::string_view("arg1"), absl::string_view("arg2"), + absl::string_view("arg3"), absl::string_view("arg4")})); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestIgnoreUndefinedFlags) { + const char* in_args1[] = { + "testbin", + "arg1", + "--undef_flag=aa", + "--int_flag=21", + }; + + auto out_args1 = flags::ParseCommandLineImpl( + 4, const_cast(in_args1), flags::ArgvListAction::kRemoveParsedArgs, + flags::UsageFlagsAction::kHandleUsage, + flags::OnUndefinedFlag::kIgnoreUndefined); + + EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"), + absl::string_view("arg1")})); + + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21); + + const char* in_args2[] = { + "testbin", + "arg1", + "--undef_flag=aa", + "--string_flag=AA", + }; + + auto out_args2 = flags::ParseCommandLineImpl( + 4, const_cast(in_args2), flags::ArgvListAction::kKeepParsedArgs, + flags::UsageFlagsAction::kHandleUsage, + flags::OnUndefinedFlag::kIgnoreUndefined); + + EXPECT_THAT( + out_args2, + ElementsAreArray( + {absl::string_view("testbin"), absl::string_view("--undef_flag=aa"), + absl::string_view("--string_flag=AA"), absl::string_view("arg1")})); + + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "AA"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestHelpFlagHandling) { + const char* in_args1[] = { + "testbin", + "--help", + }; + + EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1), ""); + + const char* in_args2[] = { + "testbin", + "--help", + "--int_flag=3", + }; + + auto out_args2 = flags::ParseCommandLineImpl( + 3, const_cast(in_args2), flags::ArgvListAction::kRemoveParsedArgs, + flags::UsageFlagsAction::kIgnoreUsage, + flags::OnUndefinedFlag::kAbortIfUndefined); + + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3); +} + +} // namespace diff --git a/absl/flags/usage.cc b/absl/flags/usage.cc new file mode 100644 index 00000000..12c346b7 --- /dev/null +++ b/absl/flags/usage.cc @@ -0,0 +1,58 @@ +// +// 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/usage.h" + +#include + +#include "absl/flags/internal/usage.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +inline namespace lts_2019_08_08 { +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; +} // namespace +} // namespace flags_internal + +// -------------------------------------------------------------------- +// Sets the "usage" message to be used by help reporting routines. +void SetProgramUsageMessage(absl::string_view new_usage_message) { + absl::MutexLock l(&flags_internal::usage_message_guard); + + if (flags_internal::program_usage_message != nullptr) { + ABSL_INTERNAL_LOG(FATAL, "SetProgramUsageMessage() called twice."); + std::exit(1); + } + + flags_internal::program_usage_message = new std::string(new_usage_message); +} + +// -------------------------------------------------------------------- +// Returns the usage message set by SetProgramUsageMessage(). +// Note: We able to return string_view here only because calling +// SetProgramUsageMessage twice is prohibited. +absl::string_view ProgramUsageMessage() { + absl::MutexLock l(&flags_internal::usage_message_guard); + + return flags_internal::program_usage_message != nullptr + ? absl::string_view(*flags_internal::program_usage_message) + : "Warning: SetProgramUsageMessage() never called"; +} + +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/usage.h b/absl/flags/usage.h new file mode 100644 index 00000000..c232a7d0 --- /dev/null +++ b/absl/flags/usage.h @@ -0,0 +1,42 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_USAGE_H_ +#define ABSL_FLAGS_USAGE_H_ + +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Usage reporting interfaces + +namespace absl { +inline namespace lts_2019_08_08 { + +// Sets the "usage" message to be used by help reporting routines. +// For example: +// absl::SetProgramUsageMessage( +// absl::StrCat("This program does nothing. Sample usage:\n", argv[0], +// " ")); +// Do not include commandline flags in the usage: we do that for you! +// Note: Calling SetProgramUsageMessage twice will trigger a call to std::exit. +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 +} // namespace absl + +#endif // ABSL_FLAGS_USAGE_H_ diff --git a/absl/flags/usage_config.cc b/absl/flags/usage_config.cc new file mode 100644 index 00000000..a538acd5 --- /dev/null +++ b/absl/flags/usage_config.cc @@ -0,0 +1,154 @@ +// +// 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/usage_config.h" + +#include +#include + +#include "absl/base/attributes.h" +#include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/program_name.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/strip.h" +#include "absl/synchronization/mutex.h" + +extern "C" { + +// Additional report of fatal usage error message before we std::exit. Error is +// fatal if is_fatal argument to ReportUsageError is true. +ABSL_ATTRIBUTE_WEAK void AbslInternalReportFatalUsageError(absl::string_view) {} + +} // extern "C" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +namespace { + +// -------------------------------------------------------------------- +// Returns true if flags defined in the filename should be reported with +// -helpshort flag. + +bool ContainsHelpshortFlags(absl::string_view filename) { + // By default we only want flags in binary's main. We expect the main + // routine to reside in .cc or -main.cc or + // _main.cc, where the is the name of the binary. + auto suffix = flags_internal::Basename(filename); + if (!absl::ConsumePrefix(&suffix, + flags_internal::ShortProgramInvocationName())) + return false; + return absl::StartsWith(suffix, ".") || absl::StartsWith(suffix, "-main.") || + absl::StartsWith(suffix, "_main."); +} + +// -------------------------------------------------------------------- +// Returns true if flags defined in the filename should be reported with +// -helppackage flag. + +bool ContainsHelppackageFlags(absl::string_view filename) { + // TODO(rogeeff): implement properly when registry is available. + return ContainsHelpshortFlags(filename); +} + +// -------------------------------------------------------------------- +// Generates program version information into supplied output. + +std::string VersionString() { + std::string version_str(flags_internal::ShortProgramInvocationName()); + + version_str += "\n"; + +#if !defined(NDEBUG) + version_str += "Debug build (NDEBUG not #defined)\n"; +#endif + + return version_str; +} + +// -------------------------------------------------------------------- +// Normalizes the filename specific to the build system/filesystem used. + +std::string NormalizeFilename(absl::string_view filename) { + // Skip any leading slashes + auto pos = filename.find_first_not_of("\\/"); + if (pos == absl::string_view::npos) return ""; + + filename.remove_prefix(pos); + return std::string(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; + +} // namespace + +FlagsUsageConfig GetUsageConfig() { + absl::MutexLock l(&custom_usage_config_guard); + + if (custom_usage_config) return *custom_usage_config; + + FlagsUsageConfig default_config; + default_config.contains_helpshort_flags = &ContainsHelpshortFlags; + default_config.contains_help_flags = &ContainsHelppackageFlags; + default_config.contains_helppackage_flags = &ContainsHelppackageFlags; + default_config.version_string = &VersionString; + default_config.normalize_filename = &NormalizeFilename; + + return default_config; +} + +void ReportUsageError(absl::string_view msg, bool is_fatal) { + std::cerr << "ERROR: " << msg << std::endl; + + if (is_fatal) { + AbslInternalReportFatalUsageError(msg); + } +} + +} // namespace flags_internal + +void SetFlagsUsageConfig(FlagsUsageConfig usage_config) { + absl::MutexLock l(&flags_internal::custom_usage_config_guard); + + if (!usage_config.contains_helpshort_flags) + usage_config.contains_helpshort_flags = + flags_internal::ContainsHelpshortFlags; + + if (!usage_config.contains_help_flags) + usage_config.contains_help_flags = flags_internal::ContainsHelppackageFlags; + + if (!usage_config.contains_helppackage_flags) + usage_config.contains_helppackage_flags = + flags_internal::ContainsHelppackageFlags; + + if (!usage_config.version_string) + usage_config.version_string = flags_internal::VersionString; + + if (!usage_config.normalize_filename) + usage_config.normalize_filename = flags_internal::NormalizeFilename; + + if (flags_internal::custom_usage_config) + *flags_internal::custom_usage_config = usage_config; + else + flags_internal::custom_usage_config = new FlagsUsageConfig(usage_config); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/usage_config.h b/absl/flags/usage_config.h new file mode 100644 index 00000000..c6d34e4a --- /dev/null +++ b/absl/flags/usage_config.h @@ -0,0 +1,133 @@ +// +// 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: usage_config.h +// ----------------------------------------------------------------------------- +// +// This file defines the main usage reporting configuration interfaces and +// documents Abseil's supported built-in usage flags. If these flags are found +// when parsing a command-line, Abseil will exit the program and display +// appropriate help messages. +#ifndef ABSL_FLAGS_USAGE_CONFIG_H_ +#define ABSL_FLAGS_USAGE_CONFIG_H_ + +#include +#include + +#include "absl/strings/string_view.h" + +// ----------------------------------------------------------------------------- +// Built-in Usage Flags +// ----------------------------------------------------------------------------- +// +// Abseil supports the following built-in usage flags. When passed, these flags +// exit the program and : +// +// * --help +// Shows help on important flags for this binary +// * --helpfull +// Shows help on all flags +// * --helpshort +// Shows help on only the main module for this program +// * --helppackage +// Shows help on all modules in the main package +// * --version +// Shows the version and build info for this binary and exits +// * --only_check_args +// Exits after checking all flags +// * --helpon +// Shows help on the modules named by this flag value +// * --helpmatch +// Shows help on modules whose name contains the specified substring + +namespace absl { +inline namespace lts_2019_08_08 { + +namespace flags_internal { +using FlagKindFilter = std::function; +} // namespace flags_internal + +// FlagsUsageConfig +// +// This structure contains the collection of callbacks for changing the behavior +// of the usage reporting routines in Abseil Flags. +struct FlagsUsageConfig { + // Returns true if flags defined in the given source code file should be + // reported with --helpshort flag. For example, if the file + // "path/to/my/code.cc" defines the flag "--my_flag", and + // contains_helpshort_flags("path/to/my/code.cc") returns true, invoking the + // program with --helpshort will include information about --my_flag in the + // program output. + flags_internal::FlagKindFilter contains_helpshort_flags; + + // Returns true if flags defined in the filename should be reported with + // --help flag. For example, if the file + // "path/to/my/code.cc" defines the flag "--my_flag", and + // contains_help_flags("path/to/my/code.cc") returns true, invoking the + // program with --help will include information about --my_flag in the + // program output. + flags_internal::FlagKindFilter contains_help_flags; + + // Returns true if flags defined in the filename should be reported with + // --helppackage flag. For example, if the file + // "path/to/my/code.cc" defines the flag "--my_flag", and + // contains_helppackage_flags("path/to/my/code.cc") returns true, invoking the + // program with --helppackage will include information about --my_flag in the + // program output. + flags_internal::FlagKindFilter contains_helppackage_flags; + + // Generates std::string containing program version. This is the std::string reported + // when user specifies --version in a command line. + std::function version_string; + + // Normalizes the filename specific to the build system/filesystem used. This + // routine is used when we report the information about the flag definition + // location. For instance, if your build resides at some location you do not + // want to expose in the usage output, you can trim it to show only relevant + // part. + // For example: + // normalize_filename("/my_company/some_long_path/src/project/file.cc") + // might produce + // "project/file.cc". + std::function normalize_filename; +}; + +// SetFlagsUsageConfig() +// +// Sets the usage reporting configuration callbacks. If any of the callbacks are +// not set in usage_config instance, then the default value of the callback is +// used. +void SetFlagsUsageConfig(FlagsUsageConfig usage_config); + +namespace flags_internal { + +FlagsUsageConfig GetUsageConfig(); + +void ReportUsageError(absl::string_view msg, bool is_fatal); + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +extern "C" { + +// Additional report of fatal usage error message before we std::exit. Error is +// fatal if is_fatal argument to ReportUsageError is true. +void AbslInternalReportFatalUsageError(absl::string_view); + +} // extern "C" + +#endif // ABSL_FLAGS_USAGE_CONFIG_H_ diff --git a/absl/flags/usage_config_test.cc b/absl/flags/usage_config_test.cc new file mode 100644 index 00000000..3bde13af --- /dev/null +++ b/absl/flags/usage_config_test.cc @@ -0,0 +1,198 @@ +// +// 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/usage_config.h" + +#include "gtest/gtest.h" +#include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/program_name.h" +#include "absl/strings/match.h" + +namespace { + +class FlagsUsageConfigTest : public testing::Test { + protected: + void SetUp() override { + // Install Default config for the use on this unit test. + // Binary may install a custom config before tests are run. + absl::FlagsUsageConfig default_config; + absl::SetFlagsUsageConfig(default_config); + } +}; + +namespace flags = absl::flags_internal; + +bool TstContainsHelpshortFlags(absl::string_view f) { + return absl::StartsWith(flags::Basename(f), "progname."); +} + +bool TstContainsHelppackageFlags(absl::string_view f) { + return absl::EndsWith(flags::Package(f), "aaa/"); +} + +bool TstContainsHelpFlags(absl::string_view f) { + return absl::EndsWith(flags::Package(f), "zzz/"); +} + +std::string TstVersionString() { return "program 1.0.0"; } + +std::string TstNormalizeFilename(absl::string_view filename) { + return std::string(filename.substr(2)); +} + +void TstReportUsageMessage(absl::string_view msg) {} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestGetSetFlagsUsageConfig) { + EXPECT_TRUE(flags::GetUsageConfig().contains_helpshort_flags); + EXPECT_TRUE(flags::GetUsageConfig().contains_help_flags); + EXPECT_TRUE(flags::GetUsageConfig().contains_helppackage_flags); + EXPECT_TRUE(flags::GetUsageConfig().version_string); + EXPECT_TRUE(flags::GetUsageConfig().normalize_filename); + + absl::FlagsUsageConfig empty_config; + empty_config.contains_helpshort_flags = &TstContainsHelpshortFlags; + empty_config.contains_help_flags = &TstContainsHelpFlags; + empty_config.contains_helppackage_flags = &TstContainsHelppackageFlags; + empty_config.version_string = &TstVersionString; + empty_config.normalize_filename = &TstNormalizeFilename; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_TRUE(flags::GetUsageConfig().contains_helpshort_flags); + EXPECT_TRUE(flags::GetUsageConfig().contains_help_flags); + EXPECT_TRUE(flags::GetUsageConfig().contains_helppackage_flags); + EXPECT_TRUE(flags::GetUsageConfig().version_string); + EXPECT_TRUE(flags::GetUsageConfig().normalize_filename); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestContainsHelpshortFlags) { + flags::SetProgramInvocationName("usage_config_test"); + + auto config = flags::GetUsageConfig(); + EXPECT_TRUE(config.contains_helpshort_flags("adir/cd/usage_config_test.cc")); + EXPECT_TRUE( + config.contains_helpshort_flags("aaaa/usage_config_test-main.cc")); + EXPECT_TRUE(config.contains_helpshort_flags("abc/usage_config_test_main.cc")); + EXPECT_FALSE(config.contains_helpshort_flags("usage_config_main.cc")); + + absl::FlagsUsageConfig empty_config; + empty_config.contains_helpshort_flags = &TstContainsHelpshortFlags; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_TRUE( + flags::GetUsageConfig().contains_helpshort_flags("aaa/progname.cpp")); + EXPECT_FALSE( + flags::GetUsageConfig().contains_helpshort_flags("aaa/progmane.cpp")); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestContainsHelpFlags) { + flags::SetProgramInvocationName("usage_config_test"); + + auto config = flags::GetUsageConfig(); + EXPECT_TRUE(config.contains_help_flags("zzz/usage_config_test.cc")); + EXPECT_TRUE( + config.contains_help_flags("bdir/a/zzz/usage_config_test-main.cc")); + EXPECT_TRUE( + config.contains_help_flags("//aqse/zzz/usage_config_test_main.cc")); + EXPECT_FALSE(config.contains_help_flags("zzz/aa/usage_config_main.cc")); + + absl::FlagsUsageConfig empty_config; + empty_config.contains_help_flags = &TstContainsHelpFlags; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_TRUE(flags::GetUsageConfig().contains_help_flags("zzz/main-body.c")); + EXPECT_FALSE( + flags::GetUsageConfig().contains_help_flags("zzz/dir/main-body.c")); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestContainsHelppackageFlags) { + flags::SetProgramInvocationName("usage_config_test"); + + auto config = flags::GetUsageConfig(); + EXPECT_TRUE(config.contains_helppackage_flags("aaa/usage_config_test.cc")); + EXPECT_TRUE( + config.contains_helppackage_flags("bbdir/aaa/usage_config_test-main.cc")); + EXPECT_TRUE(config.contains_helppackage_flags( + "//aqswde/aaa/usage_config_test_main.cc")); + EXPECT_FALSE(config.contains_helppackage_flags("aadir/usage_config_main.cc")); + + absl::FlagsUsageConfig empty_config; + empty_config.contains_helppackage_flags = &TstContainsHelppackageFlags; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_TRUE( + flags::GetUsageConfig().contains_helppackage_flags("aaa/main-body.c")); + EXPECT_FALSE( + flags::GetUsageConfig().contains_helppackage_flags("aadir/main-body.c")); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestVersionString) { + flags::SetProgramInvocationName("usage_config_test"); + +#ifdef NDEBUG + std::string expected_output = "usage_config_test\n"; +#else + std::string expected_output = + "usage_config_test\nDebug build (NDEBUG not #defined)\n"; +#endif + + EXPECT_EQ(flags::GetUsageConfig().version_string(), expected_output); + + absl::FlagsUsageConfig empty_config; + empty_config.version_string = &TstVersionString; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_EQ(flags::GetUsageConfig().version_string(), "program 1.0.0"); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestNormalizeFilename) { + // This tests the default implementation. + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("/a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("///a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("/"), ""); + + // This tests that the custom implementation is called. + absl::FlagsUsageConfig empty_config; + empty_config.normalize_filename = &TstNormalizeFilename; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("a/a.cc"), "a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("aaa/a.cc"), "a/a.cc"); + + // This tests that the default implementation is called. + empty_config.normalize_filename = nullptr; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("/a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("///a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("\\a\\a.cc"), "a\\a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("//"), ""); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("\\\\"), ""); +} + +} // namespace diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index 4f7c94ce..8c2daf70 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,9 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -32,6 +33,7 @@ cc_library( ], hdrs = ["hash.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":city", "//absl/base:core_headers", @@ -50,6 +52,7 @@ cc_library( name = "hash_testing", testonly = 1, hdrs = ["hash_testing.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":spy_hash_state", "//absl/meta:type_traits", @@ -63,6 +66,7 @@ cc_test( name = "hash_test", srcs = ["hash_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash", ":hash_testing", @@ -80,6 +84,7 @@ cc_library( testonly = 1, hdrs = ["internal/spy_hash_state.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ ":hash", @@ -95,6 +100,7 @@ cc_library( "internal/city.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", "//absl/base:core_headers", @@ -106,6 +112,7 @@ cc_test( name = "city_test", srcs = ["internal/city_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":city", "@com_google_googletest//:gtest_main", diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt index 8f97d7cc..febc551f 100644 --- a/absl/hash/CMakeLists.txt +++ b/absl/hash/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -43,6 +43,8 @@ absl_cc_library( hash_testing HDRS "hash_testing.h" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::spy_hash_state absl::meta @@ -56,7 +58,9 @@ absl_cc_test( NAME hash_test SRCS - "hash_test.cc" + "hash_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash absl::hash_testing diff --git a/absl/hash/hash.h b/absl/hash/hash.h index 2c8982b8..d36f0960 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -36,25 +36,34 @@ // framework by simply combining its state with the state of known, hashable // types. Hashing of that combined state is separately done by `absl::Hash`. // +// One should assume that a hash algorithm is chosen randomly at the start of +// each process. E.g., absl::Hash()(9) in one process and +// absl::Hash()(9) in another process are likely to differ. +// // Example: // -// // Suppose we have a class `Circle` for which we want to add hashing +// // Suppose we have a class `Circle` for which we want to add hashing: // class Circle { -// public: -// ... -// private: -// std::pair center_; -// int radius_; -// }; -// -// // To add hashing support to `Circle`, we simply need to add an ordinary -// // function `AbslHashValue()`, and return the combined hash state of the -// // existing hash state and the class state: +// public: +// ... +// private: +// std::pair center_; +// int radius_; +// }; // +// // To add hashing support to `Circle`, we simply need to add a free +// // (non-member) function `AbslHashValue()`, and return the combined hash +// // state of the existing hash state and the class state. You can add such a +// // free function using a friend declaration within the body of the class: +// class Circle { +// public: +// ... // template // friend H AbslHashValue(H h, const Circle& c) { // return H::combine(std::move(h), c.center_, c.radius_); // } +// ... +// }; // // For more information, see Adding Type Support to `absl::Hash` below. // @@ -64,7 +73,7 @@ #include "absl/hash/internal/hash.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // `absl::Hash` @@ -236,7 +245,7 @@ using Hash = absl::hash_internal::Hash; // } // private: // virtual void HashValue(absl::HashState state) const = 0; -// }; +// }; // // class Impl : Interface { // private: @@ -244,7 +253,7 @@ using Hash = absl::hash_internal::Hash; // absl::HashState::combine(std::move(state), v1_, v2_); // } // int v1_; -// string v2_; +// std::string v2_; // }; class HashState : public hash_internal::HashStateBase { public: @@ -309,6 +318,7 @@ class HashState : public hash_internal::HashStateBase { void (*combine_contiguous_)(void*, const unsigned char*, size_t); }; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_HASH_HASH_H_ diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 4a1a98d5..2a4e2262 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,6 +15,7 @@ #include "absl/hash/hash.h" #include +#include #include #include #include @@ -50,7 +51,7 @@ using absl::hash_internal::SpyHashState; template class HashValueIntTest : public testing::Test { }; -TYPED_TEST_CASE_P(HashValueIntTest); +TYPED_TEST_SUITE_P(HashValueIntTest); template SpyHashState SpyHash(const T& value) { @@ -84,11 +85,349 @@ using IntTypes = testing::Types; INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueIntTest, IntTypes); +enum LegacyEnum { kValue1, kValue2, kValue3 }; + +enum class EnumClass { kValue4, kValue5, kValue6 }; + +TEST(HashValueTest, EnumAndBool) { + EXPECT_TRUE((is_hashable::value)); + EXPECT_TRUE((is_hashable::value)); + EXPECT_TRUE((is_hashable::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + LegacyEnum::kValue1, LegacyEnum::kValue2, LegacyEnum::kValue3))); + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + EnumClass::kValue4, EnumClass::kValue5, EnumClass::kValue6))); + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(true, false))); +} + +TEST(HashValueTest, FloatingPoint) { + EXPECT_TRUE((is_hashable::value)); + EXPECT_TRUE((is_hashable::value)); + EXPECT_TRUE((is_hashable::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(42.f, 0.f, -0.f, std::numeric_limits::infinity(), + -std::numeric_limits::infinity()))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(42., 0., -0., std::numeric_limits::infinity(), + -std::numeric_limits::infinity()))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + // Add some values with small exponent to test that NORMAL values also + // append their category. + .5L, 1.L, 2.L, 4.L, 42.L, 0.L, -0.L, + 17 * static_cast(std::numeric_limits::max()), + std::numeric_limits::infinity(), + -std::numeric_limits::infinity()))); +} + +TEST(HashValueTest, Pointer) { + EXPECT_TRUE((is_hashable::value)); + + int i; + int* ptr = &i; + int* n = nullptr; + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(&i, ptr, nullptr, ptr + 1, n))); +} + +TEST(HashValueTest, PointerAlignment) { + // We want to make sure that pointer alignment will not cause bits to be + // stuck. + + constexpr size_t kTotalSize = 1 << 20; + std::unique_ptr data(new char[kTotalSize]); + constexpr size_t kLog2NumValues = 5; + constexpr size_t kNumValues = 1 << kLog2NumValues; + + for (size_t align = 1; align < kTotalSize / kNumValues; + align < 8 ? align += 1 : align < 1024 ? align += 8 : align += 32) { + SCOPED_TRACE(align); + ASSERT_LE(align * kNumValues, kTotalSize); + + size_t bits_or = 0; + size_t bits_and = ~size_t{}; + + for (size_t i = 0; i < kNumValues; ++i) { + size_t hash = absl::Hash()(data.get() + i * align); + bits_or |= hash; + bits_and &= hash; + } + + // Limit the scope to the bits we would be using for Swisstable. + constexpr size_t kMask = (1 << (kLog2NumValues + 7)) - 1; + size_t stuck_bits = (~bits_or | bits_and) & kMask; + EXPECT_EQ(stuck_bits, 0) << "0x" << std::hex << stuck_bits; + } +} + +TEST(HashValueTest, PairAndTuple) { + EXPECT_TRUE((is_hashable>::value)); + EXPECT_TRUE((is_hashable>::value)); + EXPECT_TRUE((is_hashable>::value)); + EXPECT_TRUE((is_hashable>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::make_pair(0, 42), std::make_pair(0, 42), std::make_pair(42, 0), + std::make_pair(0, 0), std::make_pair(42, 42), std::make_pair(1, 42)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(std::make_tuple(0, 0, 0), std::make_tuple(0, 0, 42), + std::make_tuple(0, 23, 0), std::make_tuple(17, 0, 0), + std::make_tuple(42, 0, 0), std::make_tuple(3, 9, 9), + std::make_tuple(0, 0, -42)))); + + // Test that tuples of lvalue references work (so we need a few lvalues): + int a = 0, b = 1, c = 17, d = 23; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::tie(a, a), std::tie(a, b), std::tie(b, c), std::tie(c, d)))); + + // Test that tuples of rvalue references work: + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::forward_as_tuple(0, 0, 0), std::forward_as_tuple(0, 0, 42), + std::forward_as_tuple(0, 23, 0), std::forward_as_tuple(17, 0, 0), + std::forward_as_tuple(42, 0, 0), std::forward_as_tuple(3, 9, 9), + std::forward_as_tuple(0, 0, -42)))); +} + +TEST(HashValueTest, CombineContiguousWorks) { + std::vector> v1 = {std::make_tuple(1), std::make_tuple(3)}; + std::vector> v2 = {std::make_tuple(1), std::make_tuple(2)}; + + auto vh1 = SpyHash(v1); + auto vh2 = SpyHash(v2); + EXPECT_NE(vh1, vh2); +} + +struct DummyDeleter { + template + void operator() (T* ptr) {} +}; + +struct SmartPointerEq { + template + bool operator()(const T& t, const U& u) const { + return GetPtr(t) == GetPtr(u); + } + + template + static auto GetPtr(const T& t) -> decltype(&*t) { + return t ? &*t : nullptr; + } + + static std::nullptr_t GetPtr(std::nullptr_t) { return nullptr; } +}; + +TEST(HashValueTest, SmartPointers) { + EXPECT_TRUE((is_hashable>::value)); + EXPECT_TRUE((is_hashable>::value)); + EXPECT_TRUE((is_hashable>::value)); + + int i, j; + std::unique_ptr unique1(&i); + std::unique_ptr unique2(&i); + std::unique_ptr unique_other(&j); + std::unique_ptr unique_null; + + std::shared_ptr shared1(&i, DummyDeleter()); + std::shared_ptr shared2(&i, DummyDeleter()); + std::shared_ptr shared_other(&j, DummyDeleter()); + std::shared_ptr shared_null; + + // Sanity check of the Eq function. + ASSERT_TRUE(SmartPointerEq{}(unique1, shared1)); + ASSERT_FALSE(SmartPointerEq{}(unique1, shared_other)); + ASSERT_TRUE(SmartPointerEq{}(unique_null, nullptr)); + ASSERT_FALSE(SmartPointerEq{}(shared2, nullptr)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::forward_as_tuple(&i, nullptr, // + unique1, unique2, unique_null, // + absl::make_unique(), // + shared1, shared2, shared_null, // + std::make_shared()), + SmartPointerEq{})); +} + +TEST(HashValueTest, FunctionPointer) { + using Func = int (*)(); + EXPECT_TRUE(is_hashable::value); + + Func p1 = [] { return 2; }, p2 = [] { return 1; }; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(p1, p2, nullptr))); +} + +struct WrapInTuple { + template + std::tuple operator()(const T& t) const { + return std::make_tuple(7, t, 0xdeadbeef); + } +}; + +TEST(HashValueTest, Strings) { + EXPECT_TRUE((is_hashable::value)); + + const std::string small = "foo"; + const std::string dup = "foofoo"; + const std::string large = "large"; + const std::string huge = std::string(5000, 'a'); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::string(), absl::string_view(), + std::string(""), absl::string_view(""), + std::string(small), absl::string_view(small), + std::string(dup), absl::string_view(dup), + std::string(large), absl::string_view(large), + std::string(huge), absl::string_view(huge)))); + + // Also check that nested types maintain the same hash. + const WrapInTuple t{}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + t(std::string()), t(absl::string_view()), + t(std::string("")), t(absl::string_view("")), + t(std::string(small)), t(absl::string_view(small)), + t(std::string(dup)), t(absl::string_view(dup)), + t(std::string(large)), t(absl::string_view(large)), + t(std::string(huge)), t(absl::string_view(huge))))); + + // Make sure that hashing a `const char*` does not use its std::string-value. + EXPECT_NE(SpyHash(static_cast("ABC")), + SpyHash(absl::string_view("ABC"))); +} + +TEST(HashValueTest, StdArray) { + EXPECT_TRUE((is_hashable>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(std::array{}, std::array{{0, 23, 42}}))); +} + +TEST(HashValueTest, StdBitset) { + EXPECT_TRUE((is_hashable>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + {std::bitset<2>("00"), std::bitset<2>("01"), std::bitset<2>("10"), + std::bitset<2>("11")})); + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + {std::bitset<5>("10101"), std::bitset<5>("10001"), std::bitset<5>()})); + + constexpr int kNumBits = 256; + std::array bit_strings; + bit_strings.fill(std::string(kNumBits, '1')); + bit_strings[1][0] = '0'; + bit_strings[2][1] = '0'; + bit_strings[3][kNumBits / 3] = '0'; + bit_strings[4][kNumBits - 2] = '0'; + bit_strings[5][kNumBits - 1] = '0'; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + {std::bitset(bit_strings[0].c_str()), + std::bitset(bit_strings[1].c_str()), + std::bitset(bit_strings[2].c_str()), + std::bitset(bit_strings[3].c_str()), + std::bitset(bit_strings[4].c_str()), + std::bitset(bit_strings[5].c_str())})); +} // namespace + +template +class HashValueSequenceTest : public testing::Test { +}; +TYPED_TEST_SUITE_P(HashValueSequenceTest); + +TYPED_TEST_P(HashValueSequenceTest, BasicUsage) { + EXPECT_TRUE((is_hashable::value)); + + using ValueType = typename TypeParam::value_type; + auto a = static_cast(0); + auto b = static_cast(23); + auto c = static_cast(42); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(TypeParam(), TypeParam{}, TypeParam{a, b, c}, + TypeParam{a, b}, TypeParam{b, c}))); +} + +REGISTER_TYPED_TEST_CASE_P(HashValueSequenceTest, BasicUsage); +using IntSequenceTypes = + testing::Types, std::forward_list, std::list, + std::vector, std::vector, std::set, + std::multiset>; +INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueSequenceTest, IntSequenceTypes); + +// Private type that only supports AbslHashValue to make sure our chosen hash +// implentation is recursive within absl::Hash. +// It uses std::abs() on the value to provide different bitwise representations +// of the same logical value. +struct Private { + int i; + template + friend H AbslHashValue(H h, Private p) { + return H::combine(std::move(h), std::abs(p.i)); + } + + friend bool operator==(Private a, Private b) { + return std::abs(a.i) == std::abs(b.i); + } + + friend std::ostream& operator<<(std::ostream& o, Private p) { + return o << p.i; + } +}; + +TEST(HashValueTest, PrivateSanity) { + // Sanity check that Private is working as the tests below expect it to work. + EXPECT_TRUE(is_hashable::value); + EXPECT_NE(SpyHash(Private{0}), SpyHash(Private{1})); + EXPECT_EQ(SpyHash(Private{1}), SpyHash(Private{1})); +} + +TEST(HashValueTest, Optional) { + EXPECT_TRUE(is_hashable>::value); + + using O = absl::optional; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(O{}, O{{1}}, O{{-1}}, O{{10}}))); +} + +TEST(HashValueTest, Variant) { + using V = absl::variant; + EXPECT_TRUE(is_hashable::value); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + V(Private{1}), V(Private{-1}), V(Private{2}), V("ABC"), V("BCD")))); + +#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ + struct S {}; + EXPECT_FALSE(is_hashable>::value); +#endif +} + +TEST(HashValueTest, Maps) { + EXPECT_TRUE((is_hashable>::value)); + + using M = std::map; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + M{}, M{{0, "foo"}}, M{{1, "foo"}}, M{{0, "bar"}}, M{{1, "bar"}}, + M{{0, "foo"}, {42, "bar"}}, M{{1, "foo"}, {42, "bar"}}, + M{{1, "foo"}, {43, "bar"}}, M{{1, "foo"}, {43, "baz"}}))); + + using MM = std::multimap; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + MM{}, MM{{0, "foo"}}, MM{{1, "foo"}}, MM{{0, "bar"}}, MM{{1, "bar"}}, + MM{{0, "foo"}, {0, "bar"}}, MM{{0, "bar"}, {0, "foo"}}, + MM{{0, "foo"}, {42, "bar"}}, MM{{1, "foo"}, {42, "bar"}}, + MM{{1, "foo"}, {1, "foo"}, {43, "bar"}}, MM{{1, "foo"}, {43, "baz"}}))); +} + template -struct IsHashCallble : std::false_type {}; +struct IsHashCallable : std::false_type {}; template -struct IsHashCallble>()( +struct IsHashCallable>()( std::declval()))>> : std::true_type {}; template @@ -105,10 +444,11 @@ TEST(IsHashableTest, ValidHash) { EXPECT_TRUE(std::is_move_constructible>::value); EXPECT_TRUE(absl::is_copy_assignable>::value); EXPECT_TRUE(absl::is_move_assignable>::value); - EXPECT_TRUE(IsHashCallble::value); + EXPECT_TRUE(IsHashCallable::value); EXPECT_TRUE(IsAggregateInitializable>::value); } -#if ABSL_HASH_INTERNAL_CAN_POISON_ && !defined(__APPLE__) + +#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ TEST(IsHashableTest, PoisonHash) { struct X {}; EXPECT_FALSE((is_hashable::value)); @@ -117,10 +457,10 @@ TEST(IsHashableTest, PoisonHash) { EXPECT_FALSE(std::is_move_constructible>::value); EXPECT_FALSE(absl::is_copy_assignable>::value); EXPECT_FALSE(absl::is_move_assignable>::value); - EXPECT_FALSE(IsHashCallble::value); + EXPECT_FALSE(IsHashCallable::value); EXPECT_FALSE(IsAggregateInitializable>::value); } -#endif // ABSL_HASH_INTERNAL_CAN_POISON_ +#endif // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ // Hashable types // @@ -129,7 +469,7 @@ TEST(IsHashableTest, PoisonHash) { struct NoOp { template friend HashCode AbslHashValue(HashCode h, NoOp n) { - return std::move(h); + return h; } }; @@ -159,8 +499,16 @@ struct CombineVariadic { Int(4)); } }; +enum class InvokeTag { + kUniquelyRepresented, + kHashValue, +#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ + kLegacyHash, +#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ + kStdHash, + kNone +}; -using InvokeTag = absl::hash_internal::InvokeHashTag; template using InvokeTagConstant = std::integral_constant; @@ -175,6 +523,7 @@ struct MinTag : InvokeTagConstant {}; template struct CustomHashType { + explicit CustomHashType(size_t val) : value(val) {} size_t value; }; @@ -195,7 +544,7 @@ H AbslHashValue(H state, CustomHashType t) { } // namespace namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { template struct is_uniquely_represented< @@ -203,7 +552,7 @@ struct is_uniquely_represented< typename EnableIfContained::type> : std::true_type {}; } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ @@ -243,17 +592,17 @@ void TestCustomHashType(InvokeTagConstant, T...) { EXPECT_TRUE(is_hashable()); const size_t offset = static_cast(std::min({T::value...})); - EXPECT_EQ(SpyHash(type{7}), SpyHash(size_t{7 + offset})); + EXPECT_EQ(SpyHash(type(7)), SpyHash(size_t{7 + offset})); } void TestCustomHashType(InvokeTagConstant) { -#if ABSL_HASH_INTERNAL_CAN_POISON_ +#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ // is_hashable is false if we don't support any of the hooks. using type = CustomHashType<>; EXPECT_FALSE(is_hashable()); EXPECT_FALSE(is_hashable()); EXPECT_FALSE(is_hashable()); -#endif // ABSL_HASH_INTERNAL_CAN_POISON_ +#endif // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ } template @@ -275,7 +624,7 @@ TEST(HashTest, NoOpsAreEquivalent) { template class HashIntTest : public testing::Test { }; -TYPED_TEST_CASE_P(HashIntTest); +TYPED_TEST_SUITE_P(HashIntTest); TYPED_TEST_P(HashIntTest, BasicUsage) { EXPECT_NE(Hash()({}), Hash()(0)); @@ -354,7 +703,8 @@ TEST(HashTest, HashNonUniquelyRepresentedType) { } TEST(HashTest, StandardHashContainerUsage) { - std::unordered_map> map = {{0, "foo"}, { 42, "bar" }}; + std::unordered_map> map = {{0, "foo"}, + {42, "bar"}}; EXPECT_NE(map.find(0), map.end()); EXPECT_EQ(map.find(1), map.end()); @@ -424,4 +774,24 @@ TEST(HashTest, TypeErased) { SpyHash(std::make_pair(size_t{7}, 17))); } +struct ValueWithBoolConversion { + operator bool() const { return false; } + int i; +}; + +} // namespace +namespace std { +template <> +struct hash { + size_t operator()(ValueWithBoolConversion v) { return v.i; } +}; +} // namespace std + +namespace { + +TEST(HashTest, DoesNotUseImplicitConversionsToBool) { + EXPECT_NE(absl::Hash()(ValueWithBoolConversion{0}), + absl::Hash()(ValueWithBoolConversion{1})); +} + } // namespace diff --git a/absl/hash/hash_testing.h b/absl/hash/hash_testing.h index 1c4db26a..6e39028f 100644 --- a/absl/hash/hash_testing.h +++ b/absl/hash/hash_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/types/variant.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Run the absl::Hash algorithm over all the elements passed in and verify that // their hash expansion is congruent with their `==` operator. @@ -191,7 +191,9 @@ VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) { struct Info { const V& value; size_t index; - std::string ToString() const { return absl::visit(PrintVisitor{index}, value); } + std::string ToString() const { + return absl::visit(PrintVisitor{index}, value); + } SpyHashState expand() const { return absl::visit(ExpandVisitor{}, value); } }; @@ -370,7 +372,7 @@ VerifyTypeImplementsAbslHashCorrectly(std::initializer_list values, equals); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HASH_HASH_TESTING_H_ diff --git a/absl/hash/internal/city.cc b/absl/hash/internal/city.cc index 5c076fb5..c7ad1591 100644 --- a/absl/hash/internal/city.cc +++ b/absl/hash/internal/city.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,7 +30,7 @@ #include "absl/base/optimization.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/hash/internal/city.h b/absl/hash/internal/city.h index 46c18ffa..7586ba08 100644 --- a/absl/hash/internal/city.h +++ b/absl/hash/internal/city.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// http://code.google.com/p/cityhash/ +// https://code.google.com/p/cityhash/ // // This file provides a few functions for hashing strings. All of them are // high-quality functions in the sense that they pass standard tests such @@ -49,9 +49,8 @@ #include // for size_t. #include - namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { typedef std::pair uint128; @@ -88,7 +87,7 @@ inline uint64_t Hash128to64(const uint128 &x) { } } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 f305ed9e..1b9373c2 100644 --- a/absl/hash/internal/city_test.cc +++ b/absl/hash/internal/city_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; @@ -591,5 +591,5 @@ TEST(CityHashTest, Unchanging) { } } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc index 3e553625..087b389b 100644 --- a/absl/hash/internal/hash.cc +++ b/absl/hash/internal/hash.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,11 +15,11 @@ #include "absl/hash/internal/hash.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed; } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index a51ca954..81f1edf8 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -50,7 +50,7 @@ #include "absl/hash/internal/city.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { // HashStateBase @@ -222,7 +222,9 @@ typename std::enable_if::value, H>::type AbslHashValue( } // AbslHashValue() for hashing floating-point values template -typename std::enable_if::value, H>::type +typename std::enable_if::value || + std::is_same::value, + H>::type AbslHashValue(H hash_state, Float value) { return hash_internal::hash_bytes(std::move(hash_state), value == 0 ? 0 : value); @@ -232,8 +234,9 @@ AbslHashValue(H hash_state, Float value) { // For example, in x86 sizeof(long double)==16 but it only really uses 80-bits // of it. This means we can't use hash_bytes on a long double and have to // convert it to something else first. -template -H AbslHashValue(H hash_state, long double value) { +template +typename std::enable_if::value, H>::type +AbslHashValue(H hash_state, LongDouble value) { const int category = std::fpclassify(value); switch (category) { case FP_INFINITE: @@ -265,7 +268,12 @@ H AbslHashValue(H hash_state, long double value) { // AbslHashValue() for hashing pointers template H AbslHashValue(H hash_state, T* ptr) { - return hash_internal::hash_bytes(std::move(hash_state), ptr); + auto v = reinterpret_cast(ptr); + // Due to alignment, pointers tend to have low bits as zero, and the next few + // bits follow a pattern since they are also multiples of some base value. + // Mixing the pointer twice helps prevent stuck low bits for certain alignment + // values. + return H::combine(std::move(hash_state), v, v); } // AbslHashValue() for hashing nullptr_t @@ -528,53 +536,22 @@ hash_range_or_bytes(H hash_state, const T* data, size_t size) { return hash_state; } -// InvokeHashTag -// -// InvokeHash(H, const T&) invokes the appropriate hash implementation for a -// hasher of type `H` and a value of type `T`. If `T` is not hashable, there -// will be no matching overload of InvokeHash(). -// Note: Some platforms (eg MSVC) do not support the detect idiom on -// std::hash. In those platforms the last fallback will be std::hash and -// InvokeHash() will always have a valid overload even if std::hash is not -// valid. -// -// We try the following options in order: -// * If is_uniquely_represented, hash bytes directly. -// * ADL AbslHashValue(H, const T&) call. -// * std::hash - -// In MSVC we can't probe std::hash or stdext::hash because it triggers a -// static_assert instead of failing substitution. -#if defined(_MSC_VER) -#define ABSL_HASH_INTERNAL_CAN_POISON_ 0 -#else // _MSC_VER -#define ABSL_HASH_INTERNAL_CAN_POISON_ 1 -#endif // _MSC_VER - #if defined(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE) && \ - ABSL_HASH_INTERNAL_CAN_POISON_ + ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ #define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 1 #else #define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 0 #endif -enum class InvokeHashTag { - kUniquelyRepresented, - kHashValue, -#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ - kLegacyHash, -#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ - kStdHash, - kNone -}; - // HashSelect // // Type trait to select the appropriate hash implementation to use. -// HashSelect::value is an instance of InvokeHashTag that indicates the best -// available hashing mechanism. -// See `Note` above about MSVC. -template +// HashSelect::type will give the proper hash implementation, to be invoked +// as: +// HashSelect::type::Invoke(state, value) +// Also, HashSelect::type::value is a boolean equal to `true` if there is a +// valid `Invoke` function. Types that are not hashable will have a ::value of +// `false`. struct HashSelect { private: struct State : HashStateBase { @@ -583,95 +560,75 @@ struct HashSelect { using State::HashStateBase::combine_contiguous; }; - // `Probe::value` evaluates to `V::value` if it is a valid - // expression, and `false` otherwise. - // `Probe::tag` always evaluates to `Tag`. - template