From 6c7de165d1c82684359ccb630bb5f83263fa5ebc Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 20 Jun 2018 06:25:23 -0700 Subject: [PATCH 1/5] Project import generated by Copybara. GitOrigin-RevId: d89dba27e35462d7457121b978fd79214205e686 Change-Id: I0eae80578a93a580820bc90d42e6b42faf7fde0a --- LTS.md | 13 + WORKSPACE | 17 +- absl/algorithm/algorithm.h | 2 + absl/algorithm/container.h | 2 + absl/base/BUILD.bazel | 5 - absl/base/bit_cast_test.cc | 2 + absl/base/call_once.h | 2 + absl/base/call_once_test.cc | 2 + absl/base/casts.h | 2 + absl/base/inline_variable_test.cc | 2 + absl/base/inline_variable_test_a.cc | 2 + absl/base/inline_variable_test_b.cc | 2 + absl/base/internal/atomic_hook.h | 2 + absl/base/internal/cycleclock.cc | 2 + absl/base/internal/cycleclock.h | 2 + absl/base/internal/direct_mmap.h | 4 + absl/base/internal/endian.h | 2 + absl/base/internal/endian_test.cc | 2 + absl/base/internal/hide_ptr.h | 2 + absl/base/internal/identity.h | 6 +- absl/base/internal/inline_variable_testing.h | 2 + absl/base/internal/invoke.h | 2 + absl/base/internal/low_level_alloc.cc | 2 + absl/base/internal/low_level_alloc.h | 2 + absl/base/internal/low_level_alloc_test.cc | 2 + absl/base/internal/low_level_scheduling.h | 2 + absl/base/internal/raw_logging.cc | 2 + absl/base/internal/raw_logging.h | 2 + absl/base/internal/scheduling_mode.h | 2 + absl/base/internal/spinlock.cc | 2 + absl/base/internal/spinlock.h | 2 + absl/base/internal/spinlock_wait.cc | 2 + absl/base/internal/spinlock_wait.h | 4 +- absl/base/internal/sysinfo.cc | 2 + absl/base/internal/sysinfo.h | 2 + absl/base/internal/sysinfo_test.cc | 2 + absl/base/internal/thread_identity.cc | 2 + absl/base/internal/thread_identity.h | 2 + absl/base/internal/thread_identity_test.cc | 2 + absl/base/internal/throw_delegate.cc | 2 + absl/base/internal/throw_delegate.h | 2 + absl/base/internal/unaligned_access.h | 8 + absl/base/internal/unscaledcycleclock.cc | 2 + absl/base/internal/unscaledcycleclock.h | 2 + absl/base/invoke_test.cc | 2 + absl/base/log_severity.h | 2 + absl/base/macros.h | 9 +- absl/base/spinlock_test_common.cc | 2 + absl/container/fixed_array.h | 2 + absl/container/inlined_vector.h | 2 + absl/container/inlined_vector_benchmark.cc | 37 +- .../internal/test_instance_tracker.cc | 2 + .../internal/test_instance_tracker.h | 2 + absl/debugging/failure_signal_handler.cc | 2 + absl/debugging/failure_signal_handler.h | 2 + .../debugging/internal/address_is_readable.cc | 4 + absl/debugging/internal/address_is_readable.h | 2 + absl/debugging/internal/demangle.cc | 2 + absl/debugging/internal/demangle.h | 2 + absl/debugging/internal/demangle_test.cc | 2 + absl/debugging/internal/elf_mem_image.cc | 2 + absl/debugging/internal/elf_mem_image.h | 2 + absl/debugging/internal/examine_stack.cc | 2 + absl/debugging/internal/examine_stack.h | 2 + absl/debugging/internal/stack_consumption.cc | 2 + absl/debugging/internal/stack_consumption.h | 2 + .../internal/stack_consumption_test.cc | 2 + .../internal/stacktrace_aarch64-inl.inc | 2 + .../debugging/internal/stacktrace_arm-inl.inc | 2 + .../internal/stacktrace_generic-inl.inc | 2 + .../internal/stacktrace_powerpc-inl.inc | 2 + .../internal/stacktrace_unimplemented-inl.inc | 2 + .../internal/stacktrace_win32-inl.inc | 2 + .../debugging/internal/stacktrace_x86-inl.inc | 2 + absl/debugging/internal/symbolize.h | 4 + absl/debugging/internal/vdso_support.cc | 2 + absl/debugging/internal/vdso_support.h | 2 + absl/debugging/leak_check.cc | 4 + absl/debugging/leak_check.h | 2 + absl/debugging/stacktrace.cc | 2 + absl/debugging/stacktrace.h | 2 + absl/debugging/symbolize.h | 2 + absl/debugging/symbolize_elf.inc | 2 + absl/debugging/symbolize_unimplemented.inc | 2 + absl/debugging/symbolize_win32.inc | 2 + absl/memory/BUILD.bazel | 14 + absl/memory/CMakeLists.txt | 19 + absl/memory/memory.h | 2 + absl/memory/memory_exception_safety_test.cc | 51 + absl/meta/type_traits.h | 2 + absl/numeric/int128.cc | 2 + absl/numeric/int128.h | 2 + absl/strings/BUILD.bazel | 74 +- absl/strings/CMakeLists.txt | 44 + absl/strings/ascii.cc | 2 + absl/strings/ascii.h | 2 + absl/strings/charconv.cc | 984 ++++++++++++++++++ absl/strings/charconv.h | 117 +++ absl/strings/charconv_benchmark.cc | 204 ++++ absl/strings/charconv_test.cc | 766 ++++++++++++++ absl/strings/escaping.cc | 2 + absl/strings/escaping.h | 2 + absl/strings/internal/bits.h | 2 + absl/strings/internal/char_map.h | 2 + absl/strings/internal/charconv_bigint.cc | 359 +++++++ absl/strings/internal/charconv_bigint.h | 428 ++++++++ absl/strings/internal/charconv_bigint_test.cc | 205 ++++ absl/strings/internal/charconv_parse.cc | 498 +++++++++ absl/strings/internal/charconv_parse.h | 98 ++ absl/strings/internal/charconv_parse_test.cc | 357 +++++++ absl/strings/internal/escaping_test_common.h | 2 + absl/strings/internal/memutil.cc | 2 + absl/strings/internal/memutil.h | 2 + absl/strings/internal/numbers_test_common.h | 2 + absl/strings/internal/ostringstream.cc | 2 + absl/strings/internal/ostringstream.h | 2 + absl/strings/internal/resize_uninitialized.h | 2 + absl/strings/internal/stl_type_traits.h | 2 + absl/strings/internal/str_join_internal.h | 2 + absl/strings/internal/str_split_internal.h | 2 + absl/strings/internal/utf8.cc | 2 + absl/strings/internal/utf8.h | 2 + absl/strings/match.cc | 2 + absl/strings/match.h | 2 + absl/strings/numbers.cc | 88 +- absl/strings/numbers.h | 4 + absl/strings/str_cat.cc | 2 + absl/strings/str_cat.h | 2 + absl/strings/str_join.h | 2 + absl/strings/str_replace.cc | 2 + absl/strings/str_replace.h | 2 + absl/strings/str_split.cc | 2 + absl/strings/str_split.h | 2 + absl/strings/string_view.cc | 2 + absl/strings/string_view.h | 6 + absl/strings/strip.h | 2 + absl/strings/substitute.cc | 2 + absl/strings/substitute.h | 2 + absl/synchronization/BUILD.bazel | 6 - absl/synchronization/barrier.cc | 2 + absl/synchronization/barrier.h | 2 + absl/synchronization/blocking_counter.cc | 2 + absl/synchronization/blocking_counter.h | 2 + absl/synchronization/blocking_counter_test.cc | 2 + .../internal/create_thread_identity.cc | 2 + .../internal/create_thread_identity.h | 2 + absl/synchronization/internal/graphcycles.cc | 2 + absl/synchronization/internal/graphcycles.h | 2 + .../internal/graphcycles_test.cc | 2 + .../synchronization/internal/kernel_timeout.h | 2 + .../synchronization/internal/mutex_nonprod.cc | 2 + .../internal/mutex_nonprod.inc | 2 + .../internal/per_thread_sem.cc | 2 + .../synchronization/internal/per_thread_sem.h | 2 + .../internal/per_thread_sem_test.cc | 2 + absl/synchronization/internal/thread_pool.h | 2 + absl/synchronization/internal/waiter.cc | 2 + absl/synchronization/internal/waiter.h | 2 + absl/synchronization/mutex.cc | 2 + absl/synchronization/mutex.h | 2 + absl/synchronization/notification.cc | 2 + absl/synchronization/notification.h | 2 + absl/synchronization/notification_test.cc | 2 + absl/time/BUILD.bazel | 4 +- absl/time/CMakeLists.txt | 2 +- absl/time/clock.cc | 8 + absl/time/clock.h | 2 + absl/time/duration.cc | 5 +- absl/time/format.cc | 3 +- .../internal/cctz/include/cctz/civil_time.h | 2 + .../cctz/include/cctz/civil_time_detail.h | 2 + .../internal/cctz/include/cctz/time_zone.h | 2 + .../cctz/include/cctz/zone_info_source.h | 4 + .../internal/cctz/src/civil_time_detail.cc | 2 + .../time/internal/cctz/src/civil_time_test.cc | 2 + .../time/internal/cctz/src/time_zone_fixed.cc | 2 + absl/time/internal/cctz/src/time_zone_fixed.h | 2 + .../internal/cctz/src/time_zone_format.cc | 2 + .../cctz/src/time_zone_format_test.cc | 2 + absl/time/internal/cctz/src/time_zone_if.cc | 2 + absl/time/internal/cctz/src/time_zone_if.h | 2 + absl/time/internal/cctz/src/time_zone_impl.cc | 2 + absl/time/internal/cctz/src/time_zone_impl.h | 2 + absl/time/internal/cctz/src/time_zone_info.cc | 2 + absl/time/internal/cctz/src/time_zone_info.h | 2 + absl/time/internal/cctz/src/time_zone_libc.cc | 2 + absl/time/internal/cctz/src/time_zone_libc.h | 2 + .../internal/cctz/src/time_zone_lookup.cc | 2 + .../cctz/src/time_zone_lookup_test.cc | 2 + .../time/internal/cctz/src/time_zone_posix.cc | 2 + absl/time/internal/cctz/src/time_zone_posix.h | 2 + .../internal/cctz/src/zone_info_source.cc | 8 +- absl/time/internal/get_current_time_ios.inc | 2 + absl/time/internal/get_current_time_posix.inc | 2 + .../internal/get_current_time_windows.inc | 2 + absl/time/internal/test_util.cc | 4 + absl/time/internal/test_util.h | 2 + absl/time/time.cc | 2 + absl/time/time.h | 8 +- absl/types/BUILD.bazel | 13 + absl/types/any.h | 4 + absl/types/bad_any_cast.cc | 2 + absl/types/bad_any_cast.h | 2 + absl/types/bad_optional_access.cc | 2 + absl/types/bad_optional_access.h | 2 + absl/types/bad_variant_access.cc | 2 + absl/types/bad_variant_access.h | 2 + absl/types/internal/variant.h | 349 ++++++- absl/types/optional.cc | 2 + absl/types/optional.h | 6 +- absl/types/optional_exception_safety_test.cc | 2 + absl/types/span.h | 2 + absl/types/variant.h | 40 +- absl/types/variant_benchmark.cc | 222 ++++ absl/types/variant_exception_safety_test.cc | 2 + absl/types/variant_test.cc | 2 + absl/utility/utility.h | 2 + 217 files changed, 5284 insertions(+), 175 deletions(-) create mode 100644 LTS.md create mode 100644 absl/memory/memory_exception_safety_test.cc create mode 100644 absl/strings/charconv.cc create mode 100644 absl/strings/charconv.h create mode 100644 absl/strings/charconv_benchmark.cc create mode 100644 absl/strings/charconv_test.cc create mode 100644 absl/strings/internal/charconv_bigint.cc create mode 100644 absl/strings/internal/charconv_bigint.h create mode 100644 absl/strings/internal/charconv_bigint_test.cc create mode 100644 absl/strings/internal/charconv_parse.cc create mode 100644 absl/strings/internal/charconv_parse.h create mode 100644 absl/strings/internal/charconv_parse_test.cc create mode 100644 absl/types/variant_benchmark.cc diff --git a/LTS.md b/LTS.md new file mode 100644 index 00000000000..385b4f062bf --- /dev/null +++ b/LTS.md @@ -0,0 +1,13 @@ +# Long Term Support (LTS) Branches + +This repository contains periodic snapshots of the Abseil codebase that are +Long Term Support (LTS) branches. An LTS branch allows you to use a known +version of Abseil without interfering with other projects which may also, in +turn, use Abseil. (For more information about our releases, see the +[Abseil Release Management](https://abseil.io/about/releases) guide.) + +## LTS Branches + +The following lists LTS branches and the dates on which they have been released: + +* [LTS Branch June 20, 2018](https://github.com/abseil/abseil-cpp/tree/lts_2018_06_20/) diff --git a/WORKSPACE b/WORKSPACE index 9c950650872..a7b1c139290 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,13 +1,13 @@ workspace(name = "com_google_absl") # Bazel toolchains http_archive( - name = "bazel_toolchains", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/f8847f64e6950e8ab9fde1c0aba768550b0d9ab2.tar.gz", - "https://github.com/bazelbuild/bazel-toolchains/archive/f8847f64e6950e8ab9fde1c0aba768550b0d9ab2.tar.gz", - ], - strip_prefix = "bazel-toolchains-f8847f64e6950e8ab9fde1c0aba768550b0d9ab2", - sha256 = "794366f51fea224b3656a0b0f8f1518e739748646523a572fcd3d68614a0e670", + name = "bazel_toolchains", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/287b64e0a211fb7c23b74695f8d5f5205b61f4eb.tar.gz", + "https://github.com/bazelbuild/bazel-toolchains/archive/287b64e0a211fb7c23b74695f8d5f5205b61f4eb.tar.gz", + ], + strip_prefix = "bazel-toolchains-287b64e0a211fb7c23b74695f8d5f5205b61f4eb", + sha256 = "aca8ac6afd7745027ee4a43032b51a725a61a75a30f02cc58681ee87e4dcdf4b", ) # GoogleTest/GoogleMock framework. Used by most unit-tests. @@ -15,6 +15,7 @@ http_archive( name = "com_google_googletest", urls = ["https://github.com/google/googletest/archive/4e4df226fc197c0dda6e37f5c8c3845ca1e73a49.zip"], strip_prefix = "googletest-4e4df226fc197c0dda6e37f5c8c3845ca1e73a49", + sha256 = "d4179caf54410968d1fff0b869e7d74803dd30209ee6645ccf1ca65ab6cf5e5a", ) # Google benchmark. @@ -22,6 +23,7 @@ http_archive( name = "com_github_google_benchmark", urls = ["https://github.com/google/benchmark/archive/16703ff83c1ae6d53e5155df3bb3ab0bc96083be.zip"], strip_prefix = "benchmark-16703ff83c1ae6d53e5155df3bb3ab0bc96083be", + sha256 = "59f918c8ccd4d74b6ac43484467b500f1d64b40cc1010daa055375b322a43ba3", ) # RE2 regular-expression framework. Used by some unit-tests. @@ -29,4 +31,5 @@ http_archive( name = "com_googlesource_code_re2", urls = ["https://github.com/google/re2/archive/6cf8ccd82dbaab2668e9b13596c68183c9ecd13f.zip"], strip_prefix = "re2-6cf8ccd82dbaab2668e9b13596c68183c9ecd13f", + sha256 = "279a852219dbfc504501775596089d30e9c0b29664ce4128b0ac4c841471a16a", ) diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h index 3d6586439fe..3a8f2724210 100644 --- a/absl/algorithm/algorithm.h +++ b/absl/algorithm/algorithm.h @@ -27,6 +27,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace algorithm_internal { @@ -145,6 +146,7 @@ ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator>()); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_ALGORITHM_ALGORITHM_H_ diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index ebe3244555f..8eb10d7a143 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -54,6 +54,7 @@ #include "absl/meta/type_traits.h" namespace absl { +inline namespace lts_2018_06_20 { namespace container_algorithm_internal { @@ -1637,6 +1638,7 @@ OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, output_first, std::forward(op)); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_ALGORITHM_CONTAINER_H_ diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 1e93d97ebd4..d117a4fea86 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -371,11 +371,6 @@ cc_test( size = "small", srcs = ["internal/sysinfo_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_android_arm", - "no_test_android_arm64", - "no_test_android_x86", - ], deps = [ ":base", "//absl/synchronization", diff --git a/absl/base/bit_cast_test.cc b/absl/base/bit_cast_test.cc index 8cd878d756e..71bb368fb74 100644 --- a/absl/base/bit_cast_test.cc +++ b/absl/base/bit_cast_test.cc @@ -22,6 +22,7 @@ #include "absl/base/macros.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { template @@ -104,4 +105,5 @@ TEST(BitCast, Double) { } } // namespace +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/call_once.h b/absl/base/call_once.h index 532ee2e38bb..37b6608a8f3 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h @@ -39,6 +39,7 @@ #include "absl/base/port.h" namespace absl { +inline namespace lts_2018_06_20 { class once_flag; @@ -211,6 +212,7 @@ void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { } } +} // inline namespace lts_2018_06_20 } // 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 cd58ee19f08..43a71656629 100644 --- a/absl/base/call_once_test.cc +++ b/absl/base/call_once_test.cc @@ -22,6 +22,7 @@ #include "absl/synchronization/mutex.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { absl::once_flag once; @@ -99,4 +100,5 @@ TEST(CallOnceTest, ExecutionCount) { } } // namespace +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/casts.h b/absl/base/casts.h index 8bd5264d978..cd07d8f68e7 100644 --- a/absl/base/casts.h +++ b/absl/base/casts.h @@ -30,6 +30,7 @@ #include "absl/base/internal/identity.h" namespace absl { +inline namespace lts_2018_06_20 { // implicit_cast() // @@ -135,6 +136,7 @@ inline Dest bit_cast(const Source& source) { return dest; } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_BASE_CASTS_H_ diff --git a/absl/base/inline_variable_test.cc b/absl/base/inline_variable_test.cc index 5499189a3da..b34aebd814a 100644 --- a/absl/base/inline_variable_test.cc +++ b/absl/base/inline_variable_test.cc @@ -20,6 +20,7 @@ #include "gtest/gtest.h" namespace absl { +inline namespace lts_2018_06_20 { namespace inline_variable_testing_internal { namespace { @@ -59,4 +60,5 @@ TEST(InlineVariableTest, FunPtrType) { } // namespace } // namespace inline_variable_testing_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/inline_variable_test_a.cc b/absl/base/inline_variable_test_a.cc index a3bf3b68b3c..0ea363af2a7 100644 --- a/absl/base/inline_variable_test_a.cc +++ b/absl/base/inline_variable_test_a.cc @@ -15,6 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { +inline namespace lts_2018_06_20 { namespace inline_variable_testing_internal { const Foo& get_foo_a() { return inline_variable_foo; } @@ -22,4 +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_06_20 } // namespace absl diff --git a/absl/base/inline_variable_test_b.cc b/absl/base/inline_variable_test_b.cc index b4b9393a558..32704cf1091 100644 --- a/absl/base/inline_variable_test_b.cc +++ b/absl/base/inline_variable_test_b.cc @@ -15,6 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { +inline namespace lts_2018_06_20 { namespace inline_variable_testing_internal { const Foo& get_foo_b() { return inline_variable_foo; } @@ -22,4 +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_06_20 } // namespace absl diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h index b458511b0c7..7d0ee2fa67b 100644 --- a/absl/base/internal/atomic_hook.h +++ b/absl/base/internal/atomic_hook.h @@ -28,6 +28,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { template @@ -160,6 +161,7 @@ class AtomicHook { #undef ABSL_HAVE_WORKING_ATOMIC_POINTER } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc index a742df01f94..a4f1fc215c4 100644 --- a/absl/base/internal/cycleclock.cc +++ b/absl/base/internal/cycleclock.cc @@ -27,6 +27,7 @@ #include "absl/base/internal/unscaledcycleclock.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { #if ABSL_USE_UNSCALED_CYCLECLOCK @@ -78,4 +79,5 @@ double CycleClock::Frequency() { #endif } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/internal/cycleclock.h b/absl/base/internal/cycleclock.h index 60e971583c5..5fd8b348fb5 100644 --- a/absl/base/internal/cycleclock.h +++ b/absl/base/internal/cycleclock.h @@ -46,6 +46,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { // ----------------------------------------------------------------------------- @@ -72,6 +73,7 @@ class CycleClock { }; } // namespace base_internal +} // inline namespace lts_2018_06_20 } // 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 2fe345fc85a..e98c9960f52 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h @@ -62,6 +62,7 @@ extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); #endif // __BIONIC__ namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { // Platform specific logic extracted from @@ -122,6 +123,7 @@ inline int DirectMunmap(void* start, size_t length) { } } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #else // !__linux__ @@ -130,6 +132,7 @@ inline int DirectMunmap(void* start, size_t length) { // actual mmap()/munmap() methods. namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, @@ -142,6 +145,7 @@ inline int DirectMunmap(void* start, size_t length) { } } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // __linux__ diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index edc10f10a5a..0044711060f 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h @@ -34,6 +34,7 @@ #include "absl/base/port.h" namespace absl { +inline namespace lts_2018_06_20 { // 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. @@ -264,6 +265,7 @@ inline void Store64(void *p, uint64_t v) { } // namespace big_endian +} // inline namespace lts_2018_06_20 } // 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 f3ff4b39bb6..66ccd45aba9 100644 --- a/absl/base/internal/endian_test.cc +++ b/absl/base/internal/endian_test.cc @@ -24,6 +24,7 @@ #include "absl/base/config.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { const uint64_t kInitialNumber{0x0123456789abcdef}; @@ -276,4 +277,5 @@ TEST(EndianessTest, big_endian) { } } // namespace +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/internal/hide_ptr.h b/absl/base/internal/hide_ptr.h index 45cf438912c..a2694508ba1 100644 --- a/absl/base/internal/hide_ptr.h +++ b/absl/base/internal/hide_ptr.h @@ -18,6 +18,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { // Arbitrary value with high bits set. Xor'ing with it is unlikely @@ -42,6 +43,7 @@ inline T* UnhidePtr(uintptr_t hidden) { } } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_ diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h index a6734b4d353..2eaab453306 100644 --- a/absl/base/internal/identity.h +++ b/absl/base/internal/identity.h @@ -17,6 +17,7 @@ #define ABSL_BASE_INTERNAL_IDENTITY_H_ namespace absl { +inline namespace lts_2018_06_20 { namespace internal { template @@ -27,7 +28,8 @@ struct identity { template using identity_t = typename identity::type; -} // namespace internal -} // namespace absl +} // namespace internal +} // inline namespace lts_2018_06_20 +} // namespace absl #endif // ABSL_BASE_INTERNAL_IDENTITY_H_ diff --git a/absl/base/internal/inline_variable_testing.h b/absl/base/internal/inline_variable_testing.h index a0dd2bb28a4..49fa4ade38b 100644 --- a/absl/base/internal/inline_variable_testing.h +++ b/absl/base/internal/inline_variable_testing.h @@ -18,6 +18,7 @@ #include "absl/base/internal/inline_variable.h" namespace absl { +inline namespace lts_2018_06_20 { namespace inline_variable_testing_internal { struct Foo { @@ -39,6 +40,7 @@ const int& get_int_a(); const int& get_int_b(); } // namespace inline_variable_testing_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_ diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index 8c3f4f60637..bc05a0a394c 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -43,6 +43,7 @@ // top of this file for the API documentation. namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { // The five classes below each implement one of the clauses from the definition @@ -183,6 +184,7 @@ InvokeT Invoke(F&& f, Args&&... args) { std::forward(args)...); } } // namespace base_internal +} // inline namespace lts_2018_06_20 } // 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 0a6f3070534..ca6239ad3cf 100644 --- a/absl/base/internal/low_level_alloc.cc +++ b/absl/base/internal/low_level_alloc.cc @@ -63,6 +63,7 @@ #endif // __APPLE__ namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { // A first-fit allocator with amortized logarithmic free() time. @@ -599,6 +600,7 @@ void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) { } } // namespace base_internal +} // inline namespace lts_2018_06_20 } // 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 3c15605bed3..3f289571c18 100644 --- a/absl/base/internal/low_level_alloc.h +++ b/absl/base/internal/low_level_alloc.h @@ -51,6 +51,7 @@ #include "absl/base/port.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { class LowLevelAlloc { @@ -115,5 +116,6 @@ class LowLevelAlloc { }; } // namespace base_internal +} // inline namespace lts_2018_06_20 } // 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 cf2b363299f..15ffe298ae3 100644 --- a/absl/base/internal/low_level_alloc_test.cc +++ b/absl/base/internal/low_level_alloc_test.cc @@ -22,6 +22,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { namespace { @@ -148,6 +149,7 @@ static struct BeforeMain { } // namespace } // namespace base_internal +} // inline namespace lts_2018_06_20 } // 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 e716f2b49fa..2ae464b44f0 100644 --- a/absl/base/internal/low_level_scheduling.h +++ b/absl/base/internal/low_level_scheduling.h @@ -28,6 +28,7 @@ extern "C" bool __google_disable_rescheduling(void); extern "C" void __google_enable_rescheduling(bool disable_result); namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { class SchedulingHelper; // To allow use of SchedulingGuard. @@ -100,5 +101,6 @@ inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index 1ce13888725..cd1bbc0942e 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -181,6 +181,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } // namespace namespace absl { +inline namespace lts_2018_06_20 { namespace raw_logging_internal { void SafeWriteToStderr(const char *s, size_t len) { #if defined(ABSL_HAVE_SYSCALL_WRITE) @@ -215,4 +216,5 @@ bool RawLoggingFullySupported() { } } // namespace raw_logging_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index a2b7207a032..04ff0607a3a 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -65,6 +65,7 @@ ::absl::NormalizeLogSeverity(severity) namespace absl { +inline namespace lts_2018_06_20 { namespace raw_logging_internal { // Helper function to implement ABSL_RAW_LOG @@ -132,6 +133,7 @@ using AbortHook = void (*)(const char* file, int line, const char* buf_start, const char* prefix_end, const char* buf_end); } // namespace raw_logging_internal +} // inline namespace lts_2018_06_20 } // 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 1b6497ad875..dd7df6bb12f 100644 --- a/absl/base/internal/scheduling_mode.h +++ b/absl/base/internal/scheduling_mode.h @@ -19,6 +19,7 @@ #define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { // Used to describe how a thread may be scheduled. Typically associated with @@ -49,6 +50,7 @@ enum SchedulingMode { }; } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc index 1b97efbccc5..9d90b3cb40b 100644 --- a/absl/base/internal/spinlock.cc +++ b/absl/base/internal/spinlock.cc @@ -54,6 +54,7 @@ // holder to acquire the lock. There may be outstanding waiter(s). namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { ABSL_CONST_INIT static base_internal::AtomicHook *w, uint32_t value, int loop, int SpinLockSuggestedDelayNS(int loop); } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl // In some build configurations we pass --detect-odr-violations to the @@ -84,7 +86,7 @@ inline void absl::base_internal::SpinLockWake(std::atomic *w, inline void absl::base_internal::SpinLockDelay( std::atomic *w, uint32_t value, int loop, - base_internal::SchedulingMode scheduling_mode) { + absl::base_internal::SchedulingMode scheduling_mode) { AbslInternalSpinLockDelay(w, value, loop, scheduling_mode); } diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index db41bacc844..7db2e001515 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -56,6 +56,7 @@ #include "absl/base/internal/unscaledcycleclock.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { static once_flag init_system_info_once; @@ -401,4 +402,5 @@ pid_t GetTID() { #endif } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/internal/sysinfo.h b/absl/base/internal/sysinfo.h index 5bd1c500bd5..18aa2e2931b 100644 --- a/absl/base/internal/sysinfo.h +++ b/absl/base/internal/sysinfo.h @@ -33,6 +33,7 @@ #include "absl/base/port.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { // Nominal core processor cycles per second of each processor. This is _not_ @@ -58,6 +59,7 @@ using pid_t = DWORD; pid_t GetTID(); } // namespace base_internal +} // inline namespace lts_2018_06_20 } // 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 e0d9aab9bc2..fdbbdf8841d 100644 --- a/absl/base/internal/sysinfo_test.cc +++ b/absl/base/internal/sysinfo_test.cc @@ -28,6 +28,7 @@ #include "absl/synchronization/mutex.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { namespace { @@ -95,4 +96,5 @@ TEST(SysinfoTest, LinuxGetTID) { } // namespace } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc index 678e8568d74..aa1add8bc1d 100644 --- a/absl/base/internal/thread_identity.cc +++ b/absl/base/internal/thread_identity.cc @@ -28,6 +28,7 @@ #include "absl/base/internal/spinlock.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { #if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11 @@ -120,4 +121,5 @@ ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index a51722f9d82..18a5a75049b 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -33,6 +33,7 @@ #include "absl/base/internal/per_thread_tls.h" namespace absl { +inline namespace lts_2018_06_20 { struct SynchLocksHeld; struct SynchWaitParams; @@ -236,5 +237,6 @@ inline ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc index ecb8af68982..f39f11d23b1 100644 --- a/absl/base/internal/thread_identity_test.cc +++ b/absl/base/internal/thread_identity_test.cc @@ -25,6 +25,7 @@ #include "absl/synchronization/mutex.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { namespace { @@ -123,4 +124,5 @@ TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) { } // namespace } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/internal/throw_delegate.cc b/absl/base/internal/throw_delegate.cc index 46dc573cfa8..5466e0f3780 100644 --- a/absl/base/internal/throw_delegate.cc +++ b/absl/base/internal/throw_delegate.cc @@ -22,6 +22,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { namespace { @@ -103,4 +104,5 @@ void ThrowStdBadFunctionCall() { Throw(std::bad_function_call()); } void ThrowStdBadAlloc() { Throw(std::bad_alloc()); } } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/internal/throw_delegate.h b/absl/base/internal/throw_delegate.h index 70e2d7709e5..d34ff79cac7 100644 --- a/absl/base/internal/throw_delegate.h +++ b/absl/base/internal/throw_delegate.h @@ -20,6 +20,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { // Helper functions that allow throwing exceptions consistently from anywhere. @@ -66,6 +67,7 @@ namespace base_internal { // [[noreturn]] void ThrowStdBadArrayNewLength(); } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h index c5724362a4e..f20a869470e 100644 --- a/absl/base/internal/unaligned_access.h +++ b/absl/base/internal/unaligned_access.h @@ -65,6 +65,7 @@ void __sanitizer_unaligned_store64(void *p, uint64_t v); } // extern "C" namespace absl { +inline namespace lts_2018_06_20 { inline uint16_t UnalignedLoad16(const void *p) { return __sanitizer_unaligned_load16(p); @@ -90,6 +91,7 @@ inline void UnalignedStore64(void *p, uint64_t v) { __sanitizer_unaligned_store64(p, v); } +} // inline namespace lts_2018_06_20 } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) (absl::UnalignedLoad16(_p)) @@ -158,6 +160,7 @@ inline void UnalignedStore64(void *p, uint64_t v) { // so we do that. namespace absl { +inline namespace lts_2018_06_20 { namespace internal { struct Unaligned16Struct { @@ -171,6 +174,7 @@ struct Unaligned32Struct { } ABSL_ATTRIBUTE_PACKED; } // namespace internal +} // inline namespace lts_2018_06_20 } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ @@ -186,6 +190,7 @@ struct Unaligned32Struct { (_val)) namespace absl { +inline namespace lts_2018_06_20 { inline uint64_t UnalignedLoad64(const void *p) { uint64_t t; @@ -195,6 +200,7 @@ inline uint64_t UnalignedLoad64(const void *p) { inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } +} // inline namespace lts_2018_06_20 } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) (absl::UnalignedLoad64(_p)) @@ -211,6 +217,7 @@ inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } // unaligned loads and stores. namespace absl { +inline namespace lts_2018_06_20 { inline uint16_t UnalignedLoad16(const void *p) { uint16_t t; @@ -236,6 +243,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); } +} // inline namespace lts_2018_06_20 } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) (absl::UnalignedLoad16(_p)) diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index a12d68bd10a..e0798eb998d 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -27,6 +27,7 @@ #include "absl/base/internal/sysinfo.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { #if defined(__i386__) @@ -96,6 +97,7 @@ double UnscaledCycleClock::Frequency() { #endif } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_USE_UNSCALED_CYCLECLOCK diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index 049f1cace98..9da14d0b463 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -84,6 +84,7 @@ #define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY #endif namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { class UnscaledCycleClockWrapperForGetCurrentTime; } // namespace time_internal @@ -113,6 +114,7 @@ class UnscaledCycleClock { }; } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_USE_UNSCALED_CYCLECLOCK diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc index 466bf114a58..2c04b591000 100644 --- a/absl/base/invoke_test.cc +++ b/absl/base/invoke_test.cc @@ -25,6 +25,7 @@ #include "absl/strings/str_cat.h" namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { namespace { @@ -197,4 +198,5 @@ TEST(InvokeTest, SfinaeFriendly) { } // namespace } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h index e2931c34d1d..8b7a66ba89c 100644 --- a/absl/base/log_severity.h +++ b/absl/base/log_severity.h @@ -21,6 +21,7 @@ #include "absl/base/attributes.h" namespace absl { +inline namespace lts_2018_06_20 { // Four severity levels are defined. Logging APIs should terminate the program // when a message is logged at severity `kFatal`; the other levels have no @@ -62,6 +63,7 @@ constexpr absl::LogSeverity NormalizeLogSeverity(int s) { return NormalizeLogSeverity(static_cast(s)); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ diff --git a/absl/base/macros.h b/absl/base/macros.h index afa30300bc3..aabe8db4825 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h @@ -43,12 +43,14 @@ (sizeof(::absl::macros_internal::ArraySizeHelper(array))) namespace absl { +inline namespace lts_2018_06_20 { 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_06_20 } // namespace absl // kLinkerInitialized @@ -72,11 +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_06_20 { namespace base_internal { enum LinkerInitialized { kLinkerInitialized = 0, }; } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl // ABSL_FALLTHROUGH_INTENDED @@ -194,8 +198,9 @@ enum LinkerInitialized { #if defined(NDEBUG) #define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0) #else -#define ABSL_ASSERT(expr) \ - (ABSL_PREDICT_TRUE((expr)) ? (void)0 : [] { assert(false && #expr); }()) +#define ABSL_ASSERT(expr) \ + (ABSL_PREDICT_TRUE((expr)) ? (void)0 \ + : [] { assert(false && #expr); }()) // NOLINT #endif #endif // ABSL_BASE_MACROS_H_ diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc index 1b508848611..d04ee36694b 100644 --- a/absl/base/spinlock_test_common.cc +++ b/absl/base/spinlock_test_common.cc @@ -36,6 +36,7 @@ constexpr int32_t kNumThreads = 10; constexpr int32_t kIters = 1000; namespace absl { +inline namespace lts_2018_06_20 { namespace base_internal { // This is defined outside of anonymous namespace so that it can be @@ -263,4 +264,5 @@ TEST(SpinLockWithThreads, DoesNotDeadlock) { } // namespace } // namespace base_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index 06bc80095c9..daa4eb229da 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -50,6 +50,7 @@ #include "absl/memory/memory.h" namespace absl { +inline namespace lts_2018_06_20 { constexpr static auto kFixedArrayUseDefault = static_cast(-1); @@ -494,5 +495,6 @@ constexpr size_t FixedArray::inline_elements; template constexpr size_t FixedArray::kInlineBytesDefault; +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_CONTAINER_FIXED_ARRAY_H_ diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 78f78ea7022..183ade5405f 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -53,6 +53,7 @@ #include "absl/memory/memory.h" namespace absl { +inline namespace lts_2018_06_20 { // ----------------------------------------------------------------------------- // InlinedVector @@ -1379,6 +1380,7 @@ auto InlinedVector::InsertWithRange(const_iterator position, return it_pair.first; } +} // inline namespace lts_2018_06_20 } // 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 5977bc934f0..24f2174928a 100644 --- a/absl/container/inlined_vector_benchmark.cc +++ b/absl/container/inlined_vector_benchmark.cc @@ -63,18 +63,34 @@ void BM_StdVectorFill(benchmark::State& state) { } BENCHMARK(BM_StdVectorFill)->Range(0, 1024); +// The purpose of the next two benchmarks is to verify that +// absl::InlinedVector is efficient when moving is more efficent than +// copying. To do so, we use strings that are larger than the short +// std::string optimization. bool StringRepresentedInline(std::string s) { const char* chars = s.data(); std::string s1 = std::move(s); return s1.data() != chars; } +int GetNonShortStringOptimizationSize() { + for (int i = 24; i <= 192; i *= 2) { + if (!StringRepresentedInline(std::string(i, 'A'))) { + return i; + } + } + ABSL_RAW_LOG( + FATAL, + "Failed to find a std::string larger than the short std::string optimization"); + return -1; +} + void BM_InlinedVectorFillString(benchmark::State& state) { const int len = state.range(0); - std::string strings[4] = {"a quite long string", - "another long string", - "012345678901234567", - "to cause allocation"}; + 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')}; + for (auto _ : state) { absl::InlinedVector v; for (int i = 0; i < len; i++) { @@ -87,10 +103,10 @@ BENCHMARK(BM_InlinedVectorFillString)->Range(0, 1024); void BM_StdVectorFillString(benchmark::State& state) { const int len = state.range(0); - std::string strings[4] = {"a quite long string", - "another long string", - "012345678901234567", - "to cause allocation"}; + 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')}; + for (auto _ : state) { std::vector v; for (int i = 0; i < len; i++) { @@ -98,11 +114,6 @@ void BM_StdVectorFillString(benchmark::State& state) { } } state.SetItemsProcessed(static_cast(state.iterations()) * len); - // The purpose of the benchmark is to verify that inlined vector is - // efficient when moving is more efficent than copying. To do so, we - // use strings that are larger than the small std::string optimization. - ABSL_RAW_CHECK(!StringRepresentedInline(strings[0]), - "benchmarked with strings that are too small"); } BENCHMARK(BM_StdVectorFillString)->Range(0, 1024); diff --git a/absl/container/internal/test_instance_tracker.cc b/absl/container/internal/test_instance_tracker.cc index fe00aca8fb9..d1aa0978206 100644 --- a/absl/container/internal/test_instance_tracker.cc +++ b/absl/container/internal/test_instance_tracker.cc @@ -15,6 +15,7 @@ #include "absl/container/internal/test_instance_tracker.h" namespace absl { +inline namespace lts_2018_06_20 { namespace test_internal { int BaseCountedInstance::num_instances_ = 0; int BaseCountedInstance::num_live_instances_ = 0; @@ -23,4 +24,5 @@ int BaseCountedInstance::num_copies_ = 0; int BaseCountedInstance::num_swaps_ = 0; } // namespace test_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/container/internal/test_instance_tracker.h b/absl/container/internal/test_instance_tracker.h index cf8f3a531e6..b4a84656b60 100644 --- a/absl/container/internal/test_instance_tracker.h +++ b/absl/container/internal/test_instance_tracker.h @@ -19,6 +19,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace test_internal { // A type that counts number of occurences of the type, the live occurrences of @@ -215,6 +216,7 @@ class MovableOnlyInstance : public BaseCountedInstance { }; } // namespace test_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index 4c131fe07e9..b2035148264 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc @@ -47,6 +47,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options; @@ -353,4 +354,5 @@ void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) { } } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h index c57954e5875..fb4de102b5d 100644 --- a/absl/debugging/failure_signal_handler.h +++ b/absl/debugging/failure_signal_handler.h @@ -45,6 +45,7 @@ #define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ namespace absl { +inline namespace lts_2018_06_20 { // FailureSignalHandlerOptions // @@ -112,6 +113,7 @@ namespace debugging_internal { const char* FailureSignalToString(int signo); } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc index 7455aa0b416..6282bd6a99b 100644 --- a/absl/debugging/internal/address_is_readable.cc +++ b/absl/debugging/internal/address_is_readable.cc @@ -20,12 +20,14 @@ #if !defined(__linux__) || defined(__ANDROID__) namespace absl { +inline namespace lts_2018_06_20 { 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_06_20 } // namespace absl #else @@ -40,6 +42,7 @@ bool AddressIsReadable(const void* /* addr */) { return true; } #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { // Pack a pid and two file descriptors into a 64-bit word, @@ -128,6 +131,7 @@ bool AddressIsReadable(const void *addr) { } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif diff --git a/absl/debugging/internal/address_is_readable.h b/absl/debugging/internal/address_is_readable.h index 9d48030065c..7e58ce555c8 100644 --- a/absl/debugging/internal/address_is_readable.h +++ b/absl/debugging/internal/address_is_readable.h @@ -17,6 +17,7 @@ #define ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { // Return whether the byte at *addr is readable, without faulting. @@ -24,6 +25,7 @@ namespace debugging_internal { bool AddressIsReadable(const void *addr); } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 c9ca2f3bdb1..15b7c7c3d4b 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc @@ -24,6 +24,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { typedef struct { @@ -1859,4 +1860,5 @@ bool Demangle(const char *mangled, char *out, int out_size) { } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h index 2e75564ed35..7faa5a2f104 100644 --- a/absl/debugging/internal/demangle.h +++ b/absl/debugging/internal/demangle.h @@ -54,6 +54,7 @@ #define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { // Demangle `mangled`. On success, return true and write the @@ -62,6 +63,7 @@ namespace debugging_internal { bool Demangle(const char *mangled, char *out, int out_size); } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 b9d9008f007..5ff64a37338 100644 --- a/absl/debugging/internal/demangle_test.cc +++ b/absl/debugging/internal/demangle_test.cc @@ -23,6 +23,7 @@ #include "absl/memory/memory.h" namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { namespace { @@ -188,4 +189,5 @@ TEST(DemangleRegression, DeeplyNestedArrayType) { } // namespace } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc index 3f747e7f958..a2bea919957 100644 --- a/absl/debugging/internal/elf_mem_image.cc +++ b/absl/debugging/internal/elf_mem_image.cc @@ -38,6 +38,7 @@ #define VERSYM_VERSION 0x7fff namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { namespace { @@ -375,6 +376,7 @@ void ElfMemImage::SymbolIterator::Update(int increment) { } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 3b577268954..ea4f5f0c949 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -39,6 +39,7 @@ #include // for ElfW namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { // An in-memory ELF image (may not exist on disk). @@ -123,6 +124,7 @@ class ElfMemImage { }; } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 8434709facc..1a2b7a428cb 100644 --- a/absl/debugging/internal/examine_stack.cc +++ b/absl/debugging/internal/examine_stack.cc @@ -30,6 +30,7 @@ #include "absl/debugging/symbolize.h" namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { // Returns the program counter from signal context, nullptr if @@ -146,4 +147,5 @@ void DumpPCAndFrameSizesAndStackTrace( } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h index a16c03b2748..a51a8b3573f 100644 --- a/absl/debugging/internal/examine_stack.h +++ b/absl/debugging/internal/examine_stack.h @@ -18,6 +18,7 @@ #define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { // Returns the program counter from signal context, or nullptr if @@ -33,6 +34,7 @@ void DumpPCAndFrameSizesAndStackTrace( void (*writerfn)(const char*, void*), void* writerfn_arg); } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 2b3b972ea53..8724007d3d4 100644 --- a/absl/debugging/internal/stack_consumption.cc +++ b/absl/debugging/internal/stack_consumption.cc @@ -27,6 +27,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { namespace { @@ -167,6 +168,7 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 4c5fa0f0d82..584a4aa9364 100644 --- a/absl/debugging/internal/stack_consumption.h +++ b/absl/debugging/internal/stack_consumption.h @@ -27,6 +27,7 @@ #define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1 namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { // Returns the stack consumption in bytes for the code exercised by @@ -38,6 +39,7 @@ namespace debugging_internal { int GetSignalHandlerStackConsumption(void (*signal_handler)(int)); } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 5ce3846ec44..4f1351b3fec 100644 --- a/absl/debugging/internal/stack_consumption_test.cc +++ b/absl/debugging/internal/stack_consumption_test.cc @@ -23,6 +23,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { namespace { @@ -43,6 +44,7 @@ TEST(SignalHandlerStackConsumptionTest, MeasuresStackConsumption) { } // namespace } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 7ed6b3eb82d..0e3fda57fc4 100644 --- a/absl/debugging/internal/stacktrace_aarch64-inl.inc +++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -180,11 +180,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 c84083379bb..ef76782cc2c 100644 --- a/absl/debugging/internal/stacktrace_arm-inl.inc +++ b/absl/debugging/internal/stacktrace_arm-inl.inc @@ -113,11 +113,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ diff --git a/absl/debugging/internal/stacktrace_generic-inl.inc b/absl/debugging/internal/stacktrace_generic-inl.inc index 2c9ca410c60..5d7492a47c1 100644 --- a/absl/debugging/internal/stacktrace_generic-inl.inc +++ b/absl/debugging/internal/stacktrace_generic-inl.inc @@ -49,11 +49,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 297bdadf8fc..86ac8534ce7 100644 --- a/absl/debugging/internal/stacktrace_powerpc-inl.inc +++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc @@ -233,11 +233,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 e256fdd4ae7..8c3ed4d7ba3 100644 --- a/absl/debugging/internal/stacktrace_unimplemented-inl.inc +++ b/absl/debugging/internal/stacktrace_unimplemented-inl.inc @@ -12,11 +12,13 @@ static int UnwindImpl(void** /* result */, int* /* sizes */, } namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 a8f8a56afb3..ceca299d4d4 100644 --- a/absl/debugging/internal/stacktrace_win32-inl.inc +++ b/absl/debugging/internal/stacktrace_win32-inl.inc @@ -73,11 +73,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 ac85b920332..13afce7b613 100644 --- a/absl/debugging/internal/stacktrace_x86-inl.inc +++ b/absl/debugging/internal/stacktrace_x86-inl.inc @@ -327,11 +327,13 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, } namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 8d926fec48a..fd10a637558 100644 --- a/absl/debugging/internal/symbolize.h +++ b/absl/debugging/internal/symbolize.h @@ -34,6 +34,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { // Iterates over all sections, invoking callback on each with the section name @@ -52,11 +53,13 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, ElfW(Shdr) *out); } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { struct SymbolDecoratorArgs { @@ -118,6 +121,7 @@ bool GetFileMappingHint(const void** start, const char** filename); } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // 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 44ec7c02f51..fd1fb05e23d 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -38,6 +38,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { ABSL_CONST_INIT @@ -187,6 +188,7 @@ static class VDSOInitHelper { } vdso_init_helper; } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_HAVE_VDSO_SUPPORT diff --git a/absl/debugging/internal/vdso_support.h b/absl/debugging/internal/vdso_support.h index 8002c7405fa..7fdd7527d59 100644 --- a/absl/debugging/internal/vdso_support.h +++ b/absl/debugging/internal/vdso_support.h @@ -53,6 +53,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { // NOTE: this class may be used from within tcmalloc, and can not @@ -149,6 +150,7 @@ class VDSOSupport { int GetCPU(); } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_HAVE_ELF_MEM_IMAGE diff --git a/absl/debugging/leak_check.cc b/absl/debugging/leak_check.cc index e01e5f8c937..7eaaf495d4c 100644 --- a/absl/debugging/leak_check.cc +++ b/absl/debugging/leak_check.cc @@ -20,12 +20,14 @@ #ifndef LEAK_SANITIZER namespace absl { +inline namespace lts_2018_06_20 { 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_06_20 } // namespace absl #else @@ -33,6 +35,7 @@ LeakCheckDisabler::~LeakCheckDisabler() { } #include namespace absl { +inline namespace lts_2018_06_20 { bool HaveLeakSanitizer() { return true; } void DoIgnoreLeak(const void* ptr) { __lsan_ignore_object(ptr); } void RegisterLivePointers(const void* ptr, size_t size) { @@ -43,6 +46,7 @@ void UnRegisterLivePointers(const void* ptr, size_t size) { } LeakCheckDisabler::LeakCheckDisabler() { __lsan_disable(); } LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // LEAK_SANITIZER diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h index c930684e959..ddded2aeb7f 100644 --- a/absl/debugging/leak_check.h +++ b/absl/debugging/leak_check.h @@ -33,6 +33,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { // HaveLeakSanitizer() // @@ -104,6 +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_06_20 } // namespace absl #endif // ABSL_DEBUGGING_LEAK_CHECK_H_ diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc index 61fee6190f5..55edad83c94 100644 --- a/absl/debugging/stacktrace.cc +++ b/absl/debugging/stacktrace.cc @@ -56,6 +56,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { namespace { typedef int (*Unwinder)(void**, int*, int, int, const void*, int*); @@ -130,4 +131,5 @@ int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip, return n; } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/debugging/stacktrace.h b/absl/debugging/stacktrace.h index 8b831e26815..a6213cf4388 100644 --- a/absl/debugging/stacktrace.h +++ b/absl/debugging/stacktrace.h @@ -32,6 +32,7 @@ #define ABSL_DEBUGGING_STACKTRACE_H_ namespace absl { +inline namespace lts_2018_06_20 { // GetStackFrames() // @@ -220,6 +221,7 @@ namespace debugging_internal { // working. extern bool StackTraceWorksForTest(); } // namespace debugging_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_DEBUGGING_STACKTRACE_H_ diff --git a/absl/debugging/symbolize.h b/absl/debugging/symbolize.h index 24e6e6471c6..6198b1fdabb 100644 --- a/absl/debugging/symbolize.h +++ b/absl/debugging/symbolize.h @@ -55,6 +55,7 @@ #include "absl/debugging/internal/symbolize.h" namespace absl { +inline namespace lts_2018_06_20 { // InitializeSymbolizer() // @@ -92,6 +93,7 @@ void InitializeSymbolizer(const char* argv0); // } bool Symbolize(const void *pc, char *out, int out_size); +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_DEBUGGING_SYMBOLIZE_H_ diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index e21439ce3fa..b8dc23b70cf 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc @@ -76,6 +76,7 @@ #include "absl/debugging/internal/vdso_support.h" namespace absl { +inline namespace lts_2018_06_20 { // Value of argv[0]. Used by MaybeInitializeObjFile(). static char *argv0_value = nullptr; @@ -1470,4 +1471,5 @@ bool Symbolize(const void *pc, char *out, int out_size) { return ok; } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/debugging/symbolize_unimplemented.inc b/absl/debugging/symbolize_unimplemented.inc index 2a3f4acb0e1..26dec0fb1ba 100644 --- a/absl/debugging/symbolize_unimplemented.inc +++ b/absl/debugging/symbolize_unimplemented.inc @@ -17,6 +17,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { namespace debugging_internal { @@ -32,4 +33,5 @@ bool RegisterFileMappingHint(const void *, const void *, uint64_t, const char *) void InitializeSymbolizer(const char*) {} bool Symbolize(const void *, char *, int) { return false; } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc index e3fff74d493..549b995afad 100644 --- a/absl/debugging/symbolize_win32.inc +++ b/absl/debugging/symbolize_win32.inc @@ -25,6 +25,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { static HANDLE process = NULL; @@ -71,4 +72,5 @@ bool Symbolize(const void *pc, char *out, int out_size) { return true; } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel index d5c62265a8c..46f47b12bb2 100644 --- a/absl/memory/BUILD.bazel +++ b/absl/memory/BUILD.bazel @@ -18,6 +18,7 @@ load( "//absl:copts.bzl", "ABSL_DEFAULT_COPTS", "ABSL_TEST_COPTS", + "ABSL_EXCEPTIONS_FLAG", ) package(default_visibility = ["//visibility:public"]) @@ -45,3 +46,16 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "memory_exception_safety_test", + srcs = [ + "memory_exception_safety_test.cc", + ], + copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + deps = [ + ":memory", + "//absl/base:exception_safety_testing", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt index 21bfc87ed6e..5958f5c52bf 100644 --- a/absl/memory/CMakeLists.txt +++ b/absl/memory/CMakeLists.txt @@ -49,4 +49,23 @@ absl_test( ) +# test memory_exception_safety_test +set(MEMORY_EXCEPTION_SAFETY_TEST_SRC "memory_exception_safety_test.cc") +set(MEMORY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES + absl::memory + absl_base_internal_exception_safety_testing +) + +absl_test( + TARGET + memory_exception_safety_test + SOURCES + ${MEMORY_EXCEPTION_SAFETY_TEST_SRC} + PUBLIC_LIBRARIES + ${MEMORY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES} + PRIVATE_COMPILE_FLAGS + ${ABSL_EXCEPTIONS_FLAG} +) + + diff --git a/absl/memory/memory.h b/absl/memory/memory.h index cd818cff4ff..54ff218225c 100644 --- a/absl/memory/memory.h +++ b/absl/memory/memory.h @@ -34,6 +34,7 @@ #include "absl/meta/type_traits.h" namespace absl { +inline namespace lts_2018_06_20 { // ----------------------------------------------------------------------------- // Function Template: WrapUnique() @@ -636,6 +637,7 @@ struct default_allocator_is_nothrow : std::true_type {}; struct default_allocator_is_nothrow : std::false_type {}; #endif +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_MEMORY_MEMORY_H_ diff --git a/absl/memory/memory_exception_safety_test.cc b/absl/memory/memory_exception_safety_test.cc new file mode 100644 index 00000000000..dc00f34989a --- /dev/null +++ b/absl/memory/memory_exception_safety_test.cc @@ -0,0 +1,51 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/memory/memory.h" + +#include "gtest/gtest.h" +#include "absl/base/internal/exception_safety_testing.h" + +namespace absl { +inline namespace lts_2018_06_20 { +namespace { + +using Thrower = ::testing::ThrowingValue<>; + +TEST(MakeUnique, CheckForLeaks) { + constexpr int kValue = 321; + constexpr size_t kLength = 10; + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(Thrower(kValue)) + // Ensures make_unique does not modify the input. The real + // test, though, is ConstructorTracker checking for leaks. + .WithInvariants(testing::strong_guarantee); + + EXPECT_TRUE(tester.Test([](Thrower* thrower) { + static_cast(absl::make_unique(*thrower)); + })); + + EXPECT_TRUE(tester.Test([](Thrower* thrower) { + static_cast(absl::make_unique(std::move(*thrower))); + })); + + // Test T[n] overload + EXPECT_TRUE(tester.Test([&](Thrower*) { + static_cast(absl::make_unique(kLength)); + })); +} + +} // namespace +} // inline namespace lts_2018_06_20 +} // namespace absl diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index 88af17c395d..923234c4c36 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -42,6 +42,7 @@ #include "absl/base/config.h" namespace absl { +inline namespace lts_2018_06_20 { namespace type_traits_internal { template @@ -368,5 +369,6 @@ struct IsHashEnabled IsHashable> {}; } // namespace type_traits_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_META_TYPE_TRAITS_H_ diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index 3688e5efef4..315cae107dc 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc @@ -23,6 +23,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { const uint128 kuint128max = MakeUint128(std::numeric_limits::max(), std::numeric_limits::max()); @@ -222,4 +223,5 @@ std::ostream& operator<<(std::ostream& os, uint128 v) { return os << rep; } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index bc7dbb47ae8..96f42a83c30 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -37,6 +37,7 @@ #include "absl/base/port.h" namespace absl { +inline namespace lts_2018_06_20 { // uint128 @@ -651,6 +652,7 @@ inline uint128& uint128::operator--() { #include "absl/numeric/int128_no_intrinsic.inc" #endif // ABSL_HAVE_INTRINSIC_INT128 +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_NUMERIC_INT128_H_ diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index f06bdc0d01c..328f52f33d2 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -32,7 +32,12 @@ cc_library( name = "strings", srcs = [ "ascii.cc", + "charconv.cc", "escaping.cc", + "internal/charconv_bigint.cc", + "internal/charconv_bigint.h", + "internal/charconv_parse.cc", + "internal/charconv_parse.h", "internal/memutil.cc", "internal/memutil.h", "internal/stl_type_traits.h", @@ -48,6 +53,7 @@ cc_library( ], hdrs = [ "ascii.h", + "charconv.h", "escaping.h", "match.h", "numbers.h", @@ -144,11 +150,6 @@ cc_test( size = "small", srcs = ["ascii_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_android_arm", - "no_test_android_arm64", - "no_test_android_x86", - ], visibility = ["//visibility:private"], deps = [ ":strings", @@ -398,12 +399,6 @@ cc_test( "numbers_test.cc", ], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_android_arm", - "no_test_android_arm64", - "no_test_android_x86", - "no_test_loonix", - ], visibility = ["//visibility:private"], deps = [ ":strings", @@ -429,11 +424,6 @@ cc_test( name = "char_map_test", srcs = ["internal/char_map_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_android_arm", - "no_test_android_arm64", - "no_test_android_x86", - ], deps = [ ":internal", "@com_google_googletest//:gtest_main", @@ -450,3 +440,55 @@ cc_test( "@com_github_google_benchmark//:benchmark_main", ], ) + +cc_test( + name = "charconv_test", + srcs = ["charconv_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":strings", + "//absl/base", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "charconv_parse_test", + srcs = [ + "internal/charconv_parse.h", + "internal/charconv_parse_test.cc", + ], + copts = ABSL_TEST_COPTS, + deps = [ + ":strings", + "//absl/base", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "charconv_bigint_test", + srcs = [ + "internal/charconv_bigint.h", + "internal/charconv_bigint_test.cc", + "internal/charconv_parse.h", + ], + copts = ABSL_TEST_COPTS, + deps = [ + ":strings", + "//absl/base", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "charconv_benchmark", + srcs = [ + "charconv_benchmark.cc", + ], + deps = [ + ":strings", + "//absl/base", + "@com_github_google_benchmark//:benchmark_main", + ], +) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 9dc47328c73..cab2c4561e4 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -17,6 +17,7 @@ list(APPEND STRINGS_PUBLIC_HEADERS "ascii.h" + "charconv.h" "escaping.h" "match.h" "numbers.h" @@ -33,6 +34,8 @@ list(APPEND STRINGS_PUBLIC_HEADERS list(APPEND STRINGS_INTERNAL_HEADERS "internal/bits.h" "internal/char_map.h" + "internal/charconv_bigint.h" + "internal/charconv_parse.h" "internal/memutil.h" "internal/ostringstream.h" "internal/resize_uninitialized.h" @@ -47,7 +50,10 @@ list(APPEND STRINGS_INTERNAL_HEADERS # add string library list(APPEND STRINGS_SRC "ascii.cc" + "charconv.cc" "escaping.cc" + "internal/charconv_bigint.cc" + "internal/charconv_parse.cc" "internal/memutil.cc" "internal/memutil.h" "internal/utf8.cc" @@ -301,5 +307,43 @@ absl_test( ) +# test charconv_test +set(CHARCONV_TEST_SRC "charconv_test.cc") +set(CHARCONV_TEST_PUBLIC_LIBRARIES absl::strings) +absl_test( + TARGET + charconv_test + SOURCES + ${CHARCONV_TEST_SRC} + PUBLIC_LIBRARIES + ${CHARCONV_TEST_PUBLIC_LIBRARIES} +) + + +# test charconv_parse_test +set(CHARCONV_PARSE_TEST_SRC "internal/charconv_parse_test.cc") +set(CHARCONV_PARSE_TEST_PUBLIC_LIBRARIES absl::strings) + +absl_test( + TARGET + charconv_parse_test + SOURCES + ${CHARCONV_PARSE_TEST_SRC} + PUBLIC_LIBRARIES + ${CHARCONV_PARSE_TEST_PUBLIC_LIBRARIES} +) + + +# test charconv_bigint_test +set(CHARCONV_BIGINT_TEST_SRC "internal/charconv_bigint_test.cc") +set(CHARCONV_BIGINT_TEST_PUBLIC_LIBRARIES absl::strings) +absl_test( + TARGET + charconv_bigint_test + SOURCES + ${CHARCONV_BIGINT_TEST_SRC} + PUBLIC_LIBRARIES + ${CHARCONV_BIGINT_TEST_PUBLIC_LIBRARIES} +) diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc index c9481e88654..5c94acc7152 100644 --- a/absl/strings/ascii.cc +++ b/absl/strings/ascii.cc @@ -15,6 +15,7 @@ #include "absl/strings/ascii.h" namespace absl { +inline namespace lts_2018_06_20 { namespace ascii_internal { // # Table generated by this Python code (bit 0x02 is currently unused): @@ -195,4 +196,5 @@ void RemoveExtraAsciiWhitespace(std::string* str) { str->erase(output_it - &(*str)[0]); } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h index 96a64541c32..507fad4bf0d 100644 --- a/absl/strings/ascii.h +++ b/absl/strings/ascii.h @@ -59,6 +59,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { namespace ascii_internal { // Declaration for an array of bitfields holding character information. @@ -234,6 +235,7 @@ inline void StripAsciiWhitespace(std::string* str) { // Removes leading, trailing, and consecutive internal whitespace. void RemoveExtraAsciiWhitespace(std::string*); +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_ASCII_H_ diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc new file mode 100644 index 00000000000..ab75ab49193 --- /dev/null +++ b/absl/strings/charconv.cc @@ -0,0 +1,984 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/charconv.h" + +#include +#include +#include +#include + +#include "absl/base/casts.h" +#include "absl/numeric/int128.h" +#include "absl/strings/internal/bits.h" +#include "absl/strings/internal/charconv_bigint.h" +#include "absl/strings/internal/charconv_parse.h" + +// The macro ABSL_BIT_PACK_FLOATS is defined on x86-64, where IEEE floating +// point numbers have the same endianness in memory as a bitfield struct +// containing the corresponding parts. +// +// When set, we replace calls to ldexp() with manual bit packing, which is +// faster and is unaffected by floating point environment. +#ifdef ABSL_BIT_PACK_FLOATS +#error ABSL_BIT_PACK_FLOATS cannot be directly set +#elif defined(__x86_64__) || defined(_M_X64) +#define ABSL_BIT_PACK_FLOATS 1 +#endif + +// A note about subnormals: +// +// The code below talks about "normals" and "subnormals". A normal IEEE float +// has a fixed-width mantissa and power of two exponent. For example, a normal +// `double` has a 53-bit mantissa. Because the high bit is always 1, it is not +// stored in the representation. The implicit bit buys an extra bit of +// resolution in the datatype. +// +// The downside of this scheme is that there is a large gap between DBL_MIN and +// zero. (Large, at least, relative to the different between DBL_MIN and the +// next representable number). This gap is softened by the "subnormal" numbers, +// which have the same power-of-two exponent as DBL_MIN, but no implicit 53rd +// bit. An all-bits-zero exponent in the encoding represents subnormals. (Zero +// is represented as a subnormal with an all-bits-zero mantissa.) +// +// The code below, in calculations, represents the mantissa as a uint64_t. The +// end result normally has the 53rd bit set. It represents subnormals by using +// narrower mantissas. + +namespace absl { +inline namespace lts_2018_06_20 { +namespace { + +template +struct FloatTraits; + +template <> +struct FloatTraits { + // The number of mantissa bits in the given float type. This includes the + // implied high bit. + static constexpr int kTargetMantissaBits = 53; + + // The largest supported IEEE exponent, in our integral mantissa + // representation. + // + // If `m` is the largest possible int kTargetMantissaBits bits wide, then + // m * 2**kMaxExponent is exactly equal to DBL_MAX. + static constexpr int kMaxExponent = 971; + + // The smallest supported IEEE normal exponent, in our integral mantissa + // representation. + // + // If `m` is the smallest possible int kTargetMantissaBits bits wide, then + // m * 2**kMinNormalExponent is exactly equal to DBL_MIN. + static constexpr int kMinNormalExponent = -1074; + + static double MakeNan(const char* tagp) { + // Support nan no matter which namespace it's in. Some platforms + // incorrectly don't put it in namespace std. + using namespace std; // NOLINT + return nan(tagp); + } + + // Builds a nonzero floating point number out of the provided parts. + // + // This is intended to do the same operation as ldexp(mantissa, exponent), + // but using purely integer math, to avoid -ffastmath and floating + // point environment issues. Using type punning is also faster. We fall back + // to ldexp on a per-platform basis for portability. + // + // `exponent` must be between kMinNormalExponent and kMaxExponent. + // + // `mantissa` must either be exactly kTargetMantissaBits wide, in which case + // a normal value is made, or it must be less narrow than that, in which case + // `exponent` must be exactly kMinNormalExponent, and a subnormal value is + // made. + static double Make(uint64_t mantissa, int exponent, bool sign) { +#ifndef ABSL_BIT_PACK_FLOATS + // Support ldexp no matter which namespace it's in. Some platforms + // incorrectly don't put it in namespace std. + using namespace std; // NOLINT + return sign ? -ldexp(mantissa, exponent) : ldexp(mantissa, exponent); +#else + constexpr uint64_t kMantissaMask = + (uint64_t(1) << (kTargetMantissaBits - 1)) - 1; + uint64_t dbl = static_cast(sign) << 63; + if (mantissa > kMantissaMask) { + // Normal value. + // Adjust by 1023 for the exponent representation bias, and an additional + // 52 due to the implied decimal point in the IEEE mantissa represenation. + dbl += uint64_t{exponent + 1023u + kTargetMantissaBits - 1} << 52; + mantissa &= kMantissaMask; + } else { + // subnormal value + assert(exponent == kMinNormalExponent); + } + dbl += mantissa; + return absl::bit_cast(dbl); +#endif // ABSL_BIT_PACK_FLOATS + } +}; + +// Specialization of floating point traits for the `float` type. See the +// FloatTraits specialization above for meaning of each of the following +// members and methods. +template <> +struct FloatTraits { + static constexpr int kTargetMantissaBits = 24; + static constexpr int kMaxExponent = 104; + static constexpr int kMinNormalExponent = -149; + static float MakeNan(const char* tagp) { + // Support nanf no matter which namespace it's in. Some platforms + // incorrectly don't put it in namespace std. + using namespace std; // NOLINT + return nanf(tagp); + } + static float Make(uint32_t mantissa, int exponent, bool sign) { +#ifndef ABSL_BIT_PACK_FLOATS + // Support ldexpf no matter which namespace it's in. Some platforms + // incorrectly don't put it in namespace std. + using namespace std; // NOLINT + return sign ? -ldexpf(mantissa, exponent) : ldexpf(mantissa, exponent); +#else + constexpr uint32_t kMantissaMask = + (uint32_t(1) << (kTargetMantissaBits - 1)) - 1; + uint32_t flt = static_cast(sign) << 31; + if (mantissa > kMantissaMask) { + // Normal value. + // Adjust by 127 for the exponent representation bias, and an additional + // 23 due to the implied decimal point in the IEEE mantissa represenation. + flt += uint32_t{exponent + 127u + kTargetMantissaBits - 1} << 23; + mantissa &= kMantissaMask; + } else { + // subnormal value + assert(exponent == kMinNormalExponent); + } + flt += mantissa; + return absl::bit_cast(flt); +#endif // ABSL_BIT_PACK_FLOATS + } +}; + +// Decimal-to-binary conversions require coercing powers of 10 into a mantissa +// and a power of 2. The two helper functions Power10Mantissa(n) and +// Power10Exponent(n) perform this task. Together, these represent a hand- +// rolled floating point value which is equal to or just less than 10**n. +// +// The return values satisfy two range guarantees: +// +// Power10Mantissa(n) * 2**Power10Exponent(n) <= 10**n +// < (Power10Mantissa(n) + 1) * 2**Power10Exponent(n) +// +// 2**63 <= Power10Mantissa(n) < 2**64. +// +// Lookups into the power-of-10 table must first check the Power10Overflow() and +// Power10Underflow() functions, to avoid out-of-bounds table access. +// +// Indexes into these tables are biased by -kPower10TableMin, and the table has +// values in the range [kPower10TableMin, kPower10TableMax]. +extern const uint64_t kPower10MantissaTable[]; +extern const int16_t kPower10ExponentTable[]; + +// The smallest allowed value for use with the Power10Mantissa() and +// Power10Exponent() functions below. (If a smaller exponent is needed in +// calculations, the end result is guaranteed to underflow.) +constexpr int kPower10TableMin = -342; + +// The largest allowed value for use with the Power10Mantissa() and +// Power10Exponent() functions below. (If a smaller exponent is needed in +// calculations, the end result is guaranteed to overflow.) +constexpr int kPower10TableMax = 308; + +uint64_t Power10Mantissa(int n) { + return kPower10MantissaTable[n - kPower10TableMin]; +} + +int Power10Exponent(int n) { + return kPower10ExponentTable[n - kPower10TableMin]; +} + +// Returns true if n is large enough that 10**n always results in an IEEE +// overflow. +bool Power10Overflow(int n) { return n > kPower10TableMax; } + +// Returns true if n is small enough that 10**n times a ParsedFloat mantissa +// always results in an IEEE underflow. +bool Power10Underflow(int n) { return n < kPower10TableMin; } + +// Returns true if Power10Mantissa(n) * 2**Power10Exponent(n) is exactly equal +// to 10**n numerically. Put another way, this returns true if there is no +// truncation error in Power10Mantissa(n). +bool Power10Exact(int n) { return n >= 0 && n <= 27; } + +// Sentinel exponent values for representing numbers too large or too close to +// zero to represent in a double. +constexpr int kOverflow = 99999; +constexpr int kUnderflow = -99999; + +// Struct representing the calculated conversion result of a positive (nonzero) +// floating point number. +// +// The calculated number is mantissa * 2**exponent (mantissa is treated as an +// integer.) `mantissa` is chosen to be the correct width for the IEEE float +// representation being calculated. (`mantissa` will always have the same bit +// width for normal values, and narrower bit widths for subnormals.) +// +// If the result of conversion was an underflow or overflow, exponent is set +// to kUnderflow or kOverflow. +struct CalculatedFloat { + uint64_t mantissa = 0; + int exponent = 0; +}; + +// Returns the bit width of the given uint128. (Equivalently, returns 128 +// minus the number of leading zero bits.) +int BitWidth(uint128 value) { + if (Uint128High64(value) == 0) { + return 64 - strings_internal::CountLeadingZeros64(Uint128Low64(value)); + } + return 128 - strings_internal::CountLeadingZeros64(Uint128High64(value)); +} + +// Calculates how far to the right a mantissa needs to be shifted to create a +// properly adjusted mantissa for an IEEE floating point number. +// +// `mantissa_width` is the bit width of the mantissa to be shifted, and +// `binary_exponent` is the exponent of the number before the shift. +// +// This accounts for subnormal values, and will return a larger-than-normal +// shift if binary_exponent would otherwise be too low. +template +int NormalizedShiftSize(int mantissa_width, int binary_exponent) { + const int normal_shift = + mantissa_width - FloatTraits::kTargetMantissaBits; + const int minimum_shift = + FloatTraits::kMinNormalExponent - binary_exponent; + return std::max(normal_shift, minimum_shift); +} + +// Right shifts a uint128 so that it has the requested bit width. (The +// resulting value will have 128 - bit_width leading zeroes.) The initial +// `value` must be wider than the requested bit width. +// +// Returns the number of bits shifted. +int TruncateToBitWidth(int bit_width, uint128* value) { + const int current_bit_width = BitWidth(*value); + const int shift = current_bit_width - bit_width; + *value >>= shift; + return shift; +} + +// Checks if the given ParsedFloat represents one of the edge cases that are +// not dependent on number base: zero, infinity, or NaN. If so, sets *value +// the appropriate double, and returns true. +template +bool HandleEdgeCase(const strings_internal::ParsedFloat& input, bool negative, + FloatType* value) { + if (input.type == strings_internal::FloatType::kNan) { + // A bug in both clang and gcc would cause the compiler to optimize away the + // buffer we are building below. Declaring the buffer volatile avoids the + // issue, and has no measurable performance impact in microbenchmarks. + // + // https://bugs.llvm.org/show_bug.cgi?id=37778 + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86113 + constexpr ptrdiff_t kNanBufferSize = 128; + volatile char n_char_sequence[kNanBufferSize]; + if (input.subrange_begin == nullptr) { + n_char_sequence[0] = '\0'; + } else { + ptrdiff_t nan_size = input.subrange_end - input.subrange_begin; + nan_size = std::min(nan_size, kNanBufferSize - 1); + std::copy_n(input.subrange_begin, nan_size, n_char_sequence); + n_char_sequence[nan_size] = '\0'; + } + char* nan_argument = const_cast(n_char_sequence); + *value = negative ? -FloatTraits::MakeNan(nan_argument) + : FloatTraits::MakeNan(nan_argument); + return true; + } + if (input.type == strings_internal::FloatType::kInfinity) { + *value = negative ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + return true; + } + if (input.mantissa == 0) { + *value = negative ? -0.0 : 0.0; + return true; + } + return false; +} + +// Given a CalculatedFloat result of a from_chars conversion, generate the +// correct output values. +// +// CalculatedFloat can represent an underflow or overflow, in which case the +// error code in *result is set. Otherwise, the calculated floating point +// number is stored in *value. +template +void EncodeResult(const CalculatedFloat& calculated, bool negative, + absl::from_chars_result* result, FloatType* value) { + if (calculated.exponent == kOverflow) { + result->ec = std::errc::result_out_of_range; + *value = negative ? -std::numeric_limits::max() + : std::numeric_limits::max(); + return; + } else if (calculated.mantissa == 0 || calculated.exponent == kUnderflow) { + result->ec = std::errc::result_out_of_range; + *value = negative ? -0.0 : 0.0; + return; + } + *value = FloatTraits::Make(calculated.mantissa, + calculated.exponent, negative); +} + +// Returns the given uint128 shifted to the right by `shift` bits, and rounds +// the remaining bits using round_to_nearest logic. The value is returned as a +// uint64_t, since this is the type used by this library for storing calculated +// floating point mantissas. +// +// It is expected that the width of the input value shifted by `shift` will +// be the correct bit-width for the target mantissa, which is strictly narrower +// than a uint64_t. +// +// If `input_exact` is false, then a nonzero error epsilon is assumed. For +// rounding purposes, the true value being rounded is strictly greater than the +// input value. The error may represent a single lost carry bit. +// +// When input_exact, shifted bits of the form 1000000... represent a tie, which +// is broken by rounding to even -- the rounding direction is chosen so the low +// bit of the returned value is 0. +// +// When !input_exact, shifted bits of the form 10000000... represent a value +// strictly greater than one half (due to the error epsilon), and so ties are +// always broken by rounding up. +// +// When !input_exact, shifted bits of the form 01111111... are uncertain; +// the true value may or may not be greater than 10000000..., due to the +// possible lost carry bit. The correct rounding direction is unknown. In this +// case, the result is rounded down, and `output_exact` is set to false. +// +// Zero and negative values of `shift` are accepted, in which case the word is +// shifted left, as necessary. +uint64_t ShiftRightAndRound(uint128 value, int shift, bool input_exact, + bool* output_exact) { + if (shift <= 0) { + *output_exact = input_exact; + return static_cast(value << -shift); + } + if (shift >= 128) { + // Exponent is so small that we are shifting away all significant bits. + // Answer will not be representable, even as a subnormal, so return a zero + // mantissa (which represents underflow). + *output_exact = true; + return 0; + } + + *output_exact = true; + const uint128 shift_mask = (uint128(1) << shift) - 1; + const uint128 halfway_point = uint128(1) << (shift - 1); + + const uint128 shifted_bits = value & shift_mask; + value >>= shift; + if (shifted_bits > halfway_point) { + // Shifted bits greater than 10000... require rounding up. + return static_cast(value + 1); + } + if (shifted_bits == halfway_point) { + // In exact mode, shifted bits of 10000... mean we're exactly halfway + // between two numbers, and we must round to even. So only round up if + // the low bit of `value` is set. + // + // In inexact mode, the nonzero error means the actual value is greater + // than the halfway point and we must alway round up. + if ((value & 1) == 1 || !input_exact) { + ++value; + } + return static_cast(value); + } + if (!input_exact && shifted_bits == halfway_point - 1) { + // Rounding direction is unclear, due to error. + *output_exact = false; + } + // Otherwise, round down. + return static_cast(value); +} + +// Checks if a floating point guess needs to be rounded up, using high precision +// math. +// +// `guess_mantissa` and `guess_exponent` represent a candidate guess for the +// number represented by `parsed_decimal`. +// +// The exact number represented by `parsed_decimal` must lie between the two +// numbers: +// A = `guess_mantissa * 2**guess_exponent` +// B = `(guess_mantissa + 1) * 2**guess_exponent` +// +// This function returns false if `A` is the better guess, and true if `B` is +// the better guess, with rounding ties broken by rounding to even. +bool MustRoundUp(uint64_t guess_mantissa, int guess_exponent, + const strings_internal::ParsedFloat& parsed_decimal) { + // 768 is the number of digits needed in the worst case. We could determine a + // better limit dynamically based on the value of parsed_decimal.exponent. + // This would optimize pathological input cases only. (Sane inputs won't have + // hundreds of digits of mantissa.) + absl::strings_internal::BigUnsigned<84> exact_mantissa; + int exact_exponent = exact_mantissa.ReadFloatMantissa(parsed_decimal, 768); + + // Adjust the `guess` arguments to be halfway between A and B. + guess_mantissa = guess_mantissa * 2 + 1; + guess_exponent -= 1; + + // In our comparison: + // lhs = exact = exact_mantissa * 10**exact_exponent + // = exact_mantissa * 5**exact_exponent * 2**exact_exponent + // rhs = guess = guess_mantissa * 2**guess_exponent + // + // Because we are doing integer math, we can't directly deal with negative + // exponents. We instead move these to the other side of the inequality. + absl::strings_internal::BigUnsigned<84>& lhs = exact_mantissa; + int comparison; + if (exact_exponent >= 0) { + lhs.MultiplyByFiveToTheNth(exact_exponent); + absl::strings_internal::BigUnsigned<84> rhs(guess_mantissa); + // There are powers of 2 on both sides of the inequality; reduce this to + // a single bit-shift. + if (exact_exponent > guess_exponent) { + lhs.ShiftLeft(exact_exponent - guess_exponent); + } else { + rhs.ShiftLeft(guess_exponent - exact_exponent); + } + comparison = Compare(lhs, rhs); + } else { + // Move the power of 5 to the other side of the equation, giving us: + // lhs = exact_mantissa * 2**exact_exponent + // rhs = guess_mantissa * 5**(-exact_exponent) * 2**guess_exponent + absl::strings_internal::BigUnsigned<84> rhs = + absl::strings_internal::BigUnsigned<84>::FiveToTheNth(-exact_exponent); + rhs.MultiplyBy(guess_mantissa); + if (exact_exponent > guess_exponent) { + lhs.ShiftLeft(exact_exponent - guess_exponent); + } else { + rhs.ShiftLeft(guess_exponent - exact_exponent); + } + comparison = Compare(lhs, rhs); + } + if (comparison < 0) { + return false; + } else if (comparison > 0) { + return true; + } else { + // When lhs == rhs, the decimal input is exactly between A and B. + // Round towards even -- round up only if the low bit of the initial + // `guess_mantissa` was a 1. We shifted guess_mantissa left 1 bit at + // the beginning of this function, so test the 2nd bit here. + return (guess_mantissa & 2) == 2; + } +} + +// Constructs a CalculatedFloat from a given mantissa and exponent, but +// with the following normalizations applied: +// +// If rounding has caused mantissa to increase just past the allowed bit +// width, shift and adjust exponent. +// +// If exponent is too high, sets kOverflow. +// +// If mantissa is zero (representing a non-zero value not representable, even +// as a subnormal), sets kUnderflow. +template +CalculatedFloat CalculatedFloatFromRawValues(uint64_t mantissa, int exponent) { + CalculatedFloat result; + if (mantissa == uint64_t(1) << FloatTraits::kTargetMantissaBits) { + mantissa >>= 1; + exponent += 1; + } + if (exponent > FloatTraits::kMaxExponent) { + result.exponent = kOverflow; + } else if (mantissa == 0) { + result.exponent = kUnderflow; + } else { + result.exponent = exponent; + result.mantissa = mantissa; + } + return result; +} + +template +CalculatedFloat CalculateFromParsedHexadecimal( + const strings_internal::ParsedFloat& parsed_hex) { + uint64_t mantissa = parsed_hex.mantissa; + int exponent = parsed_hex.exponent; + int mantissa_width = 64 - strings_internal::CountLeadingZeros64(mantissa); + const int shift = NormalizedShiftSize(mantissa_width, exponent); + bool result_exact; + exponent += shift; + mantissa = ShiftRightAndRound(mantissa, shift, + /* input exact= */ true, &result_exact); + // ParseFloat handles rounding in the hexadecimal case, so we don't have to + // check `result_exact` here. + return CalculatedFloatFromRawValues(mantissa, exponent); +} + +template +CalculatedFloat CalculateFromParsedDecimal( + const strings_internal::ParsedFloat& parsed_decimal) { + CalculatedFloat result; + + // Large or small enough decimal exponents will always result in overflow + // or underflow. + if (Power10Underflow(parsed_decimal.exponent)) { + result.exponent = kUnderflow; + return result; + } else if (Power10Overflow(parsed_decimal.exponent)) { + result.exponent = kOverflow; + return result; + } + + // Otherwise convert our power of 10 into a power of 2 times an integer + // mantissa, and multiply this by our parsed decimal mantissa. + uint128 wide_binary_mantissa = parsed_decimal.mantissa; + wide_binary_mantissa *= Power10Mantissa(parsed_decimal.exponent); + int binary_exponent = Power10Exponent(parsed_decimal.exponent); + + // Discard bits that are inaccurate due to truncation error. The magic + // `mantissa_width` constants below are justified in charconv_algorithm.md. + // They represent the number of bits in `wide_binary_mantissa` that are + // guaranteed to be unaffected by error propagation. + bool mantissa_exact; + int mantissa_width; + if (parsed_decimal.subrange_begin) { + // Truncated mantissa + mantissa_width = 58; + mantissa_exact = false; + binary_exponent += + TruncateToBitWidth(mantissa_width, &wide_binary_mantissa); + } else if (!Power10Exact(parsed_decimal.exponent)) { + // Exact mantissa, truncated power of ten + mantissa_width = 63; + mantissa_exact = false; + binary_exponent += + TruncateToBitWidth(mantissa_width, &wide_binary_mantissa); + } else { + // Product is exact + mantissa_width = BitWidth(wide_binary_mantissa); + mantissa_exact = true; + } + + // Shift into an FloatType-sized mantissa, and round to nearest. + const int shift = + NormalizedShiftSize(mantissa_width, binary_exponent); + bool result_exact; + binary_exponent += shift; + uint64_t binary_mantissa = ShiftRightAndRound(wide_binary_mantissa, shift, + mantissa_exact, &result_exact); + if (!result_exact) { + // We could not determine the rounding direction using int128 math. Use + // full resolution math instead. + if (MustRoundUp(binary_mantissa, binary_exponent, parsed_decimal)) { + binary_mantissa += 1; + } + } + + return CalculatedFloatFromRawValues(binary_mantissa, + binary_exponent); +} + +template +from_chars_result FromCharsImpl(const char* first, const char* last, + FloatType& value, chars_format fmt_flags) { + from_chars_result result; + result.ptr = first; // overwritten on successful parse + result.ec = std::errc(); + + bool negative = false; + if (first != last && *first == '-') { + ++first; + negative = true; + } + // If the `hex` flag is *not* set, then we will accept a 0x prefix and try + // to parse a hexadecimal float. + if ((fmt_flags & chars_format::hex) == chars_format{} && last - first >= 2 && + *first == '0' && (first[1] == 'x' || first[1] == 'X')) { + const char* hex_first = first + 2; + strings_internal::ParsedFloat hex_parse = + strings_internal::ParseFloat<16>(hex_first, last, fmt_flags); + if (hex_parse.end == nullptr || + hex_parse.type != strings_internal::FloatType::kNumber) { + // Either we failed to parse a hex float after the "0x", or we read + // "0xinf" or "0xnan" which we don't want to match. + // + // However, a std::string that begins with "0x" also begins with "0", which + // is normally a valid match for the number zero. So we want these + // strings to match zero unless fmt_flags is `scientific`. (This flag + // means an exponent is required, which the std::string "0" does not have.) + if (fmt_flags == chars_format::scientific) { + result.ec = std::errc::invalid_argument; + } else { + result.ptr = first + 1; + value = negative ? -0.0 : 0.0; + } + return result; + } + // We matched a value. + result.ptr = hex_parse.end; + if (HandleEdgeCase(hex_parse, negative, &value)) { + return result; + } + CalculatedFloat calculated = + CalculateFromParsedHexadecimal(hex_parse); + EncodeResult(calculated, negative, &result, &value); + return result; + } + // Otherwise, we choose the number base based on the flags. + if ((fmt_flags & chars_format::hex) == chars_format::hex) { + strings_internal::ParsedFloat hex_parse = + strings_internal::ParseFloat<16>(first, last, fmt_flags); + if (hex_parse.end == nullptr) { + result.ec = std::errc::invalid_argument; + return result; + } + result.ptr = hex_parse.end; + if (HandleEdgeCase(hex_parse, negative, &value)) { + return result; + } + CalculatedFloat calculated = + CalculateFromParsedHexadecimal(hex_parse); + EncodeResult(calculated, negative, &result, &value); + return result; + } else { + strings_internal::ParsedFloat decimal_parse = + strings_internal::ParseFloat<10>(first, last, fmt_flags); + if (decimal_parse.end == nullptr) { + result.ec = std::errc::invalid_argument; + return result; + } + result.ptr = decimal_parse.end; + if (HandleEdgeCase(decimal_parse, negative, &value)) { + return result; + } + CalculatedFloat calculated = + CalculateFromParsedDecimal(decimal_parse); + EncodeResult(calculated, negative, &result, &value); + return result; + } + return result; +} +} // namespace + +from_chars_result from_chars(const char* first, const char* last, double& value, + chars_format fmt) { + return FromCharsImpl(first, last, value, fmt); +} + +from_chars_result from_chars(const char* first, const char* last, float& value, + chars_format fmt) { + return FromCharsImpl(first, last, value, fmt); +} + +namespace { + +// Table of powers of 10, from kPower10TableMin to kPower10TableMax. +// +// kPower10MantissaTable[i - kPower10TableMin] stores the 64-bit mantissa (high +// bit always on), and kPower10ExponentTable[i - kPower10TableMin] stores the +// power-of-two exponent. For a given number i, this gives the unique mantissa +// and exponent such that mantissa * 2**exponent <= 10**i < (mantissa + 1) * +// 2**exponent. + +const uint64_t kPower10MantissaTable[] = { + 0xeef453d6923bd65aU, 0x9558b4661b6565f8U, 0xbaaee17fa23ebf76U, + 0xe95a99df8ace6f53U, 0x91d8a02bb6c10594U, 0xb64ec836a47146f9U, + 0xe3e27a444d8d98b7U, 0x8e6d8c6ab0787f72U, 0xb208ef855c969f4fU, + 0xde8b2b66b3bc4723U, 0x8b16fb203055ac76U, 0xaddcb9e83c6b1793U, + 0xd953e8624b85dd78U, 0x87d4713d6f33aa6bU, 0xa9c98d8ccb009506U, + 0xd43bf0effdc0ba48U, 0x84a57695fe98746dU, 0xa5ced43b7e3e9188U, + 0xcf42894a5dce35eaU, 0x818995ce7aa0e1b2U, 0xa1ebfb4219491a1fU, + 0xca66fa129f9b60a6U, 0xfd00b897478238d0U, 0x9e20735e8cb16382U, + 0xc5a890362fddbc62U, 0xf712b443bbd52b7bU, 0x9a6bb0aa55653b2dU, + 0xc1069cd4eabe89f8U, 0xf148440a256e2c76U, 0x96cd2a865764dbcaU, + 0xbc807527ed3e12bcU, 0xeba09271e88d976bU, 0x93445b8731587ea3U, + 0xb8157268fdae9e4cU, 0xe61acf033d1a45dfU, 0x8fd0c16206306babU, + 0xb3c4f1ba87bc8696U, 0xe0b62e2929aba83cU, 0x8c71dcd9ba0b4925U, + 0xaf8e5410288e1b6fU, 0xdb71e91432b1a24aU, 0x892731ac9faf056eU, + 0xab70fe17c79ac6caU, 0xd64d3d9db981787dU, 0x85f0468293f0eb4eU, + 0xa76c582338ed2621U, 0xd1476e2c07286faaU, 0x82cca4db847945caU, + 0xa37fce126597973cU, 0xcc5fc196fefd7d0cU, 0xff77b1fcbebcdc4fU, + 0x9faacf3df73609b1U, 0xc795830d75038c1dU, 0xf97ae3d0d2446f25U, + 0x9becce62836ac577U, 0xc2e801fb244576d5U, 0xf3a20279ed56d48aU, + 0x9845418c345644d6U, 0xbe5691ef416bd60cU, 0xedec366b11c6cb8fU, + 0x94b3a202eb1c3f39U, 0xb9e08a83a5e34f07U, 0xe858ad248f5c22c9U, + 0x91376c36d99995beU, 0xb58547448ffffb2dU, 0xe2e69915b3fff9f9U, + 0x8dd01fad907ffc3bU, 0xb1442798f49ffb4aU, 0xdd95317f31c7fa1dU, + 0x8a7d3eef7f1cfc52U, 0xad1c8eab5ee43b66U, 0xd863b256369d4a40U, + 0x873e4f75e2224e68U, 0xa90de3535aaae202U, 0xd3515c2831559a83U, + 0x8412d9991ed58091U, 0xa5178fff668ae0b6U, 0xce5d73ff402d98e3U, + 0x80fa687f881c7f8eU, 0xa139029f6a239f72U, 0xc987434744ac874eU, + 0xfbe9141915d7a922U, 0x9d71ac8fada6c9b5U, 0xc4ce17b399107c22U, + 0xf6019da07f549b2bU, 0x99c102844f94e0fbU, 0xc0314325637a1939U, + 0xf03d93eebc589f88U, 0x96267c7535b763b5U, 0xbbb01b9283253ca2U, + 0xea9c227723ee8bcbU, 0x92a1958a7675175fU, 0xb749faed14125d36U, + 0xe51c79a85916f484U, 0x8f31cc0937ae58d2U, 0xb2fe3f0b8599ef07U, + 0xdfbdcece67006ac9U, 0x8bd6a141006042bdU, 0xaecc49914078536dU, + 0xda7f5bf590966848U, 0x888f99797a5e012dU, 0xaab37fd7d8f58178U, + 0xd5605fcdcf32e1d6U, 0x855c3be0a17fcd26U, 0xa6b34ad8c9dfc06fU, + 0xd0601d8efc57b08bU, 0x823c12795db6ce57U, 0xa2cb1717b52481edU, + 0xcb7ddcdda26da268U, 0xfe5d54150b090b02U, 0x9efa548d26e5a6e1U, + 0xc6b8e9b0709f109aU, 0xf867241c8cc6d4c0U, 0x9b407691d7fc44f8U, + 0xc21094364dfb5636U, 0xf294b943e17a2bc4U, 0x979cf3ca6cec5b5aU, + 0xbd8430bd08277231U, 0xece53cec4a314ebdU, 0x940f4613ae5ed136U, + 0xb913179899f68584U, 0xe757dd7ec07426e5U, 0x9096ea6f3848984fU, + 0xb4bca50b065abe63U, 0xe1ebce4dc7f16dfbU, 0x8d3360f09cf6e4bdU, + 0xb080392cc4349decU, 0xdca04777f541c567U, 0x89e42caaf9491b60U, + 0xac5d37d5b79b6239U, 0xd77485cb25823ac7U, 0x86a8d39ef77164bcU, + 0xa8530886b54dbdebU, 0xd267caa862a12d66U, 0x8380dea93da4bc60U, + 0xa46116538d0deb78U, 0xcd795be870516656U, 0x806bd9714632dff6U, + 0xa086cfcd97bf97f3U, 0xc8a883c0fdaf7df0U, 0xfad2a4b13d1b5d6cU, + 0x9cc3a6eec6311a63U, 0xc3f490aa77bd60fcU, 0xf4f1b4d515acb93bU, + 0x991711052d8bf3c5U, 0xbf5cd54678eef0b6U, 0xef340a98172aace4U, + 0x9580869f0e7aac0eU, 0xbae0a846d2195712U, 0xe998d258869facd7U, + 0x91ff83775423cc06U, 0xb67f6455292cbf08U, 0xe41f3d6a7377eecaU, + 0x8e938662882af53eU, 0xb23867fb2a35b28dU, 0xdec681f9f4c31f31U, + 0x8b3c113c38f9f37eU, 0xae0b158b4738705eU, 0xd98ddaee19068c76U, + 0x87f8a8d4cfa417c9U, 0xa9f6d30a038d1dbcU, 0xd47487cc8470652bU, + 0x84c8d4dfd2c63f3bU, 0xa5fb0a17c777cf09U, 0xcf79cc9db955c2ccU, + 0x81ac1fe293d599bfU, 0xa21727db38cb002fU, 0xca9cf1d206fdc03bU, + 0xfd442e4688bd304aU, 0x9e4a9cec15763e2eU, 0xc5dd44271ad3cdbaU, + 0xf7549530e188c128U, 0x9a94dd3e8cf578b9U, 0xc13a148e3032d6e7U, + 0xf18899b1bc3f8ca1U, 0x96f5600f15a7b7e5U, 0xbcb2b812db11a5deU, + 0xebdf661791d60f56U, 0x936b9fcebb25c995U, 0xb84687c269ef3bfbU, + 0xe65829b3046b0afaU, 0x8ff71a0fe2c2e6dcU, 0xb3f4e093db73a093U, + 0xe0f218b8d25088b8U, 0x8c974f7383725573U, 0xafbd2350644eeacfU, + 0xdbac6c247d62a583U, 0x894bc396ce5da772U, 0xab9eb47c81f5114fU, + 0xd686619ba27255a2U, 0x8613fd0145877585U, 0xa798fc4196e952e7U, + 0xd17f3b51fca3a7a0U, 0x82ef85133de648c4U, 0xa3ab66580d5fdaf5U, + 0xcc963fee10b7d1b3U, 0xffbbcfe994e5c61fU, 0x9fd561f1fd0f9bd3U, + 0xc7caba6e7c5382c8U, 0xf9bd690a1b68637bU, 0x9c1661a651213e2dU, + 0xc31bfa0fe5698db8U, 0xf3e2f893dec3f126U, 0x986ddb5c6b3a76b7U, + 0xbe89523386091465U, 0xee2ba6c0678b597fU, 0x94db483840b717efU, + 0xba121a4650e4ddebU, 0xe896a0d7e51e1566U, 0x915e2486ef32cd60U, + 0xb5b5ada8aaff80b8U, 0xe3231912d5bf60e6U, 0x8df5efabc5979c8fU, + 0xb1736b96b6fd83b3U, 0xddd0467c64bce4a0U, 0x8aa22c0dbef60ee4U, + 0xad4ab7112eb3929dU, 0xd89d64d57a607744U, 0x87625f056c7c4a8bU, + 0xa93af6c6c79b5d2dU, 0xd389b47879823479U, 0x843610cb4bf160cbU, + 0xa54394fe1eedb8feU, 0xce947a3da6a9273eU, 0x811ccc668829b887U, + 0xa163ff802a3426a8U, 0xc9bcff6034c13052U, 0xfc2c3f3841f17c67U, + 0x9d9ba7832936edc0U, 0xc5029163f384a931U, 0xf64335bcf065d37dU, + 0x99ea0196163fa42eU, 0xc06481fb9bcf8d39U, 0xf07da27a82c37088U, + 0x964e858c91ba2655U, 0xbbe226efb628afeaU, 0xeadab0aba3b2dbe5U, + 0x92c8ae6b464fc96fU, 0xb77ada0617e3bbcbU, 0xe55990879ddcaabdU, + 0x8f57fa54c2a9eab6U, 0xb32df8e9f3546564U, 0xdff9772470297ebdU, + 0x8bfbea76c619ef36U, 0xaefae51477a06b03U, 0xdab99e59958885c4U, + 0x88b402f7fd75539bU, 0xaae103b5fcd2a881U, 0xd59944a37c0752a2U, + 0x857fcae62d8493a5U, 0xa6dfbd9fb8e5b88eU, 0xd097ad07a71f26b2U, + 0x825ecc24c873782fU, 0xa2f67f2dfa90563bU, 0xcbb41ef979346bcaU, + 0xfea126b7d78186bcU, 0x9f24b832e6b0f436U, 0xc6ede63fa05d3143U, + 0xf8a95fcf88747d94U, 0x9b69dbe1b548ce7cU, 0xc24452da229b021bU, + 0xf2d56790ab41c2a2U, 0x97c560ba6b0919a5U, 0xbdb6b8e905cb600fU, + 0xed246723473e3813U, 0x9436c0760c86e30bU, 0xb94470938fa89bceU, + 0xe7958cb87392c2c2U, 0x90bd77f3483bb9b9U, 0xb4ecd5f01a4aa828U, + 0xe2280b6c20dd5232U, 0x8d590723948a535fU, 0xb0af48ec79ace837U, + 0xdcdb1b2798182244U, 0x8a08f0f8bf0f156bU, 0xac8b2d36eed2dac5U, + 0xd7adf884aa879177U, 0x86ccbb52ea94baeaU, 0xa87fea27a539e9a5U, + 0xd29fe4b18e88640eU, 0x83a3eeeef9153e89U, 0xa48ceaaab75a8e2bU, + 0xcdb02555653131b6U, 0x808e17555f3ebf11U, 0xa0b19d2ab70e6ed6U, + 0xc8de047564d20a8bU, 0xfb158592be068d2eU, 0x9ced737bb6c4183dU, + 0xc428d05aa4751e4cU, 0xf53304714d9265dfU, 0x993fe2c6d07b7fabU, + 0xbf8fdb78849a5f96U, 0xef73d256a5c0f77cU, 0x95a8637627989aadU, + 0xbb127c53b17ec159U, 0xe9d71b689dde71afU, 0x9226712162ab070dU, + 0xb6b00d69bb55c8d1U, 0xe45c10c42a2b3b05U, 0x8eb98a7a9a5b04e3U, + 0xb267ed1940f1c61cU, 0xdf01e85f912e37a3U, 0x8b61313bbabce2c6U, + 0xae397d8aa96c1b77U, 0xd9c7dced53c72255U, 0x881cea14545c7575U, + 0xaa242499697392d2U, 0xd4ad2dbfc3d07787U, 0x84ec3c97da624ab4U, + 0xa6274bbdd0fadd61U, 0xcfb11ead453994baU, 0x81ceb32c4b43fcf4U, + 0xa2425ff75e14fc31U, 0xcad2f7f5359a3b3eU, 0xfd87b5f28300ca0dU, + 0x9e74d1b791e07e48U, 0xc612062576589ddaU, 0xf79687aed3eec551U, + 0x9abe14cd44753b52U, 0xc16d9a0095928a27U, 0xf1c90080baf72cb1U, + 0x971da05074da7beeU, 0xbce5086492111aeaU, 0xec1e4a7db69561a5U, + 0x9392ee8e921d5d07U, 0xb877aa3236a4b449U, 0xe69594bec44de15bU, + 0x901d7cf73ab0acd9U, 0xb424dc35095cd80fU, 0xe12e13424bb40e13U, + 0x8cbccc096f5088cbU, 0xafebff0bcb24aafeU, 0xdbe6fecebdedd5beU, + 0x89705f4136b4a597U, 0xabcc77118461cefcU, 0xd6bf94d5e57a42bcU, + 0x8637bd05af6c69b5U, 0xa7c5ac471b478423U, 0xd1b71758e219652bU, + 0x83126e978d4fdf3bU, 0xa3d70a3d70a3d70aU, 0xccccccccccccccccU, + 0x8000000000000000U, 0xa000000000000000U, 0xc800000000000000U, + 0xfa00000000000000U, 0x9c40000000000000U, 0xc350000000000000U, + 0xf424000000000000U, 0x9896800000000000U, 0xbebc200000000000U, + 0xee6b280000000000U, 0x9502f90000000000U, 0xba43b74000000000U, + 0xe8d4a51000000000U, 0x9184e72a00000000U, 0xb5e620f480000000U, + 0xe35fa931a0000000U, 0x8e1bc9bf04000000U, 0xb1a2bc2ec5000000U, + 0xde0b6b3a76400000U, 0x8ac7230489e80000U, 0xad78ebc5ac620000U, + 0xd8d726b7177a8000U, 0x878678326eac9000U, 0xa968163f0a57b400U, + 0xd3c21bcecceda100U, 0x84595161401484a0U, 0xa56fa5b99019a5c8U, + 0xcecb8f27f4200f3aU, 0x813f3978f8940984U, 0xa18f07d736b90be5U, + 0xc9f2c9cd04674edeU, 0xfc6f7c4045812296U, 0x9dc5ada82b70b59dU, + 0xc5371912364ce305U, 0xf684df56c3e01bc6U, 0x9a130b963a6c115cU, + 0xc097ce7bc90715b3U, 0xf0bdc21abb48db20U, 0x96769950b50d88f4U, + 0xbc143fa4e250eb31U, 0xeb194f8e1ae525fdU, 0x92efd1b8d0cf37beU, + 0xb7abc627050305adU, 0xe596b7b0c643c719U, 0x8f7e32ce7bea5c6fU, + 0xb35dbf821ae4f38bU, 0xe0352f62a19e306eU, 0x8c213d9da502de45U, + 0xaf298d050e4395d6U, 0xdaf3f04651d47b4cU, 0x88d8762bf324cd0fU, + 0xab0e93b6efee0053U, 0xd5d238a4abe98068U, 0x85a36366eb71f041U, + 0xa70c3c40a64e6c51U, 0xd0cf4b50cfe20765U, 0x82818f1281ed449fU, + 0xa321f2d7226895c7U, 0xcbea6f8ceb02bb39U, 0xfee50b7025c36a08U, + 0x9f4f2726179a2245U, 0xc722f0ef9d80aad6U, 0xf8ebad2b84e0d58bU, + 0x9b934c3b330c8577U, 0xc2781f49ffcfa6d5U, 0xf316271c7fc3908aU, + 0x97edd871cfda3a56U, 0xbde94e8e43d0c8ecU, 0xed63a231d4c4fb27U, + 0x945e455f24fb1cf8U, 0xb975d6b6ee39e436U, 0xe7d34c64a9c85d44U, + 0x90e40fbeea1d3a4aU, 0xb51d13aea4a488ddU, 0xe264589a4dcdab14U, + 0x8d7eb76070a08aecU, 0xb0de65388cc8ada8U, 0xdd15fe86affad912U, + 0x8a2dbf142dfcc7abU, 0xacb92ed9397bf996U, 0xd7e77a8f87daf7fbU, + 0x86f0ac99b4e8dafdU, 0xa8acd7c0222311bcU, 0xd2d80db02aabd62bU, + 0x83c7088e1aab65dbU, 0xa4b8cab1a1563f52U, 0xcde6fd5e09abcf26U, + 0x80b05e5ac60b6178U, 0xa0dc75f1778e39d6U, 0xc913936dd571c84cU, + 0xfb5878494ace3a5fU, 0x9d174b2dcec0e47bU, 0xc45d1df942711d9aU, + 0xf5746577930d6500U, 0x9968bf6abbe85f20U, 0xbfc2ef456ae276e8U, + 0xefb3ab16c59b14a2U, 0x95d04aee3b80ece5U, 0xbb445da9ca61281fU, + 0xea1575143cf97226U, 0x924d692ca61be758U, 0xb6e0c377cfa2e12eU, + 0xe498f455c38b997aU, 0x8edf98b59a373fecU, 0xb2977ee300c50fe7U, + 0xdf3d5e9bc0f653e1U, 0x8b865b215899f46cU, 0xae67f1e9aec07187U, + 0xda01ee641a708de9U, 0x884134fe908658b2U, 0xaa51823e34a7eedeU, + 0xd4e5e2cdc1d1ea96U, 0x850fadc09923329eU, 0xa6539930bf6bff45U, + 0xcfe87f7cef46ff16U, 0x81f14fae158c5f6eU, 0xa26da3999aef7749U, + 0xcb090c8001ab551cU, 0xfdcb4fa002162a63U, 0x9e9f11c4014dda7eU, + 0xc646d63501a1511dU, 0xf7d88bc24209a565U, 0x9ae757596946075fU, + 0xc1a12d2fc3978937U, 0xf209787bb47d6b84U, 0x9745eb4d50ce6332U, + 0xbd176620a501fbffU, 0xec5d3fa8ce427affU, 0x93ba47c980e98cdfU, + 0xb8a8d9bbe123f017U, 0xe6d3102ad96cec1dU, 0x9043ea1ac7e41392U, + 0xb454e4a179dd1877U, 0xe16a1dc9d8545e94U, 0x8ce2529e2734bb1dU, + 0xb01ae745b101e9e4U, 0xdc21a1171d42645dU, 0x899504ae72497ebaU, + 0xabfa45da0edbde69U, 0xd6f8d7509292d603U, 0x865b86925b9bc5c2U, + 0xa7f26836f282b732U, 0xd1ef0244af2364ffU, 0x8335616aed761f1fU, + 0xa402b9c5a8d3a6e7U, 0xcd036837130890a1U, 0x802221226be55a64U, + 0xa02aa96b06deb0fdU, 0xc83553c5c8965d3dU, 0xfa42a8b73abbf48cU, + 0x9c69a97284b578d7U, 0xc38413cf25e2d70dU, 0xf46518c2ef5b8cd1U, + 0x98bf2f79d5993802U, 0xbeeefb584aff8603U, 0xeeaaba2e5dbf6784U, + 0x952ab45cfa97a0b2U, 0xba756174393d88dfU, 0xe912b9d1478ceb17U, + 0x91abb422ccb812eeU, 0xb616a12b7fe617aaU, 0xe39c49765fdf9d94U, + 0x8e41ade9fbebc27dU, 0xb1d219647ae6b31cU, 0xde469fbd99a05fe3U, + 0x8aec23d680043beeU, 0xada72ccc20054ae9U, 0xd910f7ff28069da4U, + 0x87aa9aff79042286U, 0xa99541bf57452b28U, 0xd3fa922f2d1675f2U, + 0x847c9b5d7c2e09b7U, 0xa59bc234db398c25U, 0xcf02b2c21207ef2eU, + 0x8161afb94b44f57dU, 0xa1ba1ba79e1632dcU, 0xca28a291859bbf93U, + 0xfcb2cb35e702af78U, 0x9defbf01b061adabU, 0xc56baec21c7a1916U, + 0xf6c69a72a3989f5bU, 0x9a3c2087a63f6399U, 0xc0cb28a98fcf3c7fU, + 0xf0fdf2d3f3c30b9fU, 0x969eb7c47859e743U, 0xbc4665b596706114U, + 0xeb57ff22fc0c7959U, 0x9316ff75dd87cbd8U, 0xb7dcbf5354e9beceU, + 0xe5d3ef282a242e81U, 0x8fa475791a569d10U, 0xb38d92d760ec4455U, + 0xe070f78d3927556aU, 0x8c469ab843b89562U, 0xaf58416654a6babbU, + 0xdb2e51bfe9d0696aU, 0x88fcf317f22241e2U, 0xab3c2fddeeaad25aU, + 0xd60b3bd56a5586f1U, 0x85c7056562757456U, 0xa738c6bebb12d16cU, + 0xd106f86e69d785c7U, 0x82a45b450226b39cU, 0xa34d721642b06084U, + 0xcc20ce9bd35c78a5U, 0xff290242c83396ceU, 0x9f79a169bd203e41U, + 0xc75809c42c684dd1U, 0xf92e0c3537826145U, 0x9bbcc7a142b17ccbU, + 0xc2abf989935ddbfeU, 0xf356f7ebf83552feU, 0x98165af37b2153deU, + 0xbe1bf1b059e9a8d6U, 0xeda2ee1c7064130cU, 0x9485d4d1c63e8be7U, + 0xb9a74a0637ce2ee1U, 0xe8111c87c5c1ba99U, 0x910ab1d4db9914a0U, + 0xb54d5e4a127f59c8U, 0xe2a0b5dc971f303aU, 0x8da471a9de737e24U, + 0xb10d8e1456105dadU, 0xdd50f1996b947518U, 0x8a5296ffe33cc92fU, + 0xace73cbfdc0bfb7bU, 0xd8210befd30efa5aU, 0x8714a775e3e95c78U, + 0xa8d9d1535ce3b396U, 0xd31045a8341ca07cU, 0x83ea2b892091e44dU, + 0xa4e4b66b68b65d60U, 0xce1de40642e3f4b9U, 0x80d2ae83e9ce78f3U, + 0xa1075a24e4421730U, 0xc94930ae1d529cfcU, 0xfb9b7cd9a4a7443cU, + 0x9d412e0806e88aa5U, 0xc491798a08a2ad4eU, 0xf5b5d7ec8acb58a2U, + 0x9991a6f3d6bf1765U, 0xbff610b0cc6edd3fU, 0xeff394dcff8a948eU, + 0x95f83d0a1fb69cd9U, 0xbb764c4ca7a4440fU, 0xea53df5fd18d5513U, + 0x92746b9be2f8552cU, 0xb7118682dbb66a77U, 0xe4d5e82392a40515U, + 0x8f05b1163ba6832dU, 0xb2c71d5bca9023f8U, 0xdf78e4b2bd342cf6U, + 0x8bab8eefb6409c1aU, 0xae9672aba3d0c320U, 0xda3c0f568cc4f3e8U, + 0x8865899617fb1871U, 0xaa7eebfb9df9de8dU, 0xd51ea6fa85785631U, + 0x8533285c936b35deU, 0xa67ff273b8460356U, 0xd01fef10a657842cU, + 0x8213f56a67f6b29bU, 0xa298f2c501f45f42U, 0xcb3f2f7642717713U, + 0xfe0efb53d30dd4d7U, 0x9ec95d1463e8a506U, 0xc67bb4597ce2ce48U, + 0xf81aa16fdc1b81daU, 0x9b10a4e5e9913128U, 0xc1d4ce1f63f57d72U, + 0xf24a01a73cf2dccfU, 0x976e41088617ca01U, 0xbd49d14aa79dbc82U, + 0xec9c459d51852ba2U, 0x93e1ab8252f33b45U, 0xb8da1662e7b00a17U, + 0xe7109bfba19c0c9dU, 0x906a617d450187e2U, 0xb484f9dc9641e9daU, + 0xe1a63853bbd26451U, 0x8d07e33455637eb2U, 0xb049dc016abc5e5fU, + 0xdc5c5301c56b75f7U, 0x89b9b3e11b6329baU, 0xac2820d9623bf429U, + 0xd732290fbacaf133U, 0x867f59a9d4bed6c0U, 0xa81f301449ee8c70U, + 0xd226fc195c6a2f8cU, 0x83585d8fd9c25db7U, 0xa42e74f3d032f525U, + 0xcd3a1230c43fb26fU, 0x80444b5e7aa7cf85U, 0xa0555e361951c366U, + 0xc86ab5c39fa63440U, 0xfa856334878fc150U, 0x9c935e00d4b9d8d2U, + 0xc3b8358109e84f07U, 0xf4a642e14c6262c8U, 0x98e7e9cccfbd7dbdU, + 0xbf21e44003acdd2cU, 0xeeea5d5004981478U, 0x95527a5202df0ccbU, + 0xbaa718e68396cffdU, 0xe950df20247c83fdU, 0x91d28b7416cdd27eU, + 0xb6472e511c81471dU, 0xe3d8f9e563a198e5U, 0x8e679c2f5e44ff8fU, +}; + +const int16_t kPower10ExponentTable[] = { + -1200, -1196, -1193, -1190, -1186, -1183, -1180, -1176, -1173, -1170, -1166, + -1163, -1160, -1156, -1153, -1150, -1146, -1143, -1140, -1136, -1133, -1130, + -1127, -1123, -1120, -1117, -1113, -1110, -1107, -1103, -1100, -1097, -1093, + -1090, -1087, -1083, -1080, -1077, -1073, -1070, -1067, -1063, -1060, -1057, + -1053, -1050, -1047, -1043, -1040, -1037, -1034, -1030, -1027, -1024, -1020, + -1017, -1014, -1010, -1007, -1004, -1000, -997, -994, -990, -987, -984, + -980, -977, -974, -970, -967, -964, -960, -957, -954, -950, -947, + -944, -940, -937, -934, -931, -927, -924, -921, -917, -914, -911, + -907, -904, -901, -897, -894, -891, -887, -884, -881, -877, -874, + -871, -867, -864, -861, -857, -854, -851, -847, -844, -841, -838, + -834, -831, -828, -824, -821, -818, -814, -811, -808, -804, -801, + -798, -794, -791, -788, -784, -781, -778, -774, -771, -768, -764, + -761, -758, -754, -751, -748, -744, -741, -738, -735, -731, -728, + -725, -721, -718, -715, -711, -708, -705, -701, -698, -695, -691, + -688, -685, -681, -678, -675, -671, -668, -665, -661, -658, -655, + -651, -648, -645, -642, -638, -635, -632, -628, -625, -622, -618, + -615, -612, -608, -605, -602, -598, -595, -592, -588, -585, -582, + -578, -575, -572, -568, -565, -562, -558, -555, -552, -549, -545, + -542, -539, -535, -532, -529, -525, -522, -519, -515, -512, -509, + -505, -502, -499, -495, -492, -489, -485, -482, -479, -475, -472, + -469, -465, -462, -459, -455, -452, -449, -446, -442, -439, -436, + -432, -429, -426, -422, -419, -416, -412, -409, -406, -402, -399, + -396, -392, -389, -386, -382, -379, -376, -372, -369, -366, -362, + -359, -356, -353, -349, -346, -343, -339, -336, -333, -329, -326, + -323, -319, -316, -313, -309, -306, -303, -299, -296, -293, -289, + -286, -283, -279, -276, -273, -269, -266, -263, -259, -256, -253, + -250, -246, -243, -240, -236, -233, -230, -226, -223, -220, -216, + -213, -210, -206, -203, -200, -196, -193, -190, -186, -183, -180, + -176, -173, -170, -166, -163, -160, -157, -153, -150, -147, -143, + -140, -137, -133, -130, -127, -123, -120, -117, -113, -110, -107, + -103, -100, -97, -93, -90, -87, -83, -80, -77, -73, -70, + -67, -63, -60, -57, -54, -50, -47, -44, -40, -37, -34, + -30, -27, -24, -20, -17, -14, -10, -7, -4, 0, 3, + 6, 10, 13, 16, 20, 23, 26, 30, 33, 36, 39, + 43, 46, 49, 53, 56, 59, 63, 66, 69, 73, 76, + 79, 83, 86, 89, 93, 96, 99, 103, 106, 109, 113, + 116, 119, 123, 126, 129, 132, 136, 139, 142, 146, 149, + 152, 156, 159, 162, 166, 169, 172, 176, 179, 182, 186, + 189, 192, 196, 199, 202, 206, 209, 212, 216, 219, 222, + 226, 229, 232, 235, 239, 242, 245, 249, 252, 255, 259, + 262, 265, 269, 272, 275, 279, 282, 285, 289, 292, 295, + 299, 302, 305, 309, 312, 315, 319, 322, 325, 328, 332, + 335, 338, 342, 345, 348, 352, 355, 358, 362, 365, 368, + 372, 375, 378, 382, 385, 388, 392, 395, 398, 402, 405, + 408, 412, 415, 418, 422, 425, 428, 431, 435, 438, 441, + 445, 448, 451, 455, 458, 461, 465, 468, 471, 475, 478, + 481, 485, 488, 491, 495, 498, 501, 505, 508, 511, 515, + 518, 521, 524, 528, 531, 534, 538, 541, 544, 548, 551, + 554, 558, 561, 564, 568, 571, 574, 578, 581, 584, 588, + 591, 594, 598, 601, 604, 608, 611, 614, 617, 621, 624, + 627, 631, 634, 637, 641, 644, 647, 651, 654, 657, 661, + 664, 667, 671, 674, 677, 681, 684, 687, 691, 694, 697, + 701, 704, 707, 711, 714, 717, 720, 724, 727, 730, 734, + 737, 740, 744, 747, 750, 754, 757, 760, 764, 767, 770, + 774, 777, 780, 784, 787, 790, 794, 797, 800, 804, 807, + 810, 813, 817, 820, 823, 827, 830, 833, 837, 840, 843, + 847, 850, 853, 857, 860, 863, 867, 870, 873, 877, 880, + 883, 887, 890, 893, 897, 900, 903, 907, 910, 913, 916, + 920, 923, 926, 930, 933, 936, 940, 943, 946, 950, 953, + 956, 960, +}; + +} // namespace +} // inline namespace lts_2018_06_20 +} // namespace absl diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h new file mode 100644 index 00000000000..fe2dee3749a --- /dev/null +++ b/absl/strings/charconv.h @@ -0,0 +1,117 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_CHARCONV_H_ +#define ABSL_STRINGS_CHARCONV_H_ + +#include // NOLINT(build/c++11) + +namespace absl { +inline namespace lts_2018_06_20 { + +// Workalike compatibilty version of std::chars_format from C++17. +// +// This is an bitfield enumerator which can be passed to absl::from_chars to +// configure the std::string-to-float conversion. +enum class chars_format { + scientific = 1, + fixed = 2, + hex = 4, + general = fixed | scientific, +}; + +// The return result of a std::string-to-number conversion. +// +// `ec` will be set to `invalid_argument` if a well-formed number was not found +// at the start of the input range, `result_out_of_range` if a well-formed +// number was found, but it was out of the representable range of the requested +// type, or to std::errc() otherwise. +// +// If a well-formed number was found, `ptr` is set to one past the sequence of +// characters that were successfully parsed. If none was found, `ptr` is set +// to the `first` argument to from_chars. +struct from_chars_result { + const char* ptr; + std::errc ec; +}; + +// Workalike compatibilty version of std::from_chars from C++17. Currently +// this only supports the `double` and `float` types. +// +// This interface incorporates the proposed resolutions for library issues +// DR 3800 and DR 3801. If these are adopted with different wording, +// Abseil's behavior will change to match the standard. (The behavior most +// likely to change is for DR 3801, which says what `value` will be set to in +// the case of overflow and underflow. Code that wants to avoid possible +// breaking changes in this area should not depend on `value` when the returned +// from_chars_result indicates a range error.) +// +// Searches the range [first, last) for the longest matching pattern beginning +// at `first` that represents a floating point number. If one is found, store +// the result in `value`. +// +// The matching pattern format is almost the same as that of strtod(), except +// that C locale is not respected, and an initial '+' character in the input +// range will never be matched. +// +// If `fmt` is set, it must be one of the enumerator values of the chars_format. +// (This is despite the fact that chars_format is a bitmask type.) If set to +// `scientific`, a matching number must contain an exponent. If set to `fixed`, +// then an exponent will never match. (For example, the std::string "1e5" will be +// parsed as "1".) If set to `hex`, then a hexadecimal float is parsed in the +// format that strtod() accepts, except that a "0x" prefix is NOT matched. +// (In particular, in `hex` mode, the input "0xff" results in the largest +// matching pattern "0".) +absl::from_chars_result from_chars(const char* first, const char* last, + double& value, // NOLINT + chars_format fmt = chars_format::general); + +absl::from_chars_result from_chars(const char* first, const char* last, + float& value, // NOLINT + chars_format fmt = chars_format::general); + +// std::chars_format is specified as a bitmask type, which means the following +// operations must be provided: +inline constexpr chars_format operator&(chars_format lhs, chars_format rhs) { + return static_cast(static_cast(lhs) & + static_cast(rhs)); +} +inline constexpr chars_format operator|(chars_format lhs, chars_format rhs) { + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} +inline constexpr chars_format operator^(chars_format lhs, chars_format rhs) { + return static_cast(static_cast(lhs) ^ + static_cast(rhs)); +} +inline constexpr chars_format operator~(chars_format arg) { + return static_cast(~static_cast(arg)); +} +inline chars_format& operator&=(chars_format& lhs, chars_format rhs) { + lhs = lhs & rhs; + return lhs; +} +inline chars_format& operator|=(chars_format& lhs, chars_format rhs) { + lhs = lhs | rhs; + return lhs; +} +inline chars_format& operator^=(chars_format& lhs, chars_format rhs) { + lhs = lhs ^ rhs; + return lhs; +} + +} // inline namespace lts_2018_06_20 +} // namespace absl + +#endif // ABSL_STRINGS_CHARCONV_H_ diff --git a/absl/strings/charconv_benchmark.cc b/absl/strings/charconv_benchmark.cc new file mode 100644 index 00000000000..fd83f44e3d0 --- /dev/null +++ b/absl/strings/charconv_benchmark.cc @@ -0,0 +1,204 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/charconv.h" + +#include +#include +#include + +#include "benchmark/benchmark.h" + +namespace { + +void BM_Strtod_Pi(benchmark::State& state) { + const char* pi = "3.14159"; + for (auto s : state) { + benchmark::DoNotOptimize(pi); + benchmark::DoNotOptimize(strtod(pi, nullptr)); + } +} +BENCHMARK(BM_Strtod_Pi); + +void BM_Absl_Pi(benchmark::State& state) { + const char* pi = "3.14159"; + const char* pi_end = pi + strlen(pi); + for (auto s : state) { + benchmark::DoNotOptimize(pi); + double v; + absl::from_chars(pi, pi_end, v); + benchmark::DoNotOptimize(v); + } +} +BENCHMARK(BM_Absl_Pi); + +void BM_Strtod_Pi_float(benchmark::State& state) { + const char* pi = "3.14159"; + for (auto s : state) { + benchmark::DoNotOptimize(pi); + benchmark::DoNotOptimize(strtof(pi, nullptr)); + } +} +BENCHMARK(BM_Strtod_Pi_float); + +void BM_Absl_Pi_float(benchmark::State& state) { + const char* pi = "3.14159"; + const char* pi_end = pi + strlen(pi); + for (auto s : state) { + benchmark::DoNotOptimize(pi); + float v; + absl::from_chars(pi, pi_end, v); + benchmark::DoNotOptimize(v); + } +} +BENCHMARK(BM_Absl_Pi_float); + +void BM_Strtod_HardLarge(benchmark::State& state) { + const char* num = "272104041512242479.e200"; + for (auto s : state) { + benchmark::DoNotOptimize(num); + benchmark::DoNotOptimize(strtod(num, nullptr)); + } +} +BENCHMARK(BM_Strtod_HardLarge); + +void BM_Absl_HardLarge(benchmark::State& state) { + const char* numstr = "272104041512242479.e200"; + const char* numstr_end = numstr + strlen(numstr); + for (auto s : state) { + benchmark::DoNotOptimize(numstr); + double v; + absl::from_chars(numstr, numstr_end, v); + benchmark::DoNotOptimize(v); + } +} +BENCHMARK(BM_Absl_HardLarge); + +void BM_Strtod_HardSmall(benchmark::State& state) { + const char* num = "94080055902682397.e-242"; + for (auto s : state) { + benchmark::DoNotOptimize(num); + benchmark::DoNotOptimize(strtod(num, nullptr)); + } +} +BENCHMARK(BM_Strtod_HardSmall); + +void BM_Absl_HardSmall(benchmark::State& state) { + const char* numstr = "94080055902682397.e-242"; + const char* numstr_end = numstr + strlen(numstr); + for (auto s : state) { + benchmark::DoNotOptimize(numstr); + double v; + absl::from_chars(numstr, numstr_end, v); + benchmark::DoNotOptimize(v); + } +} +BENCHMARK(BM_Absl_HardSmall); + +void BM_Strtod_HugeMantissa(benchmark::State& state) { + std::string huge(200, '3'); + const char* num = huge.c_str(); + for (auto s : state) { + benchmark::DoNotOptimize(num); + benchmark::DoNotOptimize(strtod(num, nullptr)); + } +} +BENCHMARK(BM_Strtod_HugeMantissa); + +void BM_Absl_HugeMantissa(benchmark::State& state) { + std::string huge(200, '3'); + const char* num = huge.c_str(); + const char* num_end = num + 200; + for (auto s : state) { + benchmark::DoNotOptimize(num); + double v; + absl::from_chars(num, num_end, v); + benchmark::DoNotOptimize(v); + } +} +BENCHMARK(BM_Absl_HugeMantissa); + +std::string MakeHardCase(int length) { + // The number 1.1521...e-297 is exactly halfway between 12345 * 2**-1000 and + // the next larger representable number. The digits of this number are in + // the std::string below. + const std::string digits = + "1." + "152113937042223790993097181572444900347587985074226836242307364987727724" + "831384300183638649152607195040591791364113930628852279348613864894524591" + "272746490313676832900762939595690019745859128071117417798540258114233761" + "012939937017879509401007964861774960297319002612457273148497158989073482" + "171377406078223015359818300988676687994537274548940612510414856761641652" + "513434981938564294004070500716200446656421722229202383105446378511678258" + "370570631774499359748259931676320916632111681001853983492795053244971606" + "922718923011680846577744433974087653954904214152517799883551075537146316" + "168973685866425605046988661997658648354773076621610279716804960009043764" + "038392994055171112475093876476783502487512538082706095923790634572014823" + "78877699375152587890625" + + std::string(5000, '0'); + // generate the hard cases on either side for the given length. + // Lengths between 3 and 1000 are reasonable. + return digits.substr(0, length) + "1e-297"; +} + +void BM_Strtod_Big_And_Difficult(benchmark::State& state) { + std::string testcase = MakeHardCase(state.range(0)); + const char* begin = testcase.c_str(); + for (auto s : state) { + benchmark::DoNotOptimize(begin); + benchmark::DoNotOptimize(strtod(begin, nullptr)); + } +} +BENCHMARK(BM_Strtod_Big_And_Difficult)->Range(3, 5000); + +void BM_Absl_Big_And_Difficult(benchmark::State& state) { + std::string testcase = MakeHardCase(state.range(0)); + const char* begin = testcase.c_str(); + const char* end = begin + testcase.size(); + for (auto s : state) { + benchmark::DoNotOptimize(begin); + double v; + absl::from_chars(begin, end, v); + benchmark::DoNotOptimize(v); + } +} +BENCHMARK(BM_Absl_Big_And_Difficult)->Range(3, 5000); + +} // namespace + +// ------------------------------------------------------------------------ +// Benchmark Time CPU Iterations +// ------------------------------------------------------------------------ +// BM_Strtod_Pi 96 ns 96 ns 6337454 +// BM_Absl_Pi 35 ns 35 ns 20031996 +// BM_Strtod_Pi_float 91 ns 91 ns 7745851 +// BM_Absl_Pi_float 35 ns 35 ns 20430298 +// BM_Strtod_HardLarge 133 ns 133 ns 5288341 +// BM_Absl_HardLarge 181 ns 181 ns 3855615 +// BM_Strtod_HardSmall 279 ns 279 ns 2517243 +// BM_Absl_HardSmall 287 ns 287 ns 2458744 +// BM_Strtod_HugeMantissa 433 ns 433 ns 1604293 +// BM_Absl_HugeMantissa 160 ns 160 ns 4403671 +// BM_Strtod_Big_And_Difficult/3 236 ns 236 ns 2942496 +// BM_Strtod_Big_And_Difficult/8 232 ns 232 ns 2983796 +// BM_Strtod_Big_And_Difficult/64 437 ns 437 ns 1591951 +// BM_Strtod_Big_And_Difficult/512 1738 ns 1738 ns 402519 +// BM_Strtod_Big_And_Difficult/4096 3943 ns 3943 ns 176128 +// BM_Strtod_Big_And_Difficult/5000 4397 ns 4397 ns 157878 +// BM_Absl_Big_And_Difficult/3 39 ns 39 ns 17799583 +// BM_Absl_Big_And_Difficult/8 43 ns 43 ns 16096859 +// BM_Absl_Big_And_Difficult/64 550 ns 550 ns 1259717 +// BM_Absl_Big_And_Difficult/512 4167 ns 4167 ns 171414 +// BM_Absl_Big_And_Difficult/4096 9160 ns 9159 ns 76297 +// BM_Absl_Big_And_Difficult/5000 9738 ns 9738 ns 70140 diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc new file mode 100644 index 00000000000..f8d71cc6c44 --- /dev/null +++ b/absl/strings/charconv_test.cc @@ -0,0 +1,766 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/charconv.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" + +#ifdef _MSC_FULL_VER +#define ABSL_COMPILER_DOES_EXACT_ROUNDING 0 +#define ABSL_STRTOD_HANDLES_NAN_CORRECTLY 0 +#else +#define ABSL_COMPILER_DOES_EXACT_ROUNDING 1 +#define ABSL_STRTOD_HANDLES_NAN_CORRECTLY 1 +#endif + +namespace { + +#if ABSL_COMPILER_DOES_EXACT_ROUNDING + +// Tests that the given std::string is accepted by absl::from_chars, and that it +// converts exactly equal to the given number. +void TestDoubleParse(absl::string_view str, double expected_number) { + SCOPED_TRACE(str); + double actual_number = 0.0; + absl::from_chars_result result = + absl::from_chars(str.data(), str.data() + str.length(), actual_number); + EXPECT_EQ(result.ec, std::errc()); + EXPECT_EQ(result.ptr, str.data() + str.length()); + EXPECT_EQ(actual_number, expected_number); +} + +void TestFloatParse(absl::string_view str, float expected_number) { + SCOPED_TRACE(str); + float actual_number = 0.0; + absl::from_chars_result result = + absl::from_chars(str.data(), str.data() + str.length(), actual_number); + EXPECT_EQ(result.ec, std::errc()); + EXPECT_EQ(result.ptr, str.data() + str.length()); + EXPECT_EQ(actual_number, expected_number); +} + +// Tests that the given double or single precision floating point literal is +// parsed correctly by absl::from_chars. +// +// These convenience macros assume that the C++ compiler being used also does +// fully correct decimal-to-binary conversions. +#define FROM_CHARS_TEST_DOUBLE(number) \ + { \ + TestDoubleParse(#number, number); \ + TestDoubleParse("-" #number, -number); \ + } + +#define FROM_CHARS_TEST_FLOAT(number) \ + { \ + TestFloatParse(#number, number##f); \ + TestFloatParse("-" #number, -number##f); \ + } + +TEST(FromChars, NearRoundingCases) { + // Cases from "A Program for Testing IEEE Decimal-Binary Conversion" + // by Vern Paxson. + + // Forms that should round towards zero. (These are the hardest cases for + // each decimal mantissa size.) + FROM_CHARS_TEST_DOUBLE(5.e125); + FROM_CHARS_TEST_DOUBLE(69.e267); + FROM_CHARS_TEST_DOUBLE(999.e-026); + FROM_CHARS_TEST_DOUBLE(7861.e-034); + FROM_CHARS_TEST_DOUBLE(75569.e-254); + FROM_CHARS_TEST_DOUBLE(928609.e-261); + FROM_CHARS_TEST_DOUBLE(9210917.e080); + FROM_CHARS_TEST_DOUBLE(84863171.e114); + FROM_CHARS_TEST_DOUBLE(653777767.e273); + FROM_CHARS_TEST_DOUBLE(5232604057.e-298); + FROM_CHARS_TEST_DOUBLE(27235667517.e-109); + FROM_CHARS_TEST_DOUBLE(653532977297.e-123); + FROM_CHARS_TEST_DOUBLE(3142213164987.e-294); + FROM_CHARS_TEST_DOUBLE(46202199371337.e-072); + FROM_CHARS_TEST_DOUBLE(231010996856685.e-073); + FROM_CHARS_TEST_DOUBLE(9324754620109615.e212); + FROM_CHARS_TEST_DOUBLE(78459735791271921.e049); + FROM_CHARS_TEST_DOUBLE(272104041512242479.e200); + FROM_CHARS_TEST_DOUBLE(6802601037806061975.e198); + FROM_CHARS_TEST_DOUBLE(20505426358836677347.e-221); + FROM_CHARS_TEST_DOUBLE(836168422905420598437.e-234); + FROM_CHARS_TEST_DOUBLE(4891559871276714924261.e222); + FROM_CHARS_TEST_FLOAT(5.e-20); + FROM_CHARS_TEST_FLOAT(67.e14); + FROM_CHARS_TEST_FLOAT(985.e15); + FROM_CHARS_TEST_FLOAT(7693.e-42); + FROM_CHARS_TEST_FLOAT(55895.e-16); + FROM_CHARS_TEST_FLOAT(996622.e-44); + FROM_CHARS_TEST_FLOAT(7038531.e-32); + FROM_CHARS_TEST_FLOAT(60419369.e-46); + FROM_CHARS_TEST_FLOAT(702990899.e-20); + FROM_CHARS_TEST_FLOAT(6930161142.e-48); + FROM_CHARS_TEST_FLOAT(25933168707.e-13); + FROM_CHARS_TEST_FLOAT(596428896559.e20); + + // Similarly, forms that should round away from zero. + FROM_CHARS_TEST_DOUBLE(9.e-265); + FROM_CHARS_TEST_DOUBLE(85.e-037); + FROM_CHARS_TEST_DOUBLE(623.e100); + FROM_CHARS_TEST_DOUBLE(3571.e263); + FROM_CHARS_TEST_DOUBLE(81661.e153); + FROM_CHARS_TEST_DOUBLE(920657.e-023); + FROM_CHARS_TEST_DOUBLE(4603285.e-024); + FROM_CHARS_TEST_DOUBLE(87575437.e-309); + FROM_CHARS_TEST_DOUBLE(245540327.e122); + FROM_CHARS_TEST_DOUBLE(6138508175.e120); + FROM_CHARS_TEST_DOUBLE(83356057653.e193); + FROM_CHARS_TEST_DOUBLE(619534293513.e124); + FROM_CHARS_TEST_DOUBLE(2335141086879.e218); + FROM_CHARS_TEST_DOUBLE(36167929443327.e-159); + FROM_CHARS_TEST_DOUBLE(609610927149051.e-255); + FROM_CHARS_TEST_DOUBLE(3743626360493413.e-165); + FROM_CHARS_TEST_DOUBLE(94080055902682397.e-242); + FROM_CHARS_TEST_DOUBLE(899810892172646163.e283); + FROM_CHARS_TEST_DOUBLE(7120190517612959703.e120); + FROM_CHARS_TEST_DOUBLE(25188282901709339043.e-252); + FROM_CHARS_TEST_DOUBLE(308984926168550152811.e-052); + FROM_CHARS_TEST_DOUBLE(6372891218502368041059.e064); + FROM_CHARS_TEST_FLOAT(3.e-23); + FROM_CHARS_TEST_FLOAT(57.e18); + FROM_CHARS_TEST_FLOAT(789.e-35); + FROM_CHARS_TEST_FLOAT(2539.e-18); + FROM_CHARS_TEST_FLOAT(76173.e28); + FROM_CHARS_TEST_FLOAT(887745.e-11); + FROM_CHARS_TEST_FLOAT(5382571.e-37); + FROM_CHARS_TEST_FLOAT(82381273.e-35); + FROM_CHARS_TEST_FLOAT(750486563.e-38); + FROM_CHARS_TEST_FLOAT(3752432815.e-39); + FROM_CHARS_TEST_FLOAT(75224575729.e-45); + FROM_CHARS_TEST_FLOAT(459926601011.e15); +} + +#undef FROM_CHARS_TEST_DOUBLE +#undef FROM_CHARS_TEST_FLOAT +#endif + +float ToFloat(absl::string_view s) { + float f; + absl::from_chars(s.data(), s.data() + s.size(), f); + return f; +} + +double ToDouble(absl::string_view s) { + double d; + absl::from_chars(s.data(), s.data() + s.size(), d); + return d; +} + +// A duplication of the test cases in "NearRoundingCases" above, but with +// expected values expressed with integers, using ldexp/ldexpf. These test +// cases will work even on compilers that do not accurately round floating point +// literals. +TEST(FromChars, NearRoundingCasesExplicit) { + EXPECT_EQ(ToDouble("5.e125"), ldexp(6653062250012735, 365)); + EXPECT_EQ(ToDouble("69.e267"), ldexp(4705683757438170, 841)); + EXPECT_EQ(ToDouble("999.e-026"), ldexp(6798841691080350, -129)); + EXPECT_EQ(ToDouble("7861.e-034"), ldexp(8975675289889240, -153)); + EXPECT_EQ(ToDouble("75569.e-254"), ldexp(6091718967192243, -880)); + EXPECT_EQ(ToDouble("928609.e-261"), ldexp(7849264900213743, -900)); + EXPECT_EQ(ToDouble("9210917.e080"), ldexp(8341110837370930, 236)); + EXPECT_EQ(ToDouble("84863171.e114"), ldexp(4625202867375927, 353)); + EXPECT_EQ(ToDouble("653777767.e273"), ldexp(5068902999763073, 884)); + EXPECT_EQ(ToDouble("5232604057.e-298"), ldexp(5741343011915040, -1010)); + EXPECT_EQ(ToDouble("27235667517.e-109"), ldexp(6707124626673586, -380)); + EXPECT_EQ(ToDouble("653532977297.e-123"), ldexp(7078246407265384, -422)); + EXPECT_EQ(ToDouble("3142213164987.e-294"), ldexp(8219991337640559, -988)); + EXPECT_EQ(ToDouble("46202199371337.e-072"), ldexp(5224462102115359, -246)); + EXPECT_EQ(ToDouble("231010996856685.e-073"), ldexp(5224462102115359, -247)); + EXPECT_EQ(ToDouble("9324754620109615.e212"), ldexp(5539753864394442, 705)); + EXPECT_EQ(ToDouble("78459735791271921.e049"), ldexp(8388176519442766, 166)); + EXPECT_EQ(ToDouble("272104041512242479.e200"), ldexp(5554409530847367, 670)); + EXPECT_EQ(ToDouble("6802601037806061975.e198"), ldexp(5554409530847367, 668)); + EXPECT_EQ(ToDouble("20505426358836677347.e-221"), + ldexp(4524032052079546, -722)); + EXPECT_EQ(ToDouble("836168422905420598437.e-234"), + ldexp(5070963299887562, -760)); + EXPECT_EQ(ToDouble("4891559871276714924261.e222"), + ldexp(6452687840519111, 757)); + EXPECT_EQ(ToFloat("5.e-20"), ldexpf(15474250, -88)); + EXPECT_EQ(ToFloat("67.e14"), ldexpf(12479722, 29)); + EXPECT_EQ(ToFloat("985.e15"), ldexpf(14333636, 36)); + EXPECT_EQ(ToFloat("7693.e-42"), ldexpf(10979816, -150)); + EXPECT_EQ(ToFloat("55895.e-16"), ldexpf(12888509, -61)); + EXPECT_EQ(ToFloat("996622.e-44"), ldexpf(14224264, -150)); + EXPECT_EQ(ToFloat("7038531.e-32"), ldexpf(11420669, -107)); + EXPECT_EQ(ToFloat("60419369.e-46"), ldexpf(8623340, -150)); + EXPECT_EQ(ToFloat("702990899.e-20"), ldexpf(16209866, -61)); + EXPECT_EQ(ToFloat("6930161142.e-48"), ldexpf(9891056, -150)); + EXPECT_EQ(ToFloat("25933168707.e-13"), ldexpf(11138211, -32)); + EXPECT_EQ(ToFloat("596428896559.e20"), ldexpf(12333860, 82)); + + + EXPECT_EQ(ToDouble("9.e-265"), ldexp(8168427841980010, -930)); + EXPECT_EQ(ToDouble("85.e-037"), ldexp(6360455125664090, -169)); + EXPECT_EQ(ToDouble("623.e100"), ldexp(6263531988747231, 289)); + EXPECT_EQ(ToDouble("3571.e263"), ldexp(6234526311072170, 833)); + EXPECT_EQ(ToDouble("81661.e153"), ldexp(6696636728760206, 472)); + EXPECT_EQ(ToDouble("920657.e-023"), ldexp(5975405561110124, -109)); + EXPECT_EQ(ToDouble("4603285.e-024"), ldexp(5975405561110124, -110)); + EXPECT_EQ(ToDouble("87575437.e-309"), ldexp(8452160731874668, -1053)); + EXPECT_EQ(ToDouble("245540327.e122"), ldexp(4985336549131723, 381)); + EXPECT_EQ(ToDouble("6138508175.e120"), ldexp(4985336549131723, 379)); + EXPECT_EQ(ToDouble("83356057653.e193"), ldexp(5986732817132056, 625)); + EXPECT_EQ(ToDouble("619534293513.e124"), ldexp(4798406992060657, 399)); + EXPECT_EQ(ToDouble("2335141086879.e218"), ldexp(5419088166961646, 713)); + EXPECT_EQ(ToDouble("36167929443327.e-159"), ldexp(8135819834632444, -536)); + EXPECT_EQ(ToDouble("609610927149051.e-255"), ldexp(4576664294594737, -850)); + EXPECT_EQ(ToDouble("3743626360493413.e-165"), ldexp(6898586531774201, -549)); + EXPECT_EQ(ToDouble("94080055902682397.e-242"), ldexp(6273271706052298, -800)); + EXPECT_EQ(ToDouble("899810892172646163.e283"), ldexp(7563892574477827, 947)); + EXPECT_EQ(ToDouble("7120190517612959703.e120"), ldexp(5385467232557565, 409)); + EXPECT_EQ(ToDouble("25188282901709339043.e-252"), + ldexp(5635662608542340, -825)); + EXPECT_EQ(ToDouble("308984926168550152811.e-052"), + ldexp(5644774693823803, -157)); + EXPECT_EQ(ToDouble("6372891218502368041059.e064"), + ldexp(4616868614322430, 233)); + + EXPECT_EQ(ToFloat("3.e-23"), ldexpf(9507380, -98)); + EXPECT_EQ(ToFloat("57.e18"), ldexpf(12960300, 42)); + EXPECT_EQ(ToFloat("789.e-35"), ldexpf(10739312, -130)); + EXPECT_EQ(ToFloat("2539.e-18"), ldexpf(11990089, -72)); + EXPECT_EQ(ToFloat("76173.e28"), ldexpf(9845130, 86)); + EXPECT_EQ(ToFloat("887745.e-11"), ldexpf(9760860, -40)); + EXPECT_EQ(ToFloat("5382571.e-37"), ldexpf(11447463, -124)); + EXPECT_EQ(ToFloat("82381273.e-35"), ldexpf(8554961, -113)); + EXPECT_EQ(ToFloat("750486563.e-38"), ldexpf(9975678, -120)); + EXPECT_EQ(ToFloat("3752432815.e-39"), ldexpf(9975678, -121)); + EXPECT_EQ(ToFloat("75224575729.e-45"), ldexpf(13105970, -137)); + EXPECT_EQ(ToFloat("459926601011.e15"), ldexpf(12466336, 65)); +} + +// Common test logic for converting a std::string which lies exactly halfway between +// two target floats. +// +// mantissa and exponent represent the precise value between two floating point +// numbers, `expected_low` and `expected_high`. The floating point +// representation to parse in `StrCat(mantissa, "e", exponent)`. +// +// This function checks that an input just slightly less than the exact value +// is rounded down to `expected_low`, and an input just slightly greater than +// the exact value is rounded up to `expected_high`. +// +// The exact value should round to `expected_half`, which must be either +// `expected_low` or `expected_high`. +template +void TestHalfwayValue(const std::string& mantissa, int exponent, + FloatType expected_low, FloatType expected_high, + FloatType expected_half) { + std::string low_rep = mantissa; + low_rep[low_rep.size() - 1] -= 1; + absl::StrAppend(&low_rep, std::string(1000, '9'), "e", exponent); + + FloatType actual_low = 0; + absl::from_chars(low_rep.data(), low_rep.data() + low_rep.size(), actual_low); + EXPECT_EQ(expected_low, actual_low); + + std::string high_rep = absl::StrCat(mantissa, std::string(1000, '0'), "1e", exponent); + FloatType actual_high = 0; + absl::from_chars(high_rep.data(), high_rep.data() + high_rep.size(), + actual_high); + EXPECT_EQ(expected_high, actual_high); + + std::string halfway_rep = absl::StrCat(mantissa, "e", exponent); + FloatType actual_half = 0; + absl::from_chars(halfway_rep.data(), halfway_rep.data() + halfway_rep.size(), + actual_half); + EXPECT_EQ(expected_half, actual_half); +} + +TEST(FromChars, DoubleRounding) { + const double zero = 0.0; + const double first_subnormal = nextafter(zero, 1.0); + const double second_subnormal = nextafter(first_subnormal, 1.0); + + const double first_normal = DBL_MIN; + const double last_subnormal = nextafter(first_normal, 0.0); + const double second_normal = nextafter(first_normal, 1.0); + + const double last_normal = DBL_MAX; + const double penultimate_normal = nextafter(last_normal, 0.0); + + // Various test cases for numbers between two representable floats. Each + // call to TestHalfwayValue tests a number just below and just above the + // halfway point, as well as the number exactly between them. + + // Test between zero and first_subnormal. Round-to-even tie rounds down. + TestHalfwayValue( + "2." + "470328229206232720882843964341106861825299013071623822127928412503377536" + "351043759326499181808179961898982823477228588654633283551779698981993873" + "980053909390631503565951557022639229085839244910518443593180284993653615" + "250031937045767824921936562366986365848075700158576926990370631192827955" + "855133292783433840935197801553124659726357957462276646527282722005637400" + "648549997709659947045402082816622623785739345073633900796776193057750674" + "017632467360096895134053553745851666113422376667860416215968046191446729" + "184030053005753084904876539171138659164623952491262365388187963623937328" + "042389101867234849766823508986338858792562830275599565752445550725518931" + "369083625477918694866799496832404970582102851318545139621383772282614543" + "7693412532098591327667236328125", + -324, zero, first_subnormal, zero); + + // first_subnormal and second_subnormal. Round-to-even tie rounds up. + TestHalfwayValue( + "7." + "410984687618698162648531893023320585475897039214871466383785237510132609" + "053131277979497545424539885696948470431685765963899850655339096945981621" + "940161728171894510697854671067917687257517734731555330779540854980960845" + "750095811137303474765809687100959097544227100475730780971111893578483867" + "565399878350301522805593404659373979179073872386829939581848166016912201" + "945649993128979841136206248449867871357218035220901702390328579173252022" + "052897402080290685402160661237554998340267130003581248647904138574340187" + "552090159017259254714629617513415977493871857473787096164563890871811984" + "127167305601704549300470526959016576377688490826798697257336652176556794" + "107250876433756084600398490497214911746308553955635418864151316847843631" + "3080237596295773983001708984375", + -324, first_subnormal, second_subnormal, second_subnormal); + + // last_subnormal and first_normal. Round-to-even tie rounds up. + TestHalfwayValue( + "2." + "225073858507201136057409796709131975934819546351645648023426109724822222" + "021076945516529523908135087914149158913039621106870086438694594645527657" + "207407820621743379988141063267329253552286881372149012981122451451889849" + "057222307285255133155755015914397476397983411801999323962548289017107081" + "850690630666655994938275772572015763062690663332647565300009245888316433" + "037779791869612049497390377829704905051080609940730262937128958950003583" + "799967207254304360284078895771796150945516748243471030702609144621572289" + "880258182545180325707018860872113128079512233426288368622321503775666622" + "503982534335974568884423900265498198385487948292206894721689831099698365" + "846814022854243330660339850886445804001034933970427567186443383770486037" + "86162277173854562306587467901408672332763671875", + -308, last_subnormal, first_normal, first_normal); + + // first_normal and second_normal. Round-to-even tie rounds down. + TestHalfwayValue( + "2." + "225073858507201630123055637955676152503612414573018013083228724049586647" + "606759446192036794116886953213985520549032000903434781884412325572184367" + "563347617020518175998922941393629966742598285899994830148971433555578567" + "693279306015978183162142425067962460785295885199272493577688320732492479" + "924816869232247165964934329258783950102250973957579510571600738343645738" + "494324192997092179207389919761694314131497173265255020084997973676783743" + "155205818804439163810572367791175177756227497413804253387084478193655533" + "073867420834526162513029462022730109054820067654020201547112002028139700" + "141575259123440177362244273712468151750189745559978653234255886219611516" + "335924167958029604477064946470184777360934300451421683607013647479513962" + "13837722826145437693412532098591327667236328125", + -308, first_normal, second_normal, first_normal); + + // penultimate_normal and last_normal. Round-to-even rounds down. + TestHalfwayValue( + "1." + "797693134862315608353258760581052985162070023416521662616611746258695532" + "672923265745300992879465492467506314903358770175220871059269879629062776" + "047355692132901909191523941804762171253349609463563872612866401980290377" + "995141836029815117562837277714038305214839639239356331336428021390916694" + "57927874464075218944", + 308, penultimate_normal, last_normal, penultimate_normal); +} + +// Same test cases as DoubleRounding, now with new and improved Much Smaller +// Precision! +TEST(FromChars, FloatRounding) { + const float zero = 0.0; + const float first_subnormal = nextafterf(zero, 1.0); + const float second_subnormal = nextafterf(first_subnormal, 1.0); + + const float first_normal = FLT_MIN; + const float last_subnormal = nextafterf(first_normal, 0.0); + const float second_normal = nextafterf(first_normal, 1.0); + + const float last_normal = FLT_MAX; + const float penultimate_normal = nextafterf(last_normal, 0.0); + + // Test between zero and first_subnormal. Round-to-even tie rounds down. + TestHalfwayValue( + "7." + "006492321624085354618647916449580656401309709382578858785341419448955413" + "42930300743319094181060791015625", + -46, zero, first_subnormal, zero); + + // first_subnormal and second_subnormal. Round-to-even tie rounds up. + TestHalfwayValue( + "2." + "101947696487225606385594374934874196920392912814773657635602425834686624" + "028790902229957282543182373046875", + -45, first_subnormal, second_subnormal, second_subnormal); + + // last_subnormal and first_normal. Round-to-even tie rounds up. + TestHalfwayValue( + "1." + "175494280757364291727882991035766513322858992758990427682963118425003064" + "9651730385585324256680905818939208984375", + -38, last_subnormal, first_normal, first_normal); + + // first_normal and second_normal. Round-to-even tie rounds down. + TestHalfwayValue( + "1." + "175494420887210724209590083408724842314472120785184615334540294131831453" + "9442813071445925743319094181060791015625", + -38, first_normal, second_normal, first_normal); + + // penultimate_normal and last_normal. Round-to-even rounds down. + TestHalfwayValue("3.40282336497324057985868971510891282432", 38, + penultimate_normal, last_normal, penultimate_normal); +} + +TEST(FromChars, Underflow) { + // Check that underflow is handled correctly, according to the specification + // in DR 3081. + double d; + float f; + absl::from_chars_result result; + + std::string negative_underflow = "-1e-1000"; + const char* begin = negative_underflow.data(); + const char* end = begin + negative_underflow.size(); + d = 100.0; + result = absl::from_chars(begin, end, d); + EXPECT_EQ(result.ptr, end); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_TRUE(std::signbit(d)); // negative + EXPECT_GE(d, -std::numeric_limits::min()); + f = 100.0; + result = absl::from_chars(begin, end, f); + EXPECT_EQ(result.ptr, end); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_TRUE(std::signbit(f)); // negative + EXPECT_GE(f, -std::numeric_limits::min()); + + std::string positive_underflow = "1e-1000"; + begin = positive_underflow.data(); + end = begin + positive_underflow.size(); + d = -100.0; + result = absl::from_chars(begin, end, d); + EXPECT_EQ(result.ptr, end); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_FALSE(std::signbit(d)); // positive + EXPECT_LE(d, std::numeric_limits::min()); + f = -100.0; + result = absl::from_chars(begin, end, f); + EXPECT_EQ(result.ptr, end); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_FALSE(std::signbit(f)); // positive + EXPECT_LE(f, std::numeric_limits::min()); +} + +TEST(FromChars, Overflow) { + // Check that overflow is handled correctly, according to the specification + // in DR 3081. + double d; + float f; + absl::from_chars_result result; + + std::string negative_overflow = "-1e1000"; + const char* begin = negative_overflow.data(); + const char* end = begin + negative_overflow.size(); + d = 100.0; + result = absl::from_chars(begin, end, d); + EXPECT_EQ(result.ptr, end); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_TRUE(std::signbit(d)); // negative + EXPECT_EQ(d, -std::numeric_limits::max()); + f = 100.0; + result = absl::from_chars(begin, end, f); + EXPECT_EQ(result.ptr, end); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_TRUE(std::signbit(f)); // negative + EXPECT_EQ(f, -std::numeric_limits::max()); + + std::string positive_overflow = "1e1000"; + begin = positive_overflow.data(); + end = begin + positive_overflow.size(); + d = -100.0; + result = absl::from_chars(begin, end, d); + EXPECT_EQ(result.ptr, end); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_FALSE(std::signbit(d)); // positive + EXPECT_EQ(d, std::numeric_limits::max()); + f = -100.0; + result = absl::from_chars(begin, end, f); + EXPECT_EQ(result.ptr, end); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_FALSE(std::signbit(f)); // positive + EXPECT_EQ(f, std::numeric_limits::max()); +} + +TEST(FromChars, ReturnValuePtr) { + // Check that `ptr` points one past the number scanned, even if that number + // is not representable. + double d; + absl::from_chars_result result; + + std::string normal = "3.14@#$%@#$%"; + result = absl::from_chars(normal.data(), normal.data() + normal.size(), d); + EXPECT_EQ(result.ec, std::errc()); + EXPECT_EQ(result.ptr - normal.data(), 4); + + std::string overflow = "1e1000@#$%@#$%"; + result = absl::from_chars(overflow.data(), + overflow.data() + overflow.size(), d); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_EQ(result.ptr - overflow.data(), 6); + + std::string garbage = "#$%@#$%"; + result = absl::from_chars(garbage.data(), + garbage.data() + garbage.size(), d); + EXPECT_EQ(result.ec, std::errc::invalid_argument); + EXPECT_EQ(result.ptr - garbage.data(), 0); +} + +// Check for a wide range of inputs that strtod() and absl::from_chars() exactly +// agree on the conversion amount. +// +// This test assumes the platform's strtod() uses perfect round_to_nearest +// rounding. +TEST(FromChars, TestVersusStrtod) { + for (int mantissa = 1000000; mantissa <= 9999999; mantissa += 501) { + for (int exponent = -300; exponent < 300; ++exponent) { + std::string candidate = absl::StrCat(mantissa, "e", exponent); + double strtod_value = strtod(candidate.c_str(), nullptr); + double absl_value = 0; + absl::from_chars(candidate.data(), candidate.data() + candidate.size(), + absl_value); + ASSERT_EQ(strtod_value, absl_value) << candidate; + } + } +} + +// Check for a wide range of inputs that strtof() and absl::from_chars() exactly +// agree on the conversion amount. +// +// This test assumes the platform's strtof() uses perfect round_to_nearest +// rounding. +TEST(FromChars, TestVersusStrtof) { + for (int mantissa = 1000000; mantissa <= 9999999; mantissa += 501) { + for (int exponent = -43; exponent < 32; ++exponent) { + std::string candidate = absl::StrCat(mantissa, "e", exponent); + float strtod_value = strtof(candidate.c_str(), nullptr); + float absl_value = 0; + absl::from_chars(candidate.data(), candidate.data() + candidate.size(), + absl_value); + ASSERT_EQ(strtod_value, absl_value) << candidate; + } + } +} + +// Tests if two floating point values have identical bit layouts. (EXPECT_EQ +// is not suitable for NaN testing, since NaNs are never equal.) +template +bool Identical(Float a, Float b) { + return 0 == memcmp(&a, &b, sizeof(Float)); +} + +// Check that NaNs are parsed correctly. The spec requires that +// std::from_chars on "NaN(123abc)" return the same value as std::nan("123abc"). +// How such an n-char-sequence affects the generated NaN is unspecified, so we +// just test for symmetry with std::nan and strtod here. +// +// (In Linux, this parses the value as a number and stuffs that number into the +// free bits of a quiet NaN.) +TEST(FromChars, NaNDoubles) { + for (std::string n_char_sequence : + {"", "1", "2", "3", "fff", "FFF", "200000", "400000", "4000000000000", + "8000000000000", "abc123", "legal_but_unexpected", + "99999999999999999999999", "_"}) { + std::string input = absl::StrCat("nan(", n_char_sequence, ")"); + SCOPED_TRACE(input); + double from_chars_double; + absl::from_chars(input.data(), input.data() + input.size(), + from_chars_double); + double std_nan_double = std::nan(n_char_sequence.c_str()); + EXPECT_TRUE(Identical(from_chars_double, std_nan_double)); + + // Also check that we match strtod()'s behavior. This test assumes that the + // platform has a compliant strtod(). +#if ABSL_STRTOD_HANDLES_NAN_CORRECTLY + double strtod_double = strtod(input.c_str(), nullptr); + EXPECT_TRUE(Identical(from_chars_double, strtod_double)); +#endif // ABSL_STRTOD_HANDLES_NAN_CORRECTLY + + // Check that we can parse a negative NaN + std::string negative_input = "-" + input; + double negative_from_chars_double; + absl::from_chars(negative_input.data(), + negative_input.data() + negative_input.size(), + negative_from_chars_double); + EXPECT_TRUE(std::signbit(negative_from_chars_double)); + EXPECT_FALSE(Identical(negative_from_chars_double, from_chars_double)); + from_chars_double = std::copysign(from_chars_double, -1.0); + EXPECT_TRUE(Identical(negative_from_chars_double, from_chars_double)); + } +} + +TEST(FromChars, NaNFloats) { + for (std::string n_char_sequence : + {"", "1", "2", "3", "fff", "FFF", "200000", "400000", "4000000000000", + "8000000000000", "abc123", "legal_but_unexpected", + "99999999999999999999999", "_"}) { + std::string input = absl::StrCat("nan(", n_char_sequence, ")"); + SCOPED_TRACE(input); + float from_chars_float; + absl::from_chars(input.data(), input.data() + input.size(), + from_chars_float); + float std_nan_float = std::nanf(n_char_sequence.c_str()); + EXPECT_TRUE(Identical(from_chars_float, std_nan_float)); + + // Also check that we match strtof()'s behavior. This test assumes that the + // platform has a compliant strtof(). +#if ABSL_STRTOD_HANDLES_NAN_CORRECTLY + float strtof_float = strtof(input.c_str(), nullptr); + EXPECT_TRUE(Identical(from_chars_float, strtof_float)); +#endif // ABSL_STRTOD_HANDLES_NAN_CORRECTLY + + // Check that we can parse a negative NaN + std::string negative_input = "-" + input; + float negative_from_chars_float; + absl::from_chars(negative_input.data(), + negative_input.data() + negative_input.size(), + negative_from_chars_float); + EXPECT_TRUE(std::signbit(negative_from_chars_float)); + EXPECT_FALSE(Identical(negative_from_chars_float, from_chars_float)); + from_chars_float = std::copysign(from_chars_float, -1.0); + EXPECT_TRUE(Identical(negative_from_chars_float, from_chars_float)); + } +} + +// Returns an integer larger than step. The values grow exponentially. +int NextStep(int step) { + return step + (step >> 2) + 1; +} + +// Test a conversion on a family of input strings, checking that the calculation +// is correct for in-bounds values, and that overflow and underflow are done +// correctly for out-of-bounds values. +// +// input_generator maps from an integer index to a std::string to test. +// expected_generator maps from an integer index to an expected Float value. +// from_chars conversion of input_generator(i) should result in +// expected_generator(i). +// +// lower_bound and upper_bound denote the smallest and largest values for which +// the conversion is expected to succeed. +template +void TestOverflowAndUnderflow( + const std::function& input_generator, + const std::function& expected_generator, int lower_bound, + int upper_bound) { + // test legal values near lower_bound + int index, step; + for (index = lower_bound, step = 1; index < upper_bound; + index += step, step = NextStep(step)) { + std::string input = input_generator(index); + SCOPED_TRACE(input); + Float expected = expected_generator(index); + Float actual; + auto result = + absl::from_chars(input.data(), input.data() + input.size(), actual); + EXPECT_EQ(result.ec, std::errc()); + EXPECT_EQ(expected, actual); + } + // test legal values near upper_bound + for (index = upper_bound, step = 1; index > lower_bound; + index -= step, step = NextStep(step)) { + std::string input = input_generator(index); + SCOPED_TRACE(input); + Float expected = expected_generator(index); + Float actual; + auto result = + absl::from_chars(input.data(), input.data() + input.size(), actual); + EXPECT_EQ(result.ec, std::errc()); + EXPECT_EQ(expected, actual); + } + // Test underflow values below lower_bound + for (index = lower_bound - 1, step = 1; index > -1000000; + index -= step, step = NextStep(step)) { + std::string input = input_generator(index); + SCOPED_TRACE(input); + Float actual; + auto result = + absl::from_chars(input.data(), input.data() + input.size(), actual); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_LT(actual, 1.0); // check for underflow + } + // Test overflow values above upper_bound + for (index = upper_bound + 1, step = 1; index < 1000000; + index += step, step = NextStep(step)) { + std::string input = input_generator(index); + SCOPED_TRACE(input); + Float actual; + auto result = + absl::from_chars(input.data(), input.data() + input.size(), actual); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); + EXPECT_GT(actual, 1.0); // check for overflow + } +} + +// Check that overflow and underflow are caught correctly for hex doubles. +// +// The largest representable double is 0x1.fffffffffffffp+1023, and the +// smallest representable subnormal is 0x0.0000000000001p-1022, which equals +// 0x1p-1074. Therefore 1023 and -1074 are the limits of acceptable exponents +// in this test. +TEST(FromChars, HexdecimalDoubleLimits) { + auto input_gen = [](int index) { return absl::StrCat("0x1.0p", index); }; + auto expected_gen = [](int index) { return std::ldexp(1.0, index); }; + TestOverflowAndUnderflow(input_gen, expected_gen, -1074, 1023); +} + +// Check that overflow and underflow are caught correctly for hex floats. +// +// The largest representable float is 0x1.fffffep+127, and the smallest +// representable subnormal is 0x0.000002p-126, which equals 0x1p-149. +// Therefore 127 and -149 are the limits of acceptable exponents in this test. +TEST(FromChars, HexdecimalFloatLimits) { + auto input_gen = [](int index) { return absl::StrCat("0x1.0p", index); }; + auto expected_gen = [](int index) { return std::ldexp(1.0f, index); }; + TestOverflowAndUnderflow(input_gen, expected_gen, -149, 127); +} + +// Check that overflow and underflow are caught correctly for decimal doubles. +// +// The largest representable double is about 1.8e308, and the smallest +// representable subnormal is about 5e-324. '1e-324' therefore rounds away from +// the smallest representable positive value. -323 and 308 are the limits of +// acceptable exponents in this test. +TEST(FromChars, DecimalDoubleLimits) { + auto input_gen = [](int index) { return absl::StrCat("1.0e", index); }; + auto expected_gen = [](int index) { return std::pow(10.0, index); }; + TestOverflowAndUnderflow(input_gen, expected_gen, -323, 308); +} + +// Check that overflow and underflow are caught correctly for decimal floats. +// +// The largest representable float is about 3.4e38, and the smallest +// representable subnormal is about 1.45e-45. '1e-45' therefore rounds towards +// the smallest representable positive value. -45 and 38 are the limits of +// acceptable exponents in this test. +TEST(FromChars, DecimalFloatLimits) { + auto input_gen = [](int index) { return absl::StrCat("1.0e", index); }; + auto expected_gen = [](int index) { return std::pow(10.0, index); }; + TestOverflowAndUnderflow(input_gen, expected_gen, -45, 38); +} + +} // namespace diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc index fbc9f756315..9874f4926d8 100644 --- a/absl/strings/escaping.cc +++ b/absl/strings/escaping.cc @@ -33,6 +33,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { // Digit conversion. @@ -1106,4 +1107,5 @@ std::string BytesToHexString(absl::string_view from) { return result; } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/escaping.h b/absl/strings/escaping.h index 1af0afa789c..16326a9ccf9 100644 --- a/absl/strings/escaping.h +++ b/absl/strings/escaping.h @@ -34,6 +34,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { // CUnescape() // @@ -156,6 +157,7 @@ std::string HexStringToBytes(absl::string_view from); // `2*from.size()`. std::string BytesToHexString(absl::string_view from); +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_ESCAPING_H_ diff --git a/absl/strings/internal/bits.h b/absl/strings/internal/bits.h index 901082ccddf..a7d0ee5b447 100644 --- a/absl/strings/internal/bits.h +++ b/absl/strings/internal/bits.h @@ -23,6 +23,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { // Returns the number of leading 0 bits in a 64-bit value. @@ -48,6 +49,7 @@ inline int CountLeadingZeros64(uint64_t n) { } } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_BITS_H_ diff --git a/absl/strings/internal/char_map.h b/absl/strings/internal/char_map.h index 8d92963a4de..e1280b031ee 100644 --- a/absl/strings/internal/char_map.h +++ b/absl/strings/internal/char_map.h @@ -28,6 +28,7 @@ #include "absl/base/port.h" namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { class Charmap { @@ -149,6 +150,7 @@ constexpr Charmap GraphCharmap() { return PrintCharmap() & ~SpaceCharmap(); } constexpr Charmap PunctCharmap() { return GraphCharmap() & ~AlnumCharmap(); } } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_CHAR_MAP_H_ diff --git a/absl/strings/internal/charconv_bigint.cc b/absl/strings/internal/charconv_bigint.cc new file mode 100644 index 00000000000..eb2e2976919 --- /dev/null +++ b/absl/strings/internal/charconv_bigint.cc @@ -0,0 +1,359 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/charconv_bigint.h" + +#include +#include +#include + +namespace absl { +inline namespace lts_2018_06_20 { +namespace strings_internal { + +namespace { + +// Table containing some large powers of 5, for fast computation. + +// Constant step size for entries in the kLargePowersOfFive table. Each entry +// is larger than the previous entry by a factor of 5**kLargePowerOfFiveStep +// (or 5**27). +// +// In other words, the Nth entry in the table is 5**(27*N). +// +// 5**27 is the largest power of 5 that fits in 64 bits. +constexpr int kLargePowerOfFiveStep = 27; + +// The largest legal index into the kLargePowersOfFive table. +// +// In other words, the largest precomputed power of 5 is 5**(27*20). +constexpr int kLargestPowerOfFiveIndex = 20; + +// Table of powers of (5**27), up to (5**27)**20 == 5**540. +// +// Used to generate large powers of 5 while limiting the number of repeated +// multiplications required. +// +// clang-format off +const uint32_t kLargePowersOfFive[] = { +// 5**27 (i=1), start=0, end=2 + 0xfa10079dU, 0x6765c793U, +// 5**54 (i=2), start=2, end=6 + 0x97d9f649U, 0x6664242dU, 0x29939b14U, 0x29c30f10U, +// 5**81 (i=3), start=6, end=12 + 0xc4f809c5U, 0x7bf3f22aU, 0x67bdae34U, 0xad340517U, 0x369d1b5fU, 0x10de1593U, +// 5**108 (i=4), start=12, end=20 + 0x92b260d1U, 0x9efff7c7U, 0x81de0ec6U, 0xaeba5d56U, 0x410664a4U, 0x4f40737aU, + 0x20d3846fU, 0x06d00f73U, +// 5**135 (i=5), start=20, end=30 + 0xff1b172dU, 0x13a1d71cU, 0xefa07617U, 0x7f682d3dU, 0xff8c90c0U, 0x3f0131e7U, + 0x3fdcb9feU, 0x917b0177U, 0x16c407a7U, 0x02c06b9dU, +// 5**162 (i=6), start=30, end=42 + 0x960f7199U, 0x056667ecU, 0xe07aefd8U, 0x80f2b9ccU, 0x8273f5e3U, 0xeb9a214aU, + 0x40b38005U, 0x0e477ad4U, 0x277d08e6U, 0xfa28b11eU, 0xd3f7d784U, 0x011c835bU, +// 5**189 (i=7), start=42, end=56 + 0xf723d9d5U, 0x3282d3f3U, 0xe00857d1U, 0x69659d25U, 0x2cf117cfU, 0x24da6d07U, + 0x954d1417U, 0x3e5d8cedU, 0x7a8bb766U, 0xfd785ae6U, 0x645436d2U, 0x40c78b34U, + 0x94151217U, 0x0072e9f7U, +// 5**216 (i=8), start=56, end=72 + 0x2b416aa1U, 0x7893c5a7U, 0xe37dc6d4U, 0x2bad2beaU, 0xf0fc846cU, 0x7575ae4bU, + 0x62587b14U, 0x83b67a34U, 0x02110cdbU, 0xf7992f55U, 0x00deb022U, 0xa4a23becU, + 0x8af5c5cdU, 0xb85b654fU, 0x818df38bU, 0x002e69d2U, +// 5**243 (i=9), start=72, end=90 + 0x3518cbbdU, 0x20b0c15fU, 0x38756c2fU, 0xfb5dc3ddU, 0x22ad2d94U, 0xbf35a952U, + 0xa699192aU, 0x9a613326U, 0xad2a9cedU, 0xd7f48968U, 0xe87dfb54U, 0xc8f05db6U, + 0x5ef67531U, 0x31c1ab49U, 0xe202ac9fU, 0x9b2957b5U, 0xa143f6d3U, 0x0012bf07U, +// 5**270 (i=10), start=90, end=110 + 0x8b971de9U, 0x21aba2e1U, 0x63944362U, 0x57172336U, 0xd9544225U, 0xfb534166U, + 0x08c563eeU, 0x14640ee2U, 0x24e40d31U, 0x02b06537U, 0x03887f14U, 0x0285e533U, + 0xb744ef26U, 0x8be3a6c4U, 0x266979b4U, 0x6761ece2U, 0xd9cb39e4U, 0xe67de319U, + 0x0d39e796U, 0x00079250U, +// 5**297 (i=11), start=110, end=132 + 0x260eb6e5U, 0xf414a796U, 0xee1a7491U, 0xdb9368ebU, 0xf50c105bU, 0x59157750U, + 0x9ed2fb5cU, 0xf6e56d8bU, 0xeaee8d23U, 0x0f319f75U, 0x2aa134d6U, 0xac2908e9U, + 0xd4413298U, 0x02f02a55U, 0x989d5a7aU, 0x70dde184U, 0xba8040a7U, 0x03200981U, + 0xbe03b11cU, 0x3c1c2a18U, 0xd60427a1U, 0x00030ee0U, +// 5**324 (i=12), start=132, end=156 + 0xce566d71U, 0xf1c4aa25U, 0x4e93ca53U, 0xa72283d0U, 0x551a73eaU, 0x3d0538e2U, + 0x8da4303fU, 0x6a58de60U, 0x0e660221U, 0x49cf61a6U, 0x8d058fc1U, 0xb9d1a14cU, + 0x4bab157dU, 0xc85c6932U, 0x518c8b9eU, 0x9b92b8d0U, 0x0d8a0e21U, 0xbd855df9U, + 0xb3ea59a1U, 0x8da29289U, 0x4584d506U, 0x3752d80fU, 0xb72569c6U, 0x00013c33U, +// 5**351 (i=13), start=156, end=182 + 0x190f354dU, 0x83695cfeU, 0xe5a4d0c7U, 0xb60fb7e8U, 0xee5bbcc4U, 0xb922054cU, + 0xbb4f0d85U, 0x48394028U, 0x1d8957dbU, 0x0d7edb14U, 0x4ecc7587U, 0x505e9e02U, + 0x4c87f36bU, 0x99e66bd6U, 0x44b9ed35U, 0x753037d4U, 0xe5fe5f27U, 0x2742c203U, + 0x13b2ed2bU, 0xdc525d2cU, 0xe6fde59aU, 0x77ffb18fU, 0x13c5752cU, 0x08a84bccU, + 0x859a4940U, 0x00007fb6U, +// 5**378 (i=14), start=182, end=210 + 0x4f98cb39U, 0xa60edbbcU, 0x83b5872eU, 0xa501acffU, 0x9cc76f78U, 0xbadd4c73U, + 0x43e989faU, 0xca7acf80U, 0x2e0c824fU, 0xb19f4ffcU, 0x092fd81cU, 0xe4eb645bU, + 0xa1ff84c2U, 0x8a5a83baU, 0xa8a1fae9U, 0x1db43609U, 0xb0fed50bU, 0x0dd7d2bdU, + 0x7d7accd8U, 0x91fa640fU, 0x37dcc6c5U, 0x1c417fd5U, 0xe4d462adU, 0xe8a43399U, + 0x131bf9a5U, 0x8df54d29U, 0x36547dc1U, 0x00003395U, +// 5**405 (i=15), start=210, end=240 + 0x5bd330f5U, 0x77d21967U, 0x1ac481b7U, 0x6be2f7ceU, 0x7f4792a9U, 0xe84c2c52U, + 0x84592228U, 0x9dcaf829U, 0xdab44ce1U, 0x3d0c311bU, 0x532e297dU, 0x4704e8b4U, + 0x9cdc32beU, 0x41e64d9dU, 0x7717bea1U, 0xa824c00dU, 0x08f50b27U, 0x0f198d77U, + 0x49bbfdf0U, 0x025c6c69U, 0xd4e55cd3U, 0xf083602bU, 0xb9f0fecdU, 0xc0864aeaU, + 0x9cb98681U, 0xaaf620e9U, 0xacb6df30U, 0x4faafe66U, 0x8af13c3bU, 0x000014d5U, +// 5**432 (i=16), start=240, end=272 + 0x682bb941U, 0x89a9f297U, 0xcba75d7bU, 0x404217b1U, 0xb4e519e9U, 0xa1bc162bU, + 0xf7f5910aU, 0x98715af5U, 0x2ff53e57U, 0xe3ef118cU, 0x490c4543U, 0xbc9b1734U, + 0x2affbe4dU, 0x4cedcb4cU, 0xfb14e99eU, 0x35e34212U, 0xece39c24U, 0x07673ab3U, + 0xe73115ddU, 0xd15d38e7U, 0x093eed3bU, 0xf8e7eac5U, 0x78a8cc80U, 0x25227aacU, + 0x3f590551U, 0x413da1cbU, 0xdf643a55U, 0xab65ad44U, 0xd70b23d7U, 0xc672cd76U, + 0x3364ea62U, 0x0000086aU, +// 5**459 (i=17), start=272, end=306 + 0x22f163ddU, 0x23cf07acU, 0xbe2af6c2U, 0xf412f6f6U, 0xc3ff541eU, 0x6eeaf7deU, + 0xa47047e0U, 0x408cda92U, 0x0f0eeb08U, 0x56deba9dU, 0xcfc6b090U, 0x8bbbdf04U, + 0x3933cdb3U, 0x9e7bb67dU, 0x9f297035U, 0x38946244U, 0xee1d37bbU, 0xde898174U, + 0x63f3559dU, 0x705b72fbU, 0x138d27d9U, 0xf8603a78U, 0x735eec44U, 0xe30987d5U, + 0xc6d38070U, 0x9cfe548eU, 0x9ff01422U, 0x7c564aa8U, 0x91cc60baU, 0xcbc3565dU, + 0x7550a50bU, 0x6909aeadU, 0x13234c45U, 0x00000366U, +// 5**486 (i=18), start=306, end=342 + 0x17954989U, 0x3a7d7709U, 0x98042de5U, 0xa9011443U, 0x45e723c2U, 0x269ffd6fU, + 0x58852a46U, 0xaaa1042aU, 0x2eee8153U, 0xb2b6c39eU, 0xaf845b65U, 0xf6c365d7U, + 0xe4cffb2bU, 0xc840e90cU, 0xabea8abbU, 0x5c58f8d2U, 0x5c19fa3aU, 0x4670910aU, + 0x4449f21cU, 0xefa645b3U, 0xcc427decU, 0x083c3d73U, 0x467cb413U, 0x6fe10ae4U, + 0x3caffc72U, 0x9f8da55eU, 0x5e5c8ea7U, 0x490594bbU, 0xf0871b0bU, 0xdd89816cU, + 0x8e931df8U, 0xe85ce1c9U, 0xcca090a5U, 0x575fa16bU, 0x6b9f106cU, 0x0000015fU, +// 5**513 (i=19), start=342, end=380 + 0xee20d805U, 0x57bc3c07U, 0xcdea624eU, 0xd3f0f52dU, 0x9924b4f4U, 0xcf968640U, + 0x61d41962U, 0xe87fb464U, 0xeaaf51c7U, 0x564c8b60U, 0xccda4028U, 0x529428bbU, + 0x313a1fa8U, 0x96bd0f94U, 0x7a82ebaaU, 0xad99e7e9U, 0xf2668cd4U, 0xbe33a45eU, + 0xfd0db669U, 0x87ee369fU, 0xd3ec20edU, 0x9c4d7db7U, 0xdedcf0d8U, 0x7cd2ca64U, + 0xe25a6577U, 0x61003fd4U, 0xe56f54ccU, 0x10b7c748U, 0x40526e5eU, 0x7300ae87U, + 0x5c439261U, 0x2c0ff469U, 0xbf723f12U, 0xb2379b61U, 0xbf59b4f5U, 0xc91b1c3fU, + 0xf0046d27U, 0x0000008dU, +// 5**540 (i=20), start=380, end=420 + 0x525c9e11U, 0xf4e0eb41U, 0xebb2895dU, 0x5da512f9U, 0x7d9b29d4U, 0x452f4edcU, + 0x0b90bc37U, 0x341777cbU, 0x63d269afU, 0x1da77929U, 0x0a5c1826U, 0x77991898U, + 0x5aeddf86U, 0xf853a877U, 0x538c31ccU, 0xe84896daU, 0xb7a0010bU, 0x17ef4de5U, + 0xa52a2adeU, 0x029fd81cU, 0x987ce701U, 0x27fefd77U, 0xdb46c66fU, 0x5d301900U, + 0x496998c0U, 0xbb6598b9U, 0x5eebb607U, 0xe547354aU, 0xdf4a2f7eU, 0xf06c4955U, + 0x96242ffaU, 0x1775fb27U, 0xbecc58ceU, 0xebf2a53bU, 0x3eaad82aU, 0xf41137baU, + 0x573e6fbaU, 0xfb4866b8U, 0x54002148U, 0x00000039U, +}; +// clang-format on + +// Returns a pointer to the big integer data for (5**27)**i. i must be +// between 1 and 20, inclusive. +const uint32_t* LargePowerOfFiveData(int i) { + return kLargePowersOfFive + i * (i - 1); +} + +// Returns the size of the big integer data for (5**27)**i, in words. i must be +// between 1 and 20, inclusive. +int LargePowerOfFiveSize(int i) { return 2 * i; } +} // namespace + +const uint32_t kFiveToNth[14] = { + 1, 5, 25, 125, 625, 3125, 15625, + 78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125, +}; + +const uint32_t kTenToNth[10] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, +}; + +template +int BigUnsigned::ReadFloatMantissa(const ParsedFloat& fp, + int significant_digits) { + SetToZero(); + assert(fp.type == FloatType::kNumber); + + if (fp.subrange_begin == nullptr) { + // We already exactly parsed the mantissa, so no more work is necessary. + words_[0] = fp.mantissa & 0xffffffffu; + words_[1] = fp.mantissa >> 32; + if (words_[1]) { + size_ = 2; + } else if (words_[0]) { + size_ = 1; + } + return fp.exponent; + } + int exponent_adjust = + ReadDigits(fp.subrange_begin, fp.subrange_end, significant_digits); + return fp.literal_exponent + exponent_adjust; +} + +template +int BigUnsigned::ReadDigits(const char* begin, const char* end, + int significant_digits) { + assert(significant_digits <= Digits10() + 1); + SetToZero(); + + bool after_decimal_point = false; + // Discard any leading zeroes before the decimal point + while (begin < end && *begin == '0') { + ++begin; + } + int dropped_digits = 0; + // Discard any trailing zeroes. These may or may not be after the decimal + // point. + while (begin < end && *std::prev(end) == '0') { + --end; + ++dropped_digits; + } + if (begin < end && *std::prev(end) == '.') { + // If the std::string ends in '.', either before or after dropping zeroes, then + // drop the decimal point and look for more digits to drop. + dropped_digits = 0; + --end; + while (begin < end && *std::prev(end) == '0') { + --end; + ++dropped_digits; + } + } else if (dropped_digits) { + // We dropped digits, and aren't sure if they're before or after the decimal + // point. Figure that out now. + const char* dp = std::find(begin, end, '.'); + if (dp != end) { + // The dropped trailing digits were after the decimal point, so don't + // count them. + dropped_digits = 0; + } + } + // Any non-fraction digits we dropped need to be accounted for in our exponent + // adjustment. + int exponent_adjust = dropped_digits; + + uint32_t queued = 0; + int digits_queued = 0; + for (; begin != end && significant_digits > 0; ++begin) { + if (*begin == '.') { + after_decimal_point = true; + continue; + } + if (after_decimal_point) { + // For each fractional digit we emit in our parsed integer, adjust our + // decimal exponent to compensate. + --exponent_adjust; + } + int digit = (*begin - '0'); + --significant_digits; + if (significant_digits == 0 && std::next(begin) != end && + (digit == 0 || digit == 5)) { + // If this is the very last significant digit, but insignificant digits + // remain, we know that the last of those remaining significant digits is + // nonzero. (If it wasn't, we would have stripped it before we got here.) + // So if this final digit is a 0 or 5, adjust it upward by 1. + // + // This adjustment is what allows incredibly large mantissas ending in + // 500000...000000000001 to correctly round up, rather than to nearest. + ++digit; + } + queued = 10 * queued + digit; + ++digits_queued; + if (digits_queued == kMaxSmallPowerOfTen) { + MultiplyBy(kTenToNth[kMaxSmallPowerOfTen]); + AddWithCarry(0, queued); + queued = digits_queued = 0; + } + } + // Encode any remaining digits. + if (digits_queued) { + MultiplyBy(kTenToNth[digits_queued]); + AddWithCarry(0, queued); + } + + // If any insignificant digits remain, we will drop them. But if we have not + // yet read the decimal point, then we have to adjust the exponent to account + // for the dropped digits. + if (begin < end && !after_decimal_point) { + // This call to std::find will result in a pointer either to the decimal + // point, or to the end of our buffer if there was none. + // + // Either way, [begin, decimal_point) will contain the set of dropped digits + // that require an exponent adjustment. + const char* decimal_point = std::find(begin, end, '.'); + exponent_adjust += (decimal_point - begin); + } + return exponent_adjust; +} + +template +/* static */ BigUnsigned BigUnsigned::FiveToTheNth( + int n) { + BigUnsigned answer(1u); + + // Seed from the table of large powers, if possible. + bool first_pass = true; + while (n >= kLargePowerOfFiveStep) { + int big_power = + std::min(n / kLargePowerOfFiveStep, kLargestPowerOfFiveIndex); + if (first_pass) { + // just copy, rather than multiplying by 1 + std::copy( + LargePowerOfFiveData(big_power), + LargePowerOfFiveData(big_power) + LargePowerOfFiveSize(big_power), + answer.words_); + answer.size_ = LargePowerOfFiveSize(big_power); + first_pass = false; + } else { + answer.MultiplyBy(LargePowerOfFiveSize(big_power), + LargePowerOfFiveData(big_power)); + } + n -= kLargePowerOfFiveStep * big_power; + } + answer.MultiplyByFiveToTheNth(n); + return answer; +} + +template +void BigUnsigned::MultiplyStep(int original_size, + const uint32_t* other_words, + int other_size, int step) { + int this_i = std::min(original_size - 1, step); + int other_i = step - this_i; + + uint64_t this_word = 0; + uint64_t carry = 0; + for (; this_i >= 0 && other_i < other_size; --this_i, ++other_i) { + uint64_t product = words_[this_i]; + product *= other_words[other_i]; + this_word += product; + carry += (this_word >> 32); + this_word &= 0xffffffff; + } + AddWithCarry(step + 1, carry); + words_[step] = this_word & 0xffffffff; + if (this_word > 0 && size_ <= step) { + size_ = step + 1; + } +} + +template +std::string BigUnsigned::ToString() const { + BigUnsigned copy = *this; + std::string result; + // Build result in reverse order + while (copy.size() > 0) { + int next_digit = copy.DivMod<10>(); + result.push_back('0' + next_digit); + } + if (result.empty()) { + result.push_back('0'); + } + std::reverse(result.begin(), result.end()); + return result; +} + +template class BigUnsigned<4>; +template class BigUnsigned<84>; + +} // namespace strings_internal +} // inline namespace lts_2018_06_20 +} // namespace absl diff --git a/absl/strings/internal/charconv_bigint.h b/absl/strings/internal/charconv_bigint.h new file mode 100644 index 00000000000..9827b56e971 --- /dev/null +++ b/absl/strings/internal/charconv_bigint.h @@ -0,0 +1,428 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_ +#define ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_ + +#include +#include +#include +#include + +#include "absl/strings/ascii.h" +#include "absl/strings/internal/charconv_parse.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2018_06_20 { +namespace strings_internal { + +// The largest power that 5 that can be raised to, and still fit in a uint32_t. +constexpr int kMaxSmallPowerOfFive = 13; +// The largest power that 10 that can be raised to, and still fit in a uint32_t. +constexpr int kMaxSmallPowerOfTen = 9; + +extern const uint32_t kFiveToNth[kMaxSmallPowerOfFive + 1]; +extern const uint32_t kTenToNth[kMaxSmallPowerOfTen + 1]; + +// Large, fixed-width unsigned integer. +// +// Exact rounding for decimal-to-binary floating point conversion requires very +// large integer math, but a design goal of absl::from_chars is to avoid +// allocating memory. The integer precision needed for decimal-to-binary +// conversions is large but bounded, so a huge fixed-width integer class +// suffices. +// +// This is an intentionally limited big integer class. Only needed operations +// are implemented. All storage lives in an array data member, and all +// arithmetic is done in-place, to avoid requiring separate storage for operand +// and result. +// +// This is an internal class. Some methods live in the .cc file, and are +// instantiated only for the values of max_words we need. +template +class BigUnsigned { + public: + static_assert(max_words == 4 || max_words == 84, + "unsupported max_words value"); + + BigUnsigned() : size_(0), words_{} {} + explicit BigUnsigned(uint32_t v) : size_(v > 0 ? 1 : 0), words_{v} {} + explicit BigUnsigned(uint64_t v) + : size_(0), + words_{static_cast(v & 0xffffffff), + static_cast(v >> 32)} { + if (words_[1]) { + size_ = 2; + } else if (words_[0]) { + size_ = 1; + } + } + + // Constructs a BigUnsigned from the given string_view containing a decimal + // value. If the input std::string is not a decimal integer, constructs a 0 + // instead. + explicit BigUnsigned(absl::string_view sv) : size_(0), words_{} { + // Check for valid input, returning a 0 otherwise. This is reasonable + // behavior only because this constructor is for unit tests. + if (std::find_if_not(sv.begin(), sv.end(), ascii_isdigit) != sv.end() || + sv.empty()) { + return; + } + int exponent_adjust = + ReadDigits(sv.data(), sv.data() + sv.size(), Digits10() + 1); + if (exponent_adjust > 0) { + MultiplyByTenToTheNth(exponent_adjust); + } + } + + // Loads the mantissa value of a previously-parsed float. + // + // Returns the associated decimal exponent. The value of the parsed float is + // exactly *this * 10**exponent. + int ReadFloatMantissa(const ParsedFloat& fp, int significant_digits); + + // Returns the number of decimal digits of precision this type provides. All + // numbers with this many decimal digits or fewer are representable by this + // type. + // + // Analagous to std::numeric_limits::digits10. + static constexpr int Digits10() { + // 9975007/1035508 is very slightly less than log10(2**32). + return static_cast(max_words) * 9975007 / 1035508; + } + + // Shifts left by the given number of bits. + void ShiftLeft(int count) { + if (count > 0) { + const int word_shift = count / 32; + if (word_shift >= max_words) { + SetToZero(); + return; + } + size_ = std::min(size_ + word_shift, max_words); + count %= 32; + if (count == 0) { + std::copy_backward(words_, words_ + size_ - word_shift, words_ + size_); + } else { + for (int i = std::min(size_, max_words - 1); i > word_shift; --i) { + words_[i] = (words_[i - word_shift] << count) | + (words_[i - word_shift - 1] >> (32 - count)); + } + words_[word_shift] = words_[0] << count; + // Grow size_ if necessary. + if (size_ < max_words && words_[size_]) { + ++size_; + } + } + std::fill(words_, words_ + word_shift, 0u); + } + } + + + // Multiplies by v in-place. + void MultiplyBy(uint32_t v) { + if (size_ == 0 || v == 1) { + return; + } + if (v == 0) { + SetToZero(); + return; + } + const uint64_t factor = v; + uint64_t window = 0; + for (int i = 0; i < size_; ++i) { + window += factor * words_[i]; + words_[i] = window & 0xffffffff; + window >>= 32; + } + // If carry bits remain and there's space for them, grow size_. + if (window && size_ < max_words) { + words_[size_] = window & 0xffffffff; + ++size_; + } + } + + void MultiplyBy(uint64_t v) { + uint32_t words[2]; + words[0] = static_cast(v); + words[1] = static_cast(v >> 32); + if (words[1] == 0) { + MultiplyBy(words[0]); + } else { + MultiplyBy(2, words); + } + } + + // Multiplies in place by 5 to the power of n. n must be non-negative. + void MultiplyByFiveToTheNth(int n) { + while (n >= kMaxSmallPowerOfFive) { + MultiplyBy(kFiveToNth[kMaxSmallPowerOfFive]); + n -= kMaxSmallPowerOfFive; + } + if (n > 0) { + MultiplyBy(kFiveToNth[n]); + } + } + + // Multiplies in place by 10 to the power of n. n must be non-negative. + void MultiplyByTenToTheNth(int n) { + if (n > kMaxSmallPowerOfTen) { + // For large n, raise to a power of 5, then shift left by the same amount. + // (10**n == 5**n * 2**n.) This requires fewer multiplications overall. + MultiplyByFiveToTheNth(n); + ShiftLeft(n); + } else if (n > 0) { + // We can do this more quickly for very small N by using a single + // multiplication. + MultiplyBy(kTenToNth[n]); + } + } + + // Returns the value of 5**n, for non-negative n. This implementation uses + // a lookup table, and is faster then seeding a BigUnsigned with 1 and calling + // MultiplyByFiveToTheNth(). + static BigUnsigned FiveToTheNth(int n); + + // Multiplies by another BigUnsigned, in-place. + template + void MultiplyBy(const BigUnsigned& other) { + MultiplyBy(other.size(), other.words()); + } + + void SetToZero() { + std::fill(words_, words_ + size_, 0u); + size_ = 0; + } + + // Returns the value of the nth word of this BigUnsigned. This is + // range-checked, and returns 0 on out-of-bounds accesses. + uint32_t GetWord(int index) const { + if (index < 0 || index >= size_) { + return 0; + } + return words_[index]; + } + + // Returns this integer as a decimal std::string. This is not used in the decimal- + // to-binary conversion; it is intended to aid in testing. + std::string ToString() const; + + int size() const { return size_; } + const uint32_t* words() const { return words_; } + + private: + // Reads the number between [begin, end), possibly containing a decimal point, + // into this BigUnsigned. + // + // Callers are required to ensure [begin, end) contains a valid number, with + // one or more decimal digits and at most one decimal point. This routine + // will behave unpredictably if these preconditions are not met. + // + // Only the first `significant_digits` digits are read. Digits beyond this + // limit are "sticky": If the final significant digit is 0 or 5, and if any + // dropped digit is nonzero, then that final significant digit is adjusted up + // to 1 or 6. This adjustment allows for precise rounding. + // + // Returns `exponent_adjustment`, a power-of-ten exponent adjustment to + // account for the decimal point and for dropped significant digits. After + // this function returns, + // actual_value_of_parsed_string ~= *this * 10**exponent_adjustment. + int ReadDigits(const char* begin, const char* end, int significant_digits); + + // Performs a step of big integer multiplication. This computes the full + // (64-bit-wide) values that should be added at the given index (step), and + // adds to that location in-place. + // + // Because our math all occurs in place, we must multiply starting from the + // highest word working downward. (This is a bit more expensive due to the + // extra carries involved.) + // + // This must be called in steps, for each word to be calculated, starting from + // the high end and working down to 0. The first value of `step` should be + // `std::min(original_size + other.size_ - 2, max_words - 1)`. + // The reason for this expression is that multiplying the i'th word from one + // multiplicand and the j'th word of another multiplicand creates a + // two-word-wide value to be stored at the (i+j)'th element. The highest + // word indices we will access are `original_size - 1` from this object, and + // `other.size_ - 1` from our operand. Therefore, + // `original_size + other.size_ - 2` is the first step we should calculate, + // but limited on an upper bound by max_words. + + // Working from high-to-low ensures that we do not overwrite the portions of + // the initial value of *this which are still needed for later steps. + // + // Once called with step == 0, *this contains the result of the + // multiplication. + // + // `original_size` is the size_ of *this before the first call to + // MultiplyStep(). `other_words` and `other_size` are the contents of our + // operand. `step` is the step to perform, as described above. + void MultiplyStep(int original_size, const uint32_t* other_words, + int other_size, int step); + + void MultiplyBy(int other_size, const uint32_t* other_words) { + const int original_size = size_; + const int first_step = + std::min(original_size + other_size - 2, max_words - 1); + for (int step = first_step; step >= 0; --step) { + MultiplyStep(original_size, other_words, other_size, step); + } + } + + // Adds a 32-bit value to the index'th word, with carry. + void AddWithCarry(int index, uint32_t value) { + if (value) { + while (index < max_words && value > 0) { + words_[index] += value; + // carry if we overflowed in this word: + if (value > words_[index]) { + value = 1; + ++index; + } else { + value = 0; + } + } + size_ = std::min(max_words, std::max(index + 1, size_)); + } + } + + void AddWithCarry(int index, uint64_t value) { + if (value && index < max_words) { + uint32_t high = value >> 32; + uint32_t low = value & 0xffffffff; + words_[index] += low; + if (words_[index] < low) { + ++high; + if (high == 0) { + // Carry from the low word caused our high word to overflow. + // Short circuit here to do the right thing. + AddWithCarry(index + 2, static_cast(1)); + return; + } + } + if (high > 0) { + AddWithCarry(index + 1, high); + } else { + // Normally 32-bit AddWithCarry() sets size_, but since we don't call + // it when `high` is 0, do it ourselves here. + size_ = std::min(max_words, std::max(index + 1, size_)); + } + } + } + + // Divide this in place by a constant divisor. Returns the remainder of the + // division. + template + uint32_t DivMod() { + uint64_t accumulator = 0; + for (int i = size_ - 1; i >= 0; --i) { + accumulator <<= 32; + accumulator += words_[i]; + // accumulator / divisor will never overflow an int32_t in this loop + words_[i] = static_cast(accumulator / divisor); + accumulator = accumulator % divisor; + } + while (size_ > 0 && words_[size_ - 1] == 0) { + --size_; + } + return static_cast(accumulator); + } + + // The number of elements in words_ that may carry significant values. + // All elements beyond this point are 0. + // + // When size_ is 0, this BigUnsigned stores the value 0. + // When size_ is nonzero, is *not* guaranteed that words_[size_ - 1] is + // nonzero. This can occur due to overflow truncation. + // In particular, x.size_ != y.size_ does *not* imply x != y. + int size_; + uint32_t words_[max_words]; +}; + +// Compares two big integer instances. +// +// Returns -1 if lhs < rhs, 0 if lhs == rhs, and 1 if lhs > rhs. +template +int Compare(const BigUnsigned& lhs, const BigUnsigned& rhs) { + int limit = std::max(lhs.size(), rhs.size()); + for (int i = limit - 1; i >= 0; --i) { + const uint32_t lhs_word = lhs.GetWord(i); + const uint32_t rhs_word = rhs.GetWord(i); + if (lhs_word < rhs_word) { + return -1; + } else if (lhs_word > rhs_word) { + return 1; + } + } + return 0; +} + +template +bool operator==(const BigUnsigned& lhs, const BigUnsigned& rhs) { + int limit = std::max(lhs.size(), rhs.size()); + for (int i = 0; i < limit; ++i) { + if (lhs.GetWord(i) != rhs.GetWord(i)) { + return false; + } + } + return true; +} + +template +bool operator!=(const BigUnsigned& lhs, const BigUnsigned& rhs) { + return !(lhs == rhs); +} + +template +bool operator<(const BigUnsigned& lhs, const BigUnsigned& rhs) { + return Compare(lhs, rhs) == -1; +} + +template +bool operator>(const BigUnsigned& lhs, const BigUnsigned& rhs) { + return rhs < lhs; +} +template +bool operator<=(const BigUnsigned& lhs, const BigUnsigned& rhs) { + return !(rhs < lhs); +} +template +bool operator>=(const BigUnsigned& lhs, const BigUnsigned& rhs) { + return !(lhs < rhs); +} + +// Output operator for BigUnsigned, for testing purposes only. +template +std::ostream& operator<<(std::ostream& os, const BigUnsigned& num) { + return os << num.ToString(); +} + +// Explicit instantiation declarations for the sizes of BigUnsigned that we +// are using. +// +// For now, the choices of 4 and 84 are arbitrary; 4 is a small value that is +// still bigger than an int128, and 84 is a large value we will want to use +// in the from_chars implementation. +// +// Comments justifying the use of 84 belong in the from_chars implementation, +// and will be added in a follow-up CL. +extern template class BigUnsigned<4>; +extern template class BigUnsigned<84>; + +} // namespace strings_internal +} // inline namespace lts_2018_06_20 +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_ diff --git a/absl/strings/internal/charconv_bigint_test.cc b/absl/strings/internal/charconv_bigint_test.cc new file mode 100644 index 00000000000..118b0dcbe12 --- /dev/null +++ b/absl/strings/internal/charconv_bigint_test.cc @@ -0,0 +1,205 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/charconv_bigint.h" + +#include + +#include "gtest/gtest.h" + +namespace absl { +inline namespace lts_2018_06_20 { +namespace strings_internal { + +TEST(BigUnsigned, ShiftLeft) { + { + // Check that 3 * 2**100 is calculated correctly + BigUnsigned<4> num(3u); + num.ShiftLeft(100); + EXPECT_EQ(num, BigUnsigned<4>("3802951800684688204490109616128")); + } + { + // Test that overflow is truncated properly. + // 15 is 4 bits long, and BigUnsigned<4> is a 128-bit bigint. + // Shifting left by 125 bits should truncate off the high bit, so that + // 15 << 125 == 7 << 125 + // after truncation. + BigUnsigned<4> a(15u); + BigUnsigned<4> b(7u); + BigUnsigned<4> c(3u); + a.ShiftLeft(125); + b.ShiftLeft(125); + c.ShiftLeft(125); + EXPECT_EQ(a, b); + EXPECT_NE(a, c); + } + { + // Same test, larger bigint: + BigUnsigned<84> a(15u); + BigUnsigned<84> b(7u); + BigUnsigned<84> c(3u); + a.ShiftLeft(84 * 32 - 3); + b.ShiftLeft(84 * 32 - 3); + c.ShiftLeft(84 * 32 - 3); + EXPECT_EQ(a, b); + EXPECT_NE(a, c); + } + { + // Check that incrementally shifting has the same result as doing it all at + // once (attempting to capture corner cases.) + const std::string seed = "1234567890123456789012345678901234567890"; + BigUnsigned<84> a(seed); + for (int i = 1; i <= 84 * 32; ++i) { + a.ShiftLeft(1); + BigUnsigned<84> b(seed); + b.ShiftLeft(i); + EXPECT_EQ(a, b); + } + // And we should have fully rotated all bits off by now: + EXPECT_EQ(a, BigUnsigned<84>(0u)); + } +} + +TEST(BigUnsigned, MultiplyByUint32) { + const BigUnsigned<84> factorial_100( + "933262154439441526816992388562667004907159682643816214685929638952175999" + "932299156089414639761565182862536979208272237582511852109168640000000000" + "00000000000000"); + BigUnsigned<84> a(1u); + for (uint32_t i = 1; i <= 100; ++i) { + a.MultiplyBy(i); + } + EXPECT_EQ(a, BigUnsigned<84>(factorial_100)); +} + +TEST(BigUnsigned, MultiplyByBigUnsigned) { + { + // Put the terms of factorial_200 into two bigints, and multiply them + // together. + const BigUnsigned<84> factorial_200( + "7886578673647905035523632139321850622951359776871732632947425332443594" + "4996340334292030428401198462390417721213891963883025764279024263710506" + "1926624952829931113462857270763317237396988943922445621451664240254033" + "2918641312274282948532775242424075739032403212574055795686602260319041" + "7032406235170085879617892222278962370389737472000000000000000000000000" + "0000000000000000000000000"); + BigUnsigned<84> evens(1u); + BigUnsigned<84> odds(1u); + for (uint32_t i = 1; i < 200; i += 2) { + odds.MultiplyBy(i); + evens.MultiplyBy(i + 1); + } + evens.MultiplyBy(odds); + EXPECT_EQ(evens, factorial_200); + } + { + // Multiply various powers of 10 together. + for (int a = 0 ; a < 700; a += 25) { + SCOPED_TRACE(a); + BigUnsigned<84> a_value("3" + std::string(a, '0')); + for (int b = 0; b < (700 - a); b += 25) { + SCOPED_TRACE(b); + BigUnsigned<84> b_value("2" + std::string(b, '0')); + BigUnsigned<84> expected_product("6" + std::string(a + b, '0')); + b_value.MultiplyBy(a_value); + EXPECT_EQ(b_value, expected_product); + } + } + } +} + +TEST(BigUnsigned, MultiplyByOverflow) { + { + // Check that multiplcation overflow predictably truncates. + + // A big int with all bits on. + BigUnsigned<4> all_bits_on("340282366920938463463374607431768211455"); + // Modulo 2**128, this is equal to -1. Therefore the square of this, + // modulo 2**128, should be 1. + all_bits_on.MultiplyBy(all_bits_on); + EXPECT_EQ(all_bits_on, BigUnsigned<4>(1u)); + } + { + // Try multiplying a large bigint by 2**50, and compare the result to + // shifting. + BigUnsigned<4> value_1("12345678901234567890123456789012345678"); + BigUnsigned<4> value_2("12345678901234567890123456789012345678"); + BigUnsigned<4> two_to_fiftieth(1u); + two_to_fiftieth.ShiftLeft(50); + + value_1.ShiftLeft(50); + value_2.MultiplyBy(two_to_fiftieth); + EXPECT_EQ(value_1, value_2); + } +} + +TEST(BigUnsigned, FiveToTheNth) { + { + // Sanity check that MultiplyByFiveToTheNth gives consistent answers, up to + // and including overflow. + for (int i = 0; i < 1160; ++i) { + SCOPED_TRACE(i); + BigUnsigned<84> value_1(123u); + BigUnsigned<84> value_2(123u); + value_1.MultiplyByFiveToTheNth(i); + for (int j = 0; j < i; j++) { + value_2.MultiplyBy(5u); + } + EXPECT_EQ(value_1, value_2); + } + } + { + // Check that the faster, table-lookup-based static method returns the same + // result that multiplying in-place would return, up to and including + // overflow. + for (int i = 0; i < 1160; ++i) { + SCOPED_TRACE(i); + BigUnsigned<84> value_1(1u); + value_1.MultiplyByFiveToTheNth(i); + BigUnsigned<84> value_2 = BigUnsigned<84>::FiveToTheNth(i); + EXPECT_EQ(value_1, value_2); + } + } +} + +TEST(BigUnsigned, TenToTheNth) { + { + // Sanity check MultiplyByTenToTheNth. + for (int i = 0; i < 800; ++i) { + SCOPED_TRACE(i); + BigUnsigned<84> value_1(123u); + BigUnsigned<84> value_2(123u); + value_1.MultiplyByTenToTheNth(i); + for (int j = 0; j < i; j++) { + value_2.MultiplyBy(10u); + } + EXPECT_EQ(value_1, value_2); + } + } + { + // Alternate testing approach, taking advantage of the decimal parser. + for (int i = 0; i < 200; ++i) { + SCOPED_TRACE(i); + BigUnsigned<84> value_1(135u); + value_1.MultiplyByTenToTheNth(i); + BigUnsigned<84> value_2("135" + std::string(i, '0')); + EXPECT_EQ(value_1, value_2); + } + } +} + + +} // namespace strings_internal +} // inline namespace lts_2018_06_20 +} // namespace absl diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc new file mode 100644 index 00000000000..37d75635dea --- /dev/null +++ b/absl/strings/internal/charconv_parse.cc @@ -0,0 +1,498 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/charconv_parse.h" +#include "absl/strings/charconv.h" + +#include +#include +#include + +#include "absl/strings/internal/memutil.h" + +namespace absl { +inline namespace lts_2018_06_20 { +namespace { + +// ParseFloat<10> will read the first 19 significant digits of the mantissa. +// This number was chosen for multiple reasons. +// +// (a) First, for whatever integer type we choose to represent the mantissa, we +// want to choose the largest possible number of decimal digits for that integer +// type. We are using uint64_t, which can express any 19-digit unsigned +// integer. +// +// (b) Second, we need to parse enough digits that the binary value of any +// mantissa we capture has more bits of resolution than the mantissa +// representation in the target float. Our algorithm requires at least 3 bits +// of headway, but 19 decimal digits give a little more than that. +// +// The following static assertions verify the above comments: +constexpr int kDecimalMantissaDigitsMax = 19; + +static_assert(std::numeric_limits::digits10 == + kDecimalMantissaDigitsMax, + "(a) above"); + +// IEEE doubles, which we assume in Abseil, have 53 binary bits of mantissa. +static_assert(std::numeric_limits::is_iec559, "IEEE double assumed"); +static_assert(std::numeric_limits::radix == 2, "IEEE double fact"); +static_assert(std::numeric_limits::digits == 53, "IEEE double fact"); + +// The lowest valued 19-digit decimal mantissa we can read still contains +// sufficient information to reconstruct a binary mantissa. +static_assert(1000000000000000000u > (uint64_t(1) << (53 + 3)), "(b) above"); + +// ParseFloat<16> will read the first 15 significant digits of the mantissa. +// +// Because a base-16-to-base-2 conversion can be done exactly, we do not need +// to maximize the number of scanned hex digits to improve our conversion. What +// is required is to scan two more bits than the mantissa can represent, so that +// we always round correctly. +// +// (One extra bit does not suffice to perform correct rounding, since a number +// exactly halfway between two representable floats has unique rounding rules, +// so we need to differentiate between a "halfway between" number and a "closer +// to the larger value" number.) +constexpr int kHexadecimalMantissaDigitsMax = 15; + +// The minimum number of significant bits that will be read from +// kHexadecimalMantissaDigitsMax hex digits. We must subtract by three, since +// the most significant digit can be a "1", which only contributes a single +// significant bit. +constexpr int kGuaranteedHexadecimalMantissaBitPrecision = + 4 * kHexadecimalMantissaDigitsMax - 3; + +static_assert(kGuaranteedHexadecimalMantissaBitPrecision > + std::numeric_limits::digits + 2, + "kHexadecimalMantissaDigitsMax too small"); + +// We also impose a limit on the number of significant digits we will read from +// an exponent, to avoid having to deal with integer overflow. We use 9 for +// this purpose. +// +// If we read a 9 digit exponent, the end result of the conversion will +// necessarily be infinity or zero, depending on the sign of the exponent. +// Therefore we can just drop extra digits on the floor without any extra +// logic. +constexpr int kDecimalExponentDigitsMax = 9; +static_assert(std::numeric_limits::digits10 >= kDecimalExponentDigitsMax, + "int type too small"); + +// To avoid incredibly large inputs causing integer overflow for our exponent, +// we impose an arbitrary but very large limit on the number of significant +// digits we will accept. The implementation refuses to match a std::string with +// more consecutive significant mantissa digits than this. +constexpr int kDecimalDigitLimit = 50000000; + +// Corresponding limit for hexadecimal digit inputs. This is one fourth the +// amount of kDecimalDigitLimit, since each dropped hexadecimal digit requires +// a binary exponent adjustment of 4. +constexpr int kHexadecimalDigitLimit = kDecimalDigitLimit / 4; + +// The largest exponent we can read is 999999999 (per +// kDecimalExponentDigitsMax), and the largest exponent adjustment we can get +// from dropped mantissa digits is 2 * kDecimalDigitLimit, and the sum of these +// comfortably fits in an integer. +// +// We count kDecimalDigitLimit twice because there are independent limits for +// numbers before and after the decimal point. (In the case where there are no +// significant digits before the decimal point, there are independent limits for +// post-decimal-point leading zeroes and for significant digits.) +static_assert(999999999 + 2 * kDecimalDigitLimit < + std::numeric_limits::max(), + "int type too small"); +static_assert(999999999 + 2 * (4 * kHexadecimalDigitLimit) < + std::numeric_limits::max(), + "int type too small"); + +// Returns true if the provided bitfield allows parsing an exponent value +// (e.g., "1.5e100"). +bool AllowExponent(chars_format flags) { + bool fixed = (flags & chars_format::fixed) == chars_format::fixed; + bool scientific = + (flags & chars_format::scientific) == chars_format::scientific; + return scientific || !fixed; +} + +// Returns true if the provided bitfield requires an exponent value be present. +bool RequireExponent(chars_format flags) { + bool fixed = (flags & chars_format::fixed) == chars_format::fixed; + bool scientific = + (flags & chars_format::scientific) == chars_format::scientific; + return scientific && !fixed; +} + +const int8_t kAsciiToInt[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +// Returns true if `ch` is a digit in the given base +template +bool IsDigit(char ch); + +// Converts a valid `ch` to its digit value in the given base. +template +unsigned ToDigit(char ch); + +// Returns true if `ch` is the exponent delimiter for the given base. +template +bool IsExponentCharacter(char ch); + +// Returns the maximum number of significant digits we will read for a float +// in the given base. +template +constexpr int MantissaDigitsMax(); + +// Returns the largest consecutive run of digits we will accept when parsing a +// number in the given base. +template +constexpr int DigitLimit(); + +// Returns the amount the exponent must be adjusted by for each dropped digit. +// (For decimal this is 1, since the digits are in base 10 and the exponent base +// is also 10, but for hexadecimal this is 4, since the digits are base 16 but +// the exponent base is 2.) +template +constexpr int DigitMagnitude(); + +template <> +bool IsDigit<10>(char ch) { + return ch >= '0' && ch <= '9'; +} +template <> +bool IsDigit<16>(char ch) { + return kAsciiToInt[static_cast(ch)] >= 0; +} + +template <> +unsigned ToDigit<10>(char ch) { + return ch - '0'; +} +template <> +unsigned ToDigit<16>(char ch) { + return kAsciiToInt[static_cast(ch)]; +} + +template <> +bool IsExponentCharacter<10>(char ch) { + return ch == 'e' || ch == 'E'; +} + +template <> +bool IsExponentCharacter<16>(char ch) { + return ch == 'p' || ch == 'P'; +} + +template <> +constexpr int MantissaDigitsMax<10>() { + return kDecimalMantissaDigitsMax; +} +template <> +constexpr int MantissaDigitsMax<16>() { + return kHexadecimalMantissaDigitsMax; +} + +template <> +constexpr int DigitLimit<10>() { + return kDecimalDigitLimit; +} +template <> +constexpr int DigitLimit<16>() { + return kHexadecimalDigitLimit; +} + +template <> +constexpr int DigitMagnitude<10>() { + return 1; +} +template <> +constexpr int DigitMagnitude<16>() { + return 4; +} + +// Reads decimal digits from [begin, end) into *out. Returns the number of +// digits consumed. +// +// After max_digits has been read, keeps consuming characters, but no longer +// adjusts *out. If a nonzero digit is dropped this way, *dropped_nonzero_digit +// is set; otherwise, it is left unmodified. +// +// If no digits are matched, returns 0 and leaves *out unchanged. +// +// ConsumeDigits does not protect against overflow on *out; max_digits must +// be chosen with respect to type T to avoid the possibility of overflow. +template +std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits, + T* out, bool* dropped_nonzero_digit) { + if (base == 10) { + assert(max_digits <= std::numeric_limits::digits10); + } else if (base == 16) { + assert(max_digits * 4 <= std::numeric_limits::digits); + } + const char* const original_begin = begin; + T accumulator = *out; + const char* significant_digits_end = + (end - begin > max_digits) ? begin + max_digits : end; + while (begin < significant_digits_end && IsDigit(*begin)) { + // Do not guard against *out overflow; max_digits was chosen to avoid this. + // Do assert against it, to detect problems in debug builds. + auto digit = static_cast(ToDigit(*begin)); + assert(accumulator * base >= accumulator); + accumulator *= base; + assert(accumulator + digit >= accumulator); + accumulator += digit; + ++begin; + } + bool dropped_nonzero = false; + while (begin < end && IsDigit(*begin)) { + dropped_nonzero = dropped_nonzero || (*begin != '0'); + ++begin; + } + if (dropped_nonzero && dropped_nonzero_digit != nullptr) { + *dropped_nonzero_digit = true; + } + *out = accumulator; + return begin - original_begin; +} + +// Returns true if `v` is one of the chars allowed inside parentheses following +// a NaN. +bool IsNanChar(char v) { + return (v == '_') || (v >= '0' && v <= '9') || (v >= 'a' && v <= 'z') || + (v >= 'A' && v <= 'Z'); +} + +// Checks the range [begin, end) for a strtod()-formatted infinity or NaN. If +// one is found, sets `out` appropriately and returns true. +bool ParseInfinityOrNan(const char* begin, const char* end, + strings_internal::ParsedFloat* out) { + if (end - begin < 3) { + return false; + } + switch (*begin) { + case 'i': + case 'I': { + // An infinity std::string consists of the characters "inf" or "infinity", + // case insensitive. + if (strings_internal::memcasecmp(begin + 1, "nf", 2) != 0) { + return false; + } + out->type = strings_internal::FloatType::kInfinity; + if (end - begin >= 8 && + strings_internal::memcasecmp(begin + 3, "inity", 5) == 0) { + out->end = begin + 8; + } else { + out->end = begin + 3; + } + return true; + } + case 'n': + case 'N': { + // A NaN consists of the characters "nan", case insensitive, optionally + // followed by a parenthesized sequence of zero or more alphanumeric + // characters and/or underscores. + if (strings_internal::memcasecmp(begin + 1, "an", 2) != 0) { + return false; + } + out->type = strings_internal::FloatType::kNan; + out->end = begin + 3; + // NaN is allowed to be followed by a parenthesized std::string, consisting of + // only the characters [a-zA-Z0-9_]. Match that if it's present. + begin += 3; + if (begin < end && *begin == '(') { + const char* nan_begin = begin + 1; + while (nan_begin < end && IsNanChar(*nan_begin)) { + ++nan_begin; + } + if (nan_begin < end && *nan_begin == ')') { + // We found an extra NaN specifier range + out->subrange_begin = begin + 1; + out->subrange_end = nan_begin; + out->end = nan_begin + 1; + } + } + return true; + } + default: + return false; + } +} +} // namespace + +namespace strings_internal { + +template +strings_internal::ParsedFloat ParseFloat(const char* begin, const char* end, + chars_format format_flags) { + strings_internal::ParsedFloat result; + + // Exit early if we're given an empty range. + if (begin == end) return result; + + // Handle the infinity and NaN cases. + if (ParseInfinityOrNan(begin, end, &result)) { + return result; + } + + const char* const mantissa_begin = begin; + while (begin < end && *begin == '0') { + ++begin; // skip leading zeros + } + uint64_t mantissa = 0; + + int exponent_adjustment = 0; + bool mantissa_is_inexact = false; + std::size_t pre_decimal_digits = ConsumeDigits( + begin, end, MantissaDigitsMax(), &mantissa, &mantissa_is_inexact); + begin += pre_decimal_digits; + int digits_left; + if (pre_decimal_digits >= DigitLimit()) { + // refuse to parse pathological inputs + return result; + } else if (pre_decimal_digits > MantissaDigitsMax()) { + // We dropped some non-fraction digits on the floor. Adjust our exponent + // to compensate. + exponent_adjustment = + static_cast(pre_decimal_digits - MantissaDigitsMax()); + digits_left = 0; + } else { + digits_left = + static_cast(MantissaDigitsMax() - pre_decimal_digits); + } + if (begin < end && *begin == '.') { + ++begin; + if (mantissa == 0) { + // If we haven't seen any nonzero digits yet, keep skipping zeros. We + // have to adjust the exponent to reflect the changed place value. + const char* begin_zeros = begin; + while (begin < end && *begin == '0') { + ++begin; + } + std::size_t zeros_skipped = begin - begin_zeros; + if (zeros_skipped >= DigitLimit()) { + // refuse to parse pathological inputs + return result; + } + exponent_adjustment -= static_cast(zeros_skipped); + } + std::size_t post_decimal_digits = ConsumeDigits( + begin, end, digits_left, &mantissa, &mantissa_is_inexact); + begin += post_decimal_digits; + + // Since `mantissa` is an integer, each significant digit we read after + // the decimal point requires an adjustment to the exponent. "1.23e0" will + // be stored as `mantissa` == 123 and `exponent` == -2 (that is, + // "123e-2"). + if (post_decimal_digits >= DigitLimit()) { + // refuse to parse pathological inputs + return result; + } else if (post_decimal_digits > digits_left) { + exponent_adjustment -= digits_left; + } else { + exponent_adjustment -= post_decimal_digits; + } + } + // If we've found no mantissa whatsoever, this isn't a number. + if (mantissa_begin == begin) { + return result; + } + // A bare "." doesn't count as a mantissa either. + if (begin - mantissa_begin == 1 && *mantissa_begin == '.') { + return result; + } + + if (mantissa_is_inexact) { + // We dropped significant digits on the floor. Handle this appropriately. + if (base == 10) { + // If we truncated significant decimal digits, store the full range of the + // mantissa for future big integer math for exact rounding. + result.subrange_begin = mantissa_begin; + result.subrange_end = begin; + } else if (base == 16) { + // If we truncated hex digits, reflect this fact by setting the low + // ("sticky") bit. This allows for correct rounding in all cases. + mantissa |= 1; + } + } + result.mantissa = mantissa; + + const char* const exponent_begin = begin; + result.literal_exponent = 0; + bool found_exponent = false; + if (AllowExponent(format_flags) && begin < end && + IsExponentCharacter(*begin)) { + bool negative_exponent = false; + ++begin; + if (begin < end && *begin == '-') { + negative_exponent = true; + ++begin; + } else if (begin < end && *begin == '+') { + ++begin; + } + const char* const exponent_digits_begin = begin; + // Exponent is always expressed in decimal, even for hexadecimal floats. + begin += ConsumeDigits<10>(begin, end, kDecimalExponentDigitsMax, + &result.literal_exponent, nullptr); + if (begin == exponent_digits_begin) { + // there were no digits where we expected an exponent. We failed to read + // an exponent and should not consume the 'e' after all. Rewind 'begin'. + found_exponent = false; + begin = exponent_begin; + } else { + found_exponent = true; + if (negative_exponent) { + result.literal_exponent = -result.literal_exponent; + } + } + } + + if (!found_exponent && RequireExponent(format_flags)) { + // Provided flags required an exponent, but none was found. This results + // in a failure to scan. + return result; + } + + // Success! + result.type = strings_internal::FloatType::kNumber; + if (result.mantissa > 0) { + result.exponent = result.literal_exponent + + (DigitMagnitude() * exponent_adjustment); + } else { + result.exponent = 0; + } + result.end = begin; + return result; +} + +template ParsedFloat ParseFloat<10>(const char* begin, const char* end, + chars_format format_flags); +template ParsedFloat ParseFloat<16>(const char* begin, const char* end, + chars_format format_flags); + +} // namespace strings_internal +} // inline namespace lts_2018_06_20 +} // namespace absl diff --git a/absl/strings/internal/charconv_parse.h b/absl/strings/internal/charconv_parse.h new file mode 100644 index 00000000000..41f4f40d7b8 --- /dev/null +++ b/absl/strings/internal/charconv_parse.h @@ -0,0 +1,98 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_ +#define ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_ + +#include + +#include "absl/strings/charconv.h" + +namespace absl { +inline namespace lts_2018_06_20 { +namespace strings_internal { + +// Enum indicating whether a parsed float is a number or special value. +enum class FloatType { kNumber, kInfinity, kNan }; + +// The decomposed parts of a parsed `float` or `double`. +struct ParsedFloat { + // Representation of the parsed mantissa, with the decimal point adjusted to + // make it an integer. + // + // During decimal scanning, this contains 19 significant digits worth of + // mantissa value. If digits beyond this point are found, they + // are truncated, and if any of these dropped digits are nonzero, then + // `mantissa` is inexact, and the full mantissa is stored in [subrange_begin, + // subrange_end). + // + // During hexadecimal scanning, this contains 15 significant hex digits worth + // of mantissa value. Digits beyond this point are sticky -- they are + // truncated, but if any dropped digits are nonzero, the low bit of mantissa + // will be set. (This allows for precise rounding, and avoids the need + // to store the full mantissa in [subrange_begin, subrange_end).) + uint64_t mantissa = 0; + + // Floating point expontent. This reflects any decimal point adjustments and + // any truncated digits from the mantissa. The absolute value of the parsed + // number is represented by mantissa * (base ** exponent), where base==10 for + // decimal floats, and base==2 for hexadecimal floats. + int exponent = 0; + + // The literal exponent value scanned from the input, or 0 if none was + // present. This does not reflect any adjustments applied to mantissa. + int literal_exponent = 0; + + // The type of number scanned. + FloatType type = FloatType::kNumber; + + // When non-null, [subrange_begin, subrange_end) marks a range of characters + // that require further processing. The meaning is dependent on float type. + // If type == kNumber and this is set, this is a "wide input": the input + // mantissa contained more than 19 digits. The range contains the full + // mantissa. It plus `literal_exponent` need to be examined to find the best + // floating point match. + // If type == kNan and this is set, the range marks the contents of a + // matched parenthesized character region after the NaN. + const char* subrange_begin = nullptr; + const char* subrange_end = nullptr; + + // One-past-the-end of the successfully parsed region, or nullptr if no + // matching pattern was found. + const char* end = nullptr; +}; + +// Read the floating point number in the provided range, and populate +// ParsedFloat accordingly. +// +// format_flags is a bitmask value specifying what patterns this API will match. +// `scientific` and `fixed` are honored per std::from_chars rules +// ([utility.from.chars], C++17): if exactly one of these bits is set, then an +// exponent is required, or dislallowed, respectively. +// +// Template parameter `base` must be either 10 or 16. For base 16, a "0x" is +// *not* consumed. The `hex` bit from format_flags is ignored by ParseFloat. +template +ParsedFloat ParseFloat(const char* begin, const char* end, + absl::chars_format format_flags); + +extern template ParsedFloat ParseFloat<10>(const char* begin, const char* end, + absl::chars_format format_flags); +extern template ParsedFloat ParseFloat<16>(const char* begin, const char* end, + absl::chars_format format_flags); + +} // namespace strings_internal +} // inline namespace lts_2018_06_20 +} // namespace absl +#endif // ABSL_STRINGS_INTERNAL_CHARCONV_PARSE_H_ diff --git a/absl/strings/internal/charconv_parse_test.cc b/absl/strings/internal/charconv_parse_test.cc new file mode 100644 index 00000000000..1ff86004973 --- /dev/null +++ b/absl/strings/internal/charconv_parse_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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/strings/internal/charconv_parse.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/str_cat.h" + +using absl::chars_format; +using absl::strings_internal::FloatType; +using absl::strings_internal::ParsedFloat; +using absl::strings_internal::ParseFloat; + +namespace { + +// Check that a given std::string input is parsed to the expected mantissa and +// exponent. +// +// Input std::string `s` must contain a '$' character. It marks the end of the +// characters that should be consumed by the match. It is stripped from the +// input to ParseFloat. +// +// If input std::string `s` contains '[' and ']' characters, these mark the region +// of characters that should be marked as the "subrange". For NaNs, this is +// the location of the extended NaN std::string. For numbers, this is the location +// of the full, over-large mantissa. +template +void ExpectParsedFloat(std::string s, absl::chars_format format_flags, + FloatType expected_type, uint64_t expected_mantissa, + int expected_exponent, + int expected_literal_exponent = -999) { + SCOPED_TRACE(s); + + int begin_subrange = -1; + int end_subrange = -1; + // If s contains '[' and ']', then strip these characters and set the subrange + // indices appropriately. + std::string::size_type open_bracket_pos = s.find('['); + if (open_bracket_pos != std::string::npos) { + begin_subrange = static_cast(open_bracket_pos); + s.replace(open_bracket_pos, 1, ""); + std::string::size_type close_bracket_pos = s.find(']'); + ABSL_RAW_CHECK(close_bracket_pos != absl::string_view::npos, + "Test input contains [ without matching ]"); + end_subrange = static_cast(close_bracket_pos); + s.replace(close_bracket_pos, 1, ""); + } + const std::string::size_type expected_characters_matched = s.find('$'); + ABSL_RAW_CHECK(expected_characters_matched != std::string::npos, + "Input std::string must contain $"); + s.replace(expected_characters_matched, 1, ""); + + ParsedFloat parsed = + ParseFloat(s.data(), s.data() + s.size(), format_flags); + + EXPECT_NE(parsed.end, nullptr); + if (parsed.end == nullptr) { + return; // The following tests are not useful if we fully failed to parse + } + EXPECT_EQ(parsed.type, expected_type); + if (begin_subrange == -1) { + EXPECT_EQ(parsed.subrange_begin, nullptr); + EXPECT_EQ(parsed.subrange_end, nullptr); + } else { + EXPECT_EQ(parsed.subrange_begin, s.data() + begin_subrange); + EXPECT_EQ(parsed.subrange_end, s.data() + end_subrange); + } + if (parsed.type == FloatType::kNumber) { + EXPECT_EQ(parsed.mantissa, expected_mantissa); + EXPECT_EQ(parsed.exponent, expected_exponent); + if (expected_literal_exponent != -999) { + EXPECT_EQ(parsed.literal_exponent, expected_literal_exponent); + } + } + auto characters_matched = static_cast(parsed.end - s.data()); + EXPECT_EQ(characters_matched, expected_characters_matched); +} + +// Check that a given std::string input is parsed to the expected mantissa and +// exponent. +// +// Input std::string `s` must contain a '$' character. It marks the end of the +// characters that were consumed by the match. +template +void ExpectNumber(std::string s, absl::chars_format format_flags, + uint64_t expected_mantissa, int expected_exponent, + int expected_literal_exponent = -999) { + ExpectParsedFloat(std::move(s), format_flags, FloatType::kNumber, + expected_mantissa, expected_exponent, + expected_literal_exponent); +} + +// Check that a given std::string input is parsed to the given special value. +// +// This tests against both number bases, since infinities and NaNs have +// identical representations in both modes. +void ExpectSpecial(const std::string& s, absl::chars_format format_flags, + FloatType type) { + ExpectParsedFloat<10>(s, format_flags, type, 0, 0); + ExpectParsedFloat<16>(s, format_flags, type, 0, 0); +} + +// Check that a given input std::string is not matched by Float. +template +void ExpectFailedParse(absl::string_view s, absl::chars_format format_flags) { + ParsedFloat parsed = + ParseFloat(s.data(), s.data() + s.size(), format_flags); + EXPECT_EQ(parsed.end, nullptr); +} + +TEST(ParseFloat, SimpleValue) { + // Test that various forms of floating point numbers all parse correctly. + ExpectNumber<10>("1.23456789e5$", chars_format::general, 123456789, -3); + ExpectNumber<10>("1.23456789e+5$", chars_format::general, 123456789, -3); + ExpectNumber<10>("1.23456789E5$", chars_format::general, 123456789, -3); + ExpectNumber<10>("1.23456789e05$", chars_format::general, 123456789, -3); + ExpectNumber<10>("123.456789e3$", chars_format::general, 123456789, -3); + ExpectNumber<10>("0.000123456789e9$", chars_format::general, 123456789, -3); + ExpectNumber<10>("123456.789$", chars_format::general, 123456789, -3); + ExpectNumber<10>("123456789e-3$", chars_format::general, 123456789, -3); + + ExpectNumber<16>("1.234abcdefp28$", chars_format::general, 0x1234abcdef, -8); + ExpectNumber<16>("1.234abcdefp+28$", chars_format::general, 0x1234abcdef, -8); + ExpectNumber<16>("1.234ABCDEFp28$", chars_format::general, 0x1234abcdef, -8); + ExpectNumber<16>("1.234AbCdEfP0028$", chars_format::general, 0x1234abcdef, + -8); + ExpectNumber<16>("123.4abcdefp20$", chars_format::general, 0x1234abcdef, -8); + ExpectNumber<16>("0.0001234abcdefp44$", chars_format::general, 0x1234abcdef, + -8); + ExpectNumber<16>("1234abcd.ef$", chars_format::general, 0x1234abcdef, -8); + ExpectNumber<16>("1234abcdefp-8$", chars_format::general, 0x1234abcdef, -8); + + // ExpectNumber does not attempt to drop trailing zeroes. + ExpectNumber<10>("0001.2345678900e005$", chars_format::general, 12345678900, + -5); + ExpectNumber<16>("0001.234abcdef000p28$", chars_format::general, + 0x1234abcdef000, -20); + + // Ensure non-matching characters after a number are ignored, even when they + // look like potentially matching characters. + ExpectNumber<10>("1.23456789e5$ ", chars_format::general, 123456789, -3); + ExpectNumber<10>("1.23456789e5$e5e5", chars_format::general, 123456789, -3); + ExpectNumber<10>("1.23456789e5$.25", chars_format::general, 123456789, -3); + ExpectNumber<10>("1.23456789e5$-", chars_format::general, 123456789, -3); + ExpectNumber<10>("1.23456789e5$PUPPERS!!!", chars_format::general, 123456789, + -3); + ExpectNumber<10>("123456.789$efghij", chars_format::general, 123456789, -3); + ExpectNumber<10>("123456.789$e", chars_format::general, 123456789, -3); + ExpectNumber<10>("123456.789$p5", chars_format::general, 123456789, -3); + ExpectNumber<10>("123456.789$.10", chars_format::general, 123456789, -3); + + ExpectNumber<16>("1.234abcdefp28$ ", chars_format::general, 0x1234abcdef, + -8); + ExpectNumber<16>("1.234abcdefp28$p28", chars_format::general, 0x1234abcdef, + -8); + ExpectNumber<16>("1.234abcdefp28$.125", chars_format::general, 0x1234abcdef, + -8); + ExpectNumber<16>("1.234abcdefp28$-", chars_format::general, 0x1234abcdef, -8); + ExpectNumber<16>("1.234abcdefp28$KITTEHS!!!", chars_format::general, + 0x1234abcdef, -8); + ExpectNumber<16>("1234abcd.ef$ghijk", chars_format::general, 0x1234abcdef, + -8); + ExpectNumber<16>("1234abcd.ef$p", chars_format::general, 0x1234abcdef, -8); + ExpectNumber<16>("1234abcd.ef$.10", chars_format::general, 0x1234abcdef, -8); + + // Ensure we can read a full resolution mantissa without overflow. + ExpectNumber<10>("9999999999999999999$", chars_format::general, + 9999999999999999999u, 0); + ExpectNumber<16>("fffffffffffffff$", chars_format::general, + 0xfffffffffffffffu, 0); + + // Check that zero is consistently read. + ExpectNumber<10>("0$", chars_format::general, 0, 0); + ExpectNumber<16>("0$", chars_format::general, 0, 0); + ExpectNumber<10>("000000000000000000000000000000000000000$", + chars_format::general, 0, 0); + ExpectNumber<16>("000000000000000000000000000000000000000$", + chars_format::general, 0, 0); + ExpectNumber<10>("0000000000000000000000.000000000000000000$", + chars_format::general, 0, 0); + ExpectNumber<16>("0000000000000000000000.000000000000000000$", + chars_format::general, 0, 0); + ExpectNumber<10>("0.00000000000000000000000000000000e123456$", + chars_format::general, 0, 0); + ExpectNumber<16>("0.00000000000000000000000000000000p123456$", + chars_format::general, 0, 0); +} + +TEST(ParseFloat, LargeDecimalMantissa) { + // After 19 significant decimal digits in the mantissa, ParsedFloat will + // truncate additional digits. We need to test that: + // 1) the truncation to 19 digits happens + // 2) the returned exponent reflects the dropped significant digits + // 3) a correct literal_exponent is set + // + // If and only if a significant digit is found after 19 digits, then the + // entirety of the mantissa in case the exact value is needed to make a + // rounding decision. The [ and ] characters below denote where such a + // subregion was marked by by ParseFloat. They are not part of the input. + + // Mark a capture group only if a dropped digit is significant (nonzero). + ExpectNumber<10>("100000000000000000000000000$", chars_format::general, + 1000000000000000000, + /* adjusted exponent */ 8); + + ExpectNumber<10>("123456789123456789100000000$", chars_format::general, + 1234567891234567891, + /* adjusted exponent */ 8); + + ExpectNumber<10>("[123456789123456789123456789]$", chars_format::general, + 1234567891234567891, + /* adjusted exponent */ 8, + /* literal exponent */ 0); + + ExpectNumber<10>("[123456789123456789100000009]$", chars_format::general, + 1234567891234567891, + /* adjusted exponent */ 8, + /* literal exponent */ 0); + + ExpectNumber<10>("[123456789123456789120000000]$", chars_format::general, + 1234567891234567891, + /* adjusted exponent */ 8, + /* literal exponent */ 0); + + // Leading zeroes should not count towards the 19 significant digit limit + ExpectNumber<10>("[00000000123456789123456789123456789]$", + chars_format::general, 1234567891234567891, + /* adjusted exponent */ 8, + /* literal exponent */ 0); + + ExpectNumber<10>("00000000123456789123456789100000000$", + chars_format::general, 1234567891234567891, + /* adjusted exponent */ 8); + + // Truncated digits after the decimal point should not cause a further + // exponent adjustment. + ExpectNumber<10>("1.234567891234567891e123$", chars_format::general, + 1234567891234567891, 105); + ExpectNumber<10>("[1.23456789123456789123456789]e123$", chars_format::general, + 1234567891234567891, + /* adjusted exponent */ 105, + /* literal exponent */ 123); + + // Ensure we truncate, and not round. (The from_chars algorithm we use + // depends on our guess missing low, if it misses, so we need the rounding + // error to be downward.) + ExpectNumber<10>("[1999999999999999999999]$", chars_format::general, + 1999999999999999999, + /* adjusted exponent */ 3, + /* literal exponent */ 0); +} + +TEST(ParseFloat, LargeHexadecimalMantissa) { + // After 15 significant hex digits in the mantissa, ParsedFloat will treat + // additional digits as sticky, We need to test that: + // 1) The truncation to 15 digits happens + // 2) The returned exponent reflects the dropped significant digits + // 3) If a nonzero digit is dropped, the low bit of mantissa is set. + + ExpectNumber<16>("123456789abcdef123456789abcdef$", chars_format::general, + 0x123456789abcdef, 60); + + // Leading zeroes should not count towards the 15 significant digit limit + ExpectNumber<16>("000000123456789abcdef123456789abcdef$", + chars_format::general, 0x123456789abcdef, 60); + + // Truncated digits after the radix point should not cause a further + // exponent adjustment. + ExpectNumber<16>("1.23456789abcdefp100$", chars_format::general, + 0x123456789abcdef, 44); + ExpectNumber<16>("1.23456789abcdef123456789abcdefp100$", + chars_format::general, 0x123456789abcdef, 44); + + // test sticky digit behavior. The low bit should be set iff any dropped + // digit is nonzero. + ExpectNumber<16>("123456789abcdee123456789abcdee$", chars_format::general, + 0x123456789abcdef, 60); + ExpectNumber<16>("123456789abcdee000000000000001$", chars_format::general, + 0x123456789abcdef, 60); + ExpectNumber<16>("123456789abcdee000000000000000$", chars_format::general, + 0x123456789abcdee, 60); +} + +TEST(ParseFloat, ScientificVsFixed) { + // In fixed mode, an exponent is never matched (but the remainder of the + // number will be matched.) + ExpectNumber<10>("1.23456789$e5", chars_format::fixed, 123456789, -8); + ExpectNumber<10>("123456.789$", chars_format::fixed, 123456789, -3); + ExpectNumber<16>("1.234abcdef$p28", chars_format::fixed, 0x1234abcdef, -36); + ExpectNumber<16>("1234abcd.ef$", chars_format::fixed, 0x1234abcdef, -8); + + // In scientific mode, numbers don't match *unless* they have an exponent. + ExpectNumber<10>("1.23456789e5$", chars_format::scientific, 123456789, -3); + ExpectFailedParse<10>("-123456.789$", chars_format::scientific); + ExpectNumber<16>("1.234abcdefp28$", chars_format::scientific, 0x1234abcdef, + -8); + ExpectFailedParse<16>("1234abcd.ef$", chars_format::scientific); +} + +TEST(ParseFloat, Infinity) { + ExpectFailedParse<10>("in", chars_format::general); + ExpectFailedParse<16>("in", chars_format::general); + ExpectFailedParse<10>("inx", chars_format::general); + ExpectFailedParse<16>("inx", chars_format::general); + ExpectSpecial("inf$", chars_format::general, FloatType::kInfinity); + ExpectSpecial("Inf$", chars_format::general, FloatType::kInfinity); + ExpectSpecial("INF$", chars_format::general, FloatType::kInfinity); + ExpectSpecial("inf$inite", chars_format::general, FloatType::kInfinity); + ExpectSpecial("iNfInItY$", chars_format::general, FloatType::kInfinity); + ExpectSpecial("infinity$!!!", chars_format::general, FloatType::kInfinity); +} + +TEST(ParseFloat, NaN) { + ExpectFailedParse<10>("na", chars_format::general); + ExpectFailedParse<16>("na", chars_format::general); + ExpectFailedParse<10>("nah", chars_format::general); + ExpectFailedParse<16>("nah", chars_format::general); + ExpectSpecial("nan$", chars_format::general, FloatType::kNan); + ExpectSpecial("NaN$", chars_format::general, FloatType::kNan); + ExpectSpecial("nAn$", chars_format::general, FloatType::kNan); + ExpectSpecial("NAN$", chars_format::general, FloatType::kNan); + ExpectSpecial("NaN$aNaNaNaNaBatman!", chars_format::general, FloatType::kNan); + + // A parenthesized sequence of the characters [a-zA-Z0-9_] is allowed to + // appear after an NaN. Check that this is allowed, and that the correct + // characters are grouped. + // + // (The characters [ and ] in the pattern below delimit the expected matched + // subgroup; they are not part of the input passed to ParseFloat.) + ExpectSpecial("nan([0xabcdef])$", chars_format::general, FloatType::kNan); + ExpectSpecial("nan([0xabcdef])$...", chars_format::general, FloatType::kNan); + ExpectSpecial("nan([0xabcdef])$)...", chars_format::general, FloatType::kNan); + ExpectSpecial("nan([])$", chars_format::general, FloatType::kNan); + ExpectSpecial("nan([aAzZ09_])$", chars_format::general, FloatType::kNan); + // If the subgroup contains illegal characters, don't match it at all. + ExpectSpecial("nan$(bad-char)", chars_format::general, FloatType::kNan); + // Also cope with a missing close paren. + ExpectSpecial("nan$(0xabcdef", chars_format::general, FloatType::kNan); +} + +} // namespace diff --git a/absl/strings/internal/escaping_test_common.h b/absl/strings/internal/escaping_test_common.h index cc41f4312c7..478e05822d6 100644 --- a/absl/strings/internal/escaping_test_common.h +++ b/absl/strings/internal/escaping_test_common.h @@ -22,6 +22,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { struct base64_testcase { @@ -126,6 +127,7 @@ inline const std::array& base64_strings() { } } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_ESCAPING_TEST_COMMON_H_ diff --git a/absl/strings/internal/memutil.cc b/absl/strings/internal/memutil.cc index a0de70dffdb..13ad686cd91 100644 --- a/absl/strings/internal/memutil.cc +++ b/absl/strings/internal/memutil.cc @@ -17,6 +17,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { int memcasecmp(const char* s1, const char* s2, size_t len) { @@ -107,4 +108,5 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, } } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/internal/memutil.h b/absl/strings/internal/memutil.h index a6f1c69138e..828b01d51b5 100644 --- a/absl/strings/internal/memutil.h +++ b/absl/strings/internal/memutil.h @@ -69,6 +69,7 @@ #include "absl/strings/ascii.h" // for absl::ascii_tolower namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { inline char* memcat(char* dest, size_t destlen, const char* src, @@ -141,6 +142,7 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, size_t neelen); } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_MEMUTIL_H_ diff --git a/absl/strings/internal/numbers_test_common.h b/absl/strings/internal/numbers_test_common.h index 20e3af51141..a511dcf780c 100644 --- a/absl/strings/internal/numbers_test_common.h +++ b/absl/strings/internal/numbers_test_common.h @@ -24,6 +24,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { template @@ -173,6 +174,7 @@ inline const std::array& strtouint64_test_cases() { } } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_NUMBERS_TEST_COMMON_H_ diff --git a/absl/strings/internal/ostringstream.cc b/absl/strings/internal/ostringstream.cc index 6ee2b109088..9fe5b3c56d5 100644 --- a/absl/strings/internal/ostringstream.cc +++ b/absl/strings/internal/ostringstream.cc @@ -15,6 +15,7 @@ #include "absl/strings/internal/ostringstream.h" namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { OStringStream::Buf::int_type OStringStream::overflow(int c) { @@ -31,4 +32,5 @@ std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) { } } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/internal/ostringstream.h b/absl/strings/internal/ostringstream.h index 6e1325b9140..66ae3c29d1c 100644 --- a/absl/strings/internal/ostringstream.h +++ b/absl/strings/internal/ostringstream.h @@ -23,6 +23,7 @@ #include "absl/base/port.h" namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { // The same as std::ostringstream but appends to a user-specified std::string, @@ -82,6 +83,7 @@ class OStringStream : private std::basic_streambuf, public std::ostream { }; } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_ diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h index 0157ca0245f..b36903323a6 100644 --- a/absl/strings/internal/resize_uninitialized.h +++ b/absl/strings/internal/resize_uninitialized.h @@ -24,6 +24,7 @@ #include "absl/meta/type_traits.h" // for void_t namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { // Is a subclass of true_type or false_type, depending on whether or not @@ -64,6 +65,7 @@ inline void STLStringResizeUninitialized(string_type* s, size_t new_size) { } } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h index 04c4a5323b8..7fc56a3cfd4 100644 --- a/absl/strings/internal/stl_type_traits.h +++ b/absl/strings/internal/stl_type_traits.h @@ -40,6 +40,7 @@ #include "absl/meta/type_traits.h" namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { template class T> @@ -242,5 +243,6 @@ struct IsStrictlyBaseOfAndConvertibleToSTLContainer IsConvertibleToSTLContainer> {}; } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_ diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h index a734758c21e..5834403e846 100644 --- a/absl/strings/internal/str_join_internal.h +++ b/absl/strings/internal/str_join_internal.h @@ -43,6 +43,7 @@ #include "absl/strings/str_cat.h" namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { // @@ -306,6 +307,7 @@ std::string JoinRange(const Range& range, absl::string_view separator) { } } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_ diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h index a1b10f3addc..e2d2c6b086f 100644 --- a/absl/strings/internal/str_split_internal.h +++ b/absl/strings/internal/str_split_internal.h @@ -47,6 +47,7 @@ #endif // _GLIBCXX_DEBUG namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { // This class is implicitly constructible from everything that absl::string_view @@ -430,6 +431,7 @@ class Splitter { }; } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_ diff --git a/absl/strings/internal/utf8.cc b/absl/strings/internal/utf8.cc index 2415c2ccc45..81b6241bbef 100644 --- a/absl/strings/internal/utf8.cc +++ b/absl/strings/internal/utf8.cc @@ -17,6 +17,7 @@ #include "absl/strings/internal/utf8.h" namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) { @@ -48,4 +49,5 @@ size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) { } } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/internal/utf8.h b/absl/strings/internal/utf8.h index d2c3c0b0120..5a94ae6f5cf 100644 --- a/absl/strings/internal/utf8.h +++ b/absl/strings/internal/utf8.h @@ -22,6 +22,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { // For Unicode code points 0 through 0x10FFFF, EncodeUTF8Char writes @@ -42,6 +43,7 @@ enum { kMaxEncodedUTF8Size = 4 }; size_t EncodeUTF8Char(char *buffer, char32_t utf8_char); } // namespace strings_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_UTF8_H_ diff --git a/absl/strings/match.cc b/absl/strings/match.cc index 25bd7f0b828..8f33c72cc73 100644 --- a/absl/strings/match.cc +++ b/absl/strings/match.cc @@ -17,6 +17,7 @@ #include "absl/strings/internal/memutil.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { bool CaseEqual(absl::string_view piece1, absl::string_view piece2) { @@ -37,4 +38,5 @@ bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) { CaseEqual(text.substr(text.size() - suffix.size()), suffix); } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/match.h b/absl/strings/match.h index 108b6048b04..908f051fcfa 100644 --- a/absl/strings/match.h +++ b/absl/strings/match.h @@ -38,6 +38,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { // StrContains() // @@ -78,6 +79,7 @@ bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix); // in the comparison. bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix); +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_MATCH_H_ diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc index 68ef7999a98..48dca919c02 100644 --- a/absl/strings/numbers.cc +++ b/absl/strings/numbers.cc @@ -32,59 +32,64 @@ #include "absl/base/internal/raw_logging.h" #include "absl/strings/ascii.h" +#include "absl/strings/charconv.h" #include "absl/strings/internal/bits.h" #include "absl/strings/internal/memutil.h" #include "absl/strings/str_cat.h" namespace absl { +inline namespace lts_2018_06_20 { bool SimpleAtof(absl::string_view str, float* value) { *value = 0.0; - if (str.empty()) return false; - char buf[32]; - std::unique_ptr bigbuf; - char* ptr = buf; - if (str.size() > sizeof(buf) - 1) { - bigbuf.reset(new char[str.size() + 1]); - ptr = bigbuf.get(); - } - memcpy(ptr, str.data(), str.size()); - ptr[str.size()] = '\0'; - - char* endptr; - *value = strtof(ptr, &endptr); - if (endptr != ptr) { - while (absl::ascii_isspace(*endptr)) ++endptr; - } - // Ignore range errors from strtod/strtof. - // The values it returns on underflow and - // overflow are the right fallback in a - // robust setting. - return *ptr != '\0' && *endptr == '\0'; + str = StripAsciiWhitespace(str); + if (!str.empty() && str[0] == '+') { + str.remove_prefix(1); + } + auto result = absl::from_chars(str.data(), str.data() + str.size(), *value); + if (result.ec == std::errc::invalid_argument) { + return false; + } + if (result.ptr != str.data() + str.size()) { + // not all non-whitespace characters consumed + return false; + } + // from_chars() with DR 3801's current wording will return max() on + // overflow. SimpleAtof returns infinity instead. + if (result.ec == std::errc::result_out_of_range) { + if (*value > 1.0) { + *value = std::numeric_limits::infinity(); + } else if (*value < -1.0) { + *value = -std::numeric_limits::infinity(); + } + } + return true; } bool SimpleAtod(absl::string_view str, double* value) { *value = 0.0; - if (str.empty()) return false; - char buf[32]; - std::unique_ptr bigbuf; - char* ptr = buf; - if (str.size() > sizeof(buf) - 1) { - bigbuf.reset(new char[str.size() + 1]); - ptr = bigbuf.get(); - } - memcpy(ptr, str.data(), str.size()); - ptr[str.size()] = '\0'; - - char* endptr; - *value = strtod(ptr, &endptr); - if (endptr != ptr) { - while (absl::ascii_isspace(*endptr)) ++endptr; - } - // Ignore range errors from strtod. The values it - // returns on underflow and overflow are the right - // fallback in a robust setting. - return *ptr != '\0' && *endptr == '\0'; + str = StripAsciiWhitespace(str); + if (!str.empty() && str[0] == '+') { + str.remove_prefix(1); + } + auto result = absl::from_chars(str.data(), str.data() + str.size(), *value); + if (result.ec == std::errc::invalid_argument) { + return false; + } + if (result.ptr != str.data() + str.size()) { + // not all non-whitespace characters consumed + return false; + } + // from_chars() with DR 3801's current wording will return max() on + // overflow. SimpleAtod returns infinity instead. + if (result.ec == std::errc::result_out_of_range) { + if (*value > 1.0) { + *value = std::numeric_limits::infinity(); + } else if (*value < -1.0) { + *value = -std::numeric_limits::infinity(); + } + } + return true; } namespace { @@ -905,4 +910,5 @@ bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) { } } // namespace numbers_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index cf3c597266c..8efdb200a93 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -38,6 +38,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { // SimpleAtoi() // @@ -72,11 +73,13 @@ ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* value); // are interpreted as boolean `false`: "false", "f", "no", "n", "0". ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* value); +} // inline namespace lts_2018_06_20 } // namespace absl // End of public API. Implementation details follow. namespace absl { +inline namespace lts_2018_06_20 { namespace numbers_internal { // safe_strto?() functions for implementing SimpleAtoi() @@ -179,6 +182,7 @@ ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view s, int_type* out) { return numbers_internal::safe_strtoi_base(s, out, 10); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_NUMBERS_H_ diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index 3fe8c95eca9..499f3f4116f 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc @@ -23,6 +23,7 @@ #include "absl/strings/internal/resize_uninitialized.h" namespace absl { +inline namespace lts_2018_06_20 { AlphaNum::AlphaNum(Hex hex) { char* const end = &digits_[numbers_internal::kFastToBufferSize]; @@ -236,4 +237,5 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, assert(out == begin + dest->size()); } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index e5501a5012e..c694cbc127c 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -63,6 +63,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { // AlphaNumBuffer allows a way to pass a std::string to StrCat without having to do @@ -380,6 +381,7 @@ SixDigits(double d) { return result; } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_STR_CAT_H_ diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h index bd4d0e1d932..bce2cd66b99 100644 --- a/absl/strings/str_join.h +++ b/absl/strings/str_join.h @@ -59,6 +59,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { // ----------------------------------------------------------------------------- // Concept: Formatter @@ -283,6 +284,7 @@ std::string StrJoin(const std::tuple& value, absl::string_view separator) return strings_internal::JoinAlgorithm(value, separator, AlphaNumFormatter()); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_STR_JOIN_H_ diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc index 69efa357138..c50d314aa9f 100644 --- a/absl/strings/str_replace.cc +++ b/absl/strings/str_replace.cc @@ -17,6 +17,7 @@ #include "absl/strings/str_cat.h" namespace absl { +inline namespace lts_2018_06_20 { namespace strings_internal { using FixedMapping = @@ -76,4 +77,5 @@ int StrReplaceAll(strings_internal::FixedMapping replacements, std::string* targ return StrReplaceAll(replacements, target); } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h index f4d9bb9545d..7ba4394658b 100644 --- a/absl/strings/str_replace.h +++ b/absl/strings/str_replace.h @@ -46,6 +46,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { // StrReplaceAll() // @@ -208,6 +209,7 @@ int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) { return substitutions; } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_STR_REPLACE_H_ diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc index 0207213c26b..2a31e09e384 100644 --- a/absl/strings/str_split.cc +++ b/absl/strings/str_split.cc @@ -27,6 +27,7 @@ #include "absl/strings/ascii.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { @@ -133,4 +134,5 @@ absl::string_view ByLength::Find(absl::string_view text, return absl::string_view(substr.begin() + length_, 0); } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h index 1f089b9377b..b9f586b965f 100644 --- a/absl/strings/str_split.h +++ b/absl/strings/str_split.h @@ -49,6 +49,7 @@ #include "absl/strings/strip.h" namespace absl { +inline namespace lts_2018_06_20 { //------------------------------------------------------------------------------ // Delimiters @@ -506,6 +507,7 @@ StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d, std::move(text), DelimiterType(d), std::move(p)); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_STR_SPLIT_H_ diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc index 4ceeb6bff58..92197a753b8 100644 --- a/absl/strings/string_view.cc +++ b/absl/strings/string_view.cc @@ -24,6 +24,7 @@ #include "absl/strings/internal/memutil.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { void WritePadding(std::ostream& o, size_t pad) { @@ -240,6 +241,7 @@ constexpr string_view::size_type string_view::npos; ABSL_STRING_VIEW_SELECTANY constexpr string_view::size_type string_view::kMaxSize; +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_HAVE_STD_STRING_VIEW diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index a7f9199240a..4a5e4d8a272 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -35,7 +35,9 @@ #include namespace absl { +inline namespace lts_2018_06_20 { using std::string_view; +} // inline namespace lts_2018_06_20 } // namespace absl #else // ABSL_HAVE_STD_STRING_VIEW @@ -53,6 +55,7 @@ using std::string_view; #include "absl/base/port.h" namespace absl { +inline namespace lts_2018_06_20 { // absl::string_view // @@ -540,11 +543,13 @@ inline bool operator>=(string_view x, string_view y) noexcept { // IO Insertion Operator std::ostream& operator<<(std::ostream& o, string_view piece); +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_HAVE_STD_STRING_VIEW namespace absl { +inline namespace lts_2018_06_20 { // ClippedSubstr() // @@ -565,6 +570,7 @@ inline string_view NullSafeStringView(const char* p) { return p ? string_view(p) : string_view(); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_STRING_VIEW_H_ diff --git a/absl/strings/strip.h b/absl/strings/strip.h index 2f8d21f7deb..ea64c3343c8 100644 --- a/absl/strings/strip.h +++ b/absl/strings/strip.h @@ -30,6 +30,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { // ConsumePrefix() // @@ -84,6 +85,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix( return str; } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_STRIP_H_ diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc index 3b200594509..849443b9118 100644 --- a/absl/strings/substitute.cc +++ b/absl/strings/substitute.cc @@ -23,6 +23,7 @@ #include "absl/strings/string_view.h" namespace absl { +inline namespace lts_2018_06_20 { namespace substitute_internal { void SubstituteAndAppendArray(std::string* output, absl::string_view format, @@ -167,4 +168,5 @@ Arg::Arg(Dec dec) { } } // namespace substitute_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index c4b25ba7095..def79be2eeb 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h @@ -82,6 +82,7 @@ #include "absl/strings/strip.h" namespace absl { +inline namespace lts_2018_06_20 { namespace substitute_internal { // Arg @@ -666,6 +667,7 @@ std::string Substitute( "format std::string doesn't contain all of $0 through $9"); #endif // ABSL_BAD_CALL_IF +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_STRINGS_SUBSTITUTE_H_ diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index 123536ea4d1..372874e1104 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -149,12 +149,6 @@ cc_test( size = "large", srcs = ["mutex_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_android_arm", - "no_test_android_arm64", - "no_test_android_x86", - "no_test_loonix", # Too slow. - ], deps = [ ":synchronization", ":thread_pool", diff --git a/absl/synchronization/barrier.cc b/absl/synchronization/barrier.cc index a1b3ad5c217..545bb891627 100644 --- a/absl/synchronization/barrier.cc +++ b/absl/synchronization/barrier.cc @@ -18,6 +18,7 @@ #include "absl/synchronization/mutex.h" namespace absl { +inline namespace lts_2018_06_20 { // Return whether int *arg is zero. static bool IsZero(void *arg) { @@ -47,4 +48,5 @@ bool Barrier::Block() { return this->num_to_exit_ == 0; } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/barrier.h b/absl/synchronization/barrier.h index f834feec11d..ccae0a4c0b8 100644 --- a/absl/synchronization/barrier.h +++ b/absl/synchronization/barrier.h @@ -23,6 +23,7 @@ #include "absl/synchronization/mutex.h" namespace absl { +inline namespace lts_2018_06_20 { // Barrier // @@ -73,5 +74,6 @@ class Barrier { int num_to_exit_ GUARDED_BY(lock_); }; +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_SYNCHRONIZATION_BARRIER_H_ diff --git a/absl/synchronization/blocking_counter.cc b/absl/synchronization/blocking_counter.cc index 7e68e960bc3..f998099ab5c 100644 --- a/absl/synchronization/blocking_counter.cc +++ b/absl/synchronization/blocking_counter.cc @@ -17,6 +17,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { // Return whether int *arg is zero. static bool IsZero(void *arg) { @@ -52,4 +53,5 @@ void BlockingCounter::Wait() { // after we return from this method. } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/blocking_counter.h b/absl/synchronization/blocking_counter.h index 557ed028fe5..08b6f58ce38 100644 --- a/absl/synchronization/blocking_counter.h +++ b/absl/synchronization/blocking_counter.h @@ -24,6 +24,7 @@ #include "absl/synchronization/mutex.h" namespace absl { +inline namespace lts_2018_06_20 { // BlockingCounter // @@ -92,6 +93,7 @@ class BlockingCounter { int num_waiting_ GUARDED_BY(lock_); }; +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_SYNCHRONIZATION_BLOCKING_COUNTER_H_ diff --git a/absl/synchronization/blocking_counter_test.cc b/absl/synchronization/blocking_counter_test.cc index e8223f841a5..486aa9b1cc3 100644 --- a/absl/synchronization/blocking_counter_test.cc +++ b/absl/synchronization/blocking_counter_test.cc @@ -22,6 +22,7 @@ #include "absl/time/time.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { void PauseAndDecreaseCounter(BlockingCounter* counter, int* done) { @@ -63,4 +64,5 @@ TEST(BlockingCounterTest, BasicFunctionality) { } } // namespace +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc index e7a65cd884a..38cc7e2f55f 100644 --- a/absl/synchronization/internal/create_thread_identity.cc +++ b/absl/synchronization/internal/create_thread_identity.cc @@ -27,6 +27,7 @@ #include "absl/synchronization/internal/per_thread_sem.h" namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { // ThreadIdentity storage is persistent, we maintain a free-list of previously @@ -107,6 +108,7 @@ base_internal::ThreadIdentity* CreateThreadIdentity() { } } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_LOW_LEVEL_ALLOC_MISSING diff --git a/absl/synchronization/internal/create_thread_identity.h b/absl/synchronization/internal/create_thread_identity.h index 1bb87dee630..05b5f7e1d47 100644 --- a/absl/synchronization/internal/create_thread_identity.h +++ b/absl/synchronization/internal/create_thread_identity.h @@ -29,6 +29,7 @@ #include "absl/base/port.h" namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { // Allocates and attaches a ThreadIdentity object for the calling thread. @@ -49,5 +50,6 @@ inline base_internal::ThreadIdentity* GetOrCreateCurrentThreadIdentity() { } } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_CREATE_THREAD_IDENTITY_H_ diff --git a/absl/synchronization/internal/graphcycles.cc b/absl/synchronization/internal/graphcycles.cc index ab1f3f84e74..5a0158446c7 100644 --- a/absl/synchronization/internal/graphcycles.cc +++ b/absl/synchronization/internal/graphcycles.cc @@ -44,6 +44,7 @@ // Do not use STL. This module does not use standard memory allocation. namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { namespace { @@ -694,6 +695,7 @@ int GraphCycles::GetStackTrace(GraphId id, void*** ptr) { } } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_LOW_LEVEL_ALLOC_MISSING diff --git a/absl/synchronization/internal/graphcycles.h b/absl/synchronization/internal/graphcycles.h index 2e6686a4c06..e43ae26bdef 100644 --- a/absl/synchronization/internal/graphcycles.h +++ b/absl/synchronization/internal/graphcycles.h @@ -41,6 +41,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { // Opaque identifier for a graph node. @@ -132,6 +133,7 @@ class GraphCycles { }; } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif diff --git a/absl/synchronization/internal/graphcycles_test.cc b/absl/synchronization/internal/graphcycles_test.cc index 9a85b39074d..09332bad2e2 100644 --- a/absl/synchronization/internal/graphcycles_test.cc +++ b/absl/synchronization/internal/graphcycles_test.cc @@ -25,6 +25,7 @@ #include "absl/base/macros.h" namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { // We emulate a GraphCycles object with a node vector and an edge vector. @@ -459,4 +460,5 @@ TEST_F(GraphCyclesTest, ManyEdges) { } } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h index 0d132d98f88..3d3dc0cb50e 100644 --- a/absl/synchronization/internal/kernel_timeout.h +++ b/absl/synchronization/internal/kernel_timeout.h @@ -37,6 +37,7 @@ #include "absl/time/time.h" namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { class Futex; @@ -145,5 +146,6 @@ class KernelTimeout { }; } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ diff --git a/absl/synchronization/internal/mutex_nonprod.cc b/absl/synchronization/internal/mutex_nonprod.cc index 45c60326dba..a8071a961ca 100644 --- a/absl/synchronization/internal/mutex_nonprod.cc +++ b/absl/synchronization/internal/mutex_nonprod.cc @@ -31,6 +31,7 @@ #include "absl/time/time.h" namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { namespace { @@ -315,4 +316,5 @@ bool Condition::Eval() const { void RegisterSymbolizer(bool (*)(const void*, char*, int)) {} +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/internal/mutex_nonprod.inc b/absl/synchronization/internal/mutex_nonprod.inc index 0aab3d1314e..2d06285f102 100644 --- a/absl/synchronization/internal/mutex_nonprod.inc +++ b/absl/synchronization/internal/mutex_nonprod.inc @@ -36,6 +36,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { class Condition; namespace synchronization_internal { @@ -253,4 +254,5 @@ class SynchronizationStorage { }; } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc index caa2baf64bd..53b789d68b6 100644 --- a/absl/synchronization/internal/per_thread_sem.cc +++ b/absl/synchronization/internal/per_thread_sem.cc @@ -25,6 +25,7 @@ #include "absl/synchronization/internal/waiter.h" namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { void PerThreadSem::SetThreadBlockedCounter(std::atomic *counter) { @@ -58,6 +59,7 @@ void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) { } } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl extern "C" { diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h index 678b69e487a..fc64f768f5b 100644 --- a/absl/synchronization/internal/per_thread_sem.h +++ b/absl/synchronization/internal/per_thread_sem.h @@ -32,6 +32,7 @@ #include "absl/synchronization/internal/kernel_timeout.h" namespace absl { +inline namespace lts_2018_06_20 { class Mutex; @@ -80,6 +81,7 @@ class PerThreadSem { }; } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl // In some build configurations we pass --detect-odr-violations to the diff --git a/absl/synchronization/internal/per_thread_sem_test.cc b/absl/synchronization/internal/per_thread_sem_test.cc index 2b52ea76ab0..63c8e56ae77 100644 --- a/absl/synchronization/internal/per_thread_sem_test.cc +++ b/absl/synchronization/internal/per_thread_sem_test.cc @@ -33,6 +33,7 @@ // primitives which might use PerThreadSem, most notably absl::Mutex. namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { class SimpleSemaphore { @@ -169,4 +170,5 @@ TEST_F(PerThreadSemTest, Timeouts) { } // namespace } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/internal/thread_pool.h b/absl/synchronization/internal/thread_pool.h index 846404277a0..82dedbf542c 100644 --- a/absl/synchronization/internal/thread_pool.h +++ b/absl/synchronization/internal/thread_pool.h @@ -25,6 +25,7 @@ #include "absl/synchronization/mutex.h" namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { // A simple ThreadPool implementation for tests. @@ -85,6 +86,7 @@ class ThreadPool { }; } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_THREAD_POOL_H_ diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index 768c52085cd..ad86acce9a6 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -46,6 +46,7 @@ #include "absl/synchronization/internal/kernel_timeout.h" namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { static void MaybeBecomeIdle() { @@ -409,4 +410,5 @@ void Waiter::Poke() { #endif } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index 23166f4bfa3..1c284c0afd2 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -53,6 +53,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { namespace synchronization_internal { // Waiter is an OS-specific semaphore. @@ -134,6 +135,7 @@ class Waiter { }; } // namespace synchronization_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_WAITER_H_ diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index e6525046be8..14e24a760aa 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc @@ -71,6 +71,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalMutexYield() { std::this_thread::yield(); } } // extern "C" namespace absl { +inline namespace lts_2018_06_20 { namespace { @@ -2684,4 +2685,5 @@ bool Condition::GuaranteedEqual(const Condition *a, const Condition *b) { a->arg_ == b->arg_ && a->method_ == b->method_; } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index 840b9d6b0e7..b5276037361 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -81,6 +81,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { class Condition; struct SynchWaitParams; @@ -1014,6 +1015,7 @@ enum class OnDeadlockCycle { // the manner chosen here. void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode); +} // inline namespace lts_2018_06_20 } // namespace absl // In some build configurations we pass --detect-odr-violations to the diff --git a/absl/synchronization/notification.cc b/absl/synchronization/notification.cc index ed8cc906774..a38acc5f322 100644 --- a/absl/synchronization/notification.cc +++ b/absl/synchronization/notification.cc @@ -22,6 +22,7 @@ #include "absl/time/time.h" namespace absl { +inline namespace lts_2018_06_20 { void Notification::Notify() { MutexLock l(&this->mutex_); @@ -81,4 +82,5 @@ bool Notification::WaitForNotificationWithDeadline(absl::Time deadline) const { return notified; } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/synchronization/notification.h b/absl/synchronization/notification.h index 107932f2909..99f8ee63219 100644 --- a/absl/synchronization/notification.h +++ b/absl/synchronization/notification.h @@ -56,6 +56,7 @@ #include "absl/time/time.h" namespace absl { +inline namespace lts_2018_06_20 { // ----------------------------------------------------------------------------- // Notification @@ -108,5 +109,6 @@ class Notification { std::atomic notified_yet_; // written under mutex_ }; +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_SYNCHRONIZATION_NOTIFICATION_H_ diff --git a/absl/synchronization/notification_test.cc b/absl/synchronization/notification_test.cc index 9b3b6a5a9e8..83e17e34346 100644 --- a/absl/synchronization/notification_test.cc +++ b/absl/synchronization/notification_test.cc @@ -21,6 +21,7 @@ #include "absl/synchronization/mutex.h" namespace absl { +inline namespace lts_2018_06_20 { // A thread-safe class that holds a counter. class ThreadSafeCounter { @@ -121,4 +122,5 @@ TEST(NotificationTest, SanityTest) { BasicTests(true, &local_notification2); } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index fe55fe1f9bf..e793da87d10 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -44,6 +44,7 @@ cc_library( "//absl/base", "//absl/base:core_headers", "//absl/numeric:int128", + "//absl/strings", "//absl/time/internal/cctz:civil_time", "//absl/time/internal/cctz:time_zone", ], @@ -80,9 +81,6 @@ cc_test( "time_zone_test.cc", ], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_loonix", - ], deps = [ ":test_util", ":time", diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt index 72bb4d25b5a..06272364f14 100644 --- a/absl/time/CMakeLists.txt +++ b/absl/time/CMakeLists.txt @@ -53,7 +53,7 @@ list(APPEND TIME_SRC ${TIME_PUBLIC_HEADERS} ${TIME_INTERNAL_HEADERS} ) -set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128) +set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128 absl::strings) absl_library( TARGET diff --git a/absl/time/clock.cc b/absl/time/clock.cc index 772f8525e84..3b1e87394d3 100644 --- a/absl/time/clock.cc +++ b/absl/time/clock.cc @@ -34,6 +34,7 @@ #include "absl/base/thread_annotations.h" namespace absl { +inline namespace lts_2018_06_20 { Time Now() { // TODO(bww): Get a timespec instead so we don't have to divide. int64_t n = absl::GetCurrentTimeNanos(); @@ -43,6 +44,7 @@ Time Now() { } return time_internal::FromUnixDuration(absl::Nanoseconds(n)); } +} // inline namespace lts_2018_06_20 } // namespace absl // Decide if we should use the fast GetCurrentTimeNanos() algorithm @@ -73,9 +75,11 @@ Time Now() { #if !ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS namespace absl { +inline namespace lts_2018_06_20 { int64_t GetCurrentTimeNanos() { return GET_CURRENT_TIME_NANOS_FROM_SYSTEM(); } +} // inline namespace lts_2018_06_20 } // namespace absl #else // Use the cyclecounter-based implementation below. @@ -93,6 +97,7 @@ static int64_t stats_slow_paths; static int64_t stats_fast_slow_paths; namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { // This is a friend wrapper around UnscaledCycleClock::Now() // (needed to access UnscaledCycleClock). @@ -517,10 +522,12 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, return estimated_base_ns; } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS namespace absl { +inline namespace lts_2018_06_20 { namespace { // Returns the maximum duration that SleepOnce() can sleep for. @@ -548,6 +555,7 @@ void SleepOnce(absl::Duration to_sleep) { } } // namespace +} // inline namespace lts_2018_06_20 } // namespace absl extern "C" { diff --git a/absl/time/clock.h b/absl/time/clock.h index 3753d4ee43a..15f587c591a 100644 --- a/absl/time/clock.h +++ b/absl/time/clock.h @@ -26,6 +26,7 @@ #include "absl/time/time.h" namespace absl { +inline namespace lts_2018_06_20 { // Now() // @@ -49,6 +50,7 @@ int64_t GetCurrentTimeNanos(); // * Returns immediately when passed a nonpositive duration. void SleepFor(absl::Duration duration); +} // inline namespace lts_2018_06_20 } // namespace absl // ----------------------------------------------------------------------------- diff --git a/absl/time/duration.cc b/absl/time/duration.cc index 82b4d9892c7..2271f7da09e 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc @@ -67,6 +67,7 @@ #include "absl/time/time.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { @@ -896,8 +897,7 @@ bool ParseDuration(const std::string& dur_string, Duration* d) { return true; } -// TODO(absl-team): Remove once dependencies are removed. -bool ParseFlag(const std::string& text, Duration* dst, std::string* /* err */) { +bool ParseFlag(const std::string& text, Duration* dst, std::string* ) { return ParseDuration(text, dst); } @@ -905,4 +905,5 @@ std::string UnparseFlag(Duration d) { return FormatDuration(d); } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/format.cc b/absl/time/format.cc index 5dc01bda155..6bf3c20f097 100644 --- a/absl/time/format.cc +++ b/absl/time/format.cc @@ -22,6 +22,7 @@ namespace cctz = absl::time_internal::cctz; namespace absl { +inline namespace lts_2018_06_20 { extern const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez"; extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; @@ -129,7 +130,6 @@ bool ParseTime(const std::string& format, const std::string& input, absl::TimeZo return b; } -// TODO(absl-team): Remove once dependencies are removed. // Functions required to support absl::Time flags. bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) { return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error); @@ -139,4 +139,5 @@ std::string UnparseFlag(absl::Time t) { return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone()); } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/include/cctz/civil_time.h b/absl/time/internal/cctz/include/cctz/civil_time.h index 898222b4c7a..31a28cb75ad 100644 --- a/absl/time/internal/cctz/include/cctz/civil_time.h +++ b/absl/time/internal/cctz/include/cctz/civil_time.h @@ -18,6 +18,7 @@ #include "absl/time/internal/cctz/include/cctz/civil_time_detail.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -324,6 +325,7 @@ using detail::get_yearday; } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_ diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h index d52eddcdba0..78d1db1cf5e 100644 --- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h +++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h @@ -32,6 +32,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -555,6 +556,7 @@ std::ostream& operator<<(std::ostream& os, weekday wd); } // namespace detail } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #undef CONSTEXPR_M diff --git a/absl/time/internal/cctz/include/cctz/time_zone.h b/absl/time/internal/cctz/include/cctz/time_zone.h index 31abc2c4bbd..52d69384a39 100644 --- a/absl/time/internal/cctz/include/cctz/time_zone.h +++ b/absl/time/internal/cctz/include/cctz/time_zone.h @@ -28,6 +28,7 @@ #include "absl/time/internal/cctz/include/cctz/civil_time.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -311,6 +312,7 @@ inline bool parse(const std::string& fmt, const std::string& input, } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_ diff --git a/absl/time/internal/cctz/include/cctz/zone_info_source.h b/absl/time/internal/cctz/include/cctz/zone_info_source.h index 4d9d8f875ed..b72a02cff8a 100644 --- a/absl/time/internal/cctz/include/cctz/zone_info_source.h +++ b/absl/time/internal/cctz/include/cctz/zone_info_source.h @@ -21,6 +21,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -35,9 +36,11 @@ class ZoneInfoSource { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz_extension { @@ -86,6 +89,7 @@ extern ZoneInfoSourceFactory zone_info_source_factory; } // namespace cctz_extension } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_ diff --git a/absl/time/internal/cctz/src/civil_time_detail.cc b/absl/time/internal/cctz/src/civil_time_detail.cc index 780d5c96e8b..92a2e09c0e1 100644 --- a/absl/time/internal/cctz/src/civil_time_detail.cc +++ b/absl/time/internal/cctz/src/civil_time_detail.cc @@ -19,6 +19,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { namespace detail { @@ -87,4 +88,5 @@ std::ostream& operator<<(std::ostream& os, weekday wd) { } // namespace detail } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/civil_time_test.cc b/absl/time/internal/cctz/src/civil_time_test.cc index f6648c8f1f2..d26c6498f8b 100644 --- a/absl/time/internal/cctz/src/civil_time_test.cc +++ b/absl/time/internal/cctz/src/civil_time_test.cc @@ -23,6 +23,7 @@ #include "gtest/gtest.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -1046,4 +1047,5 @@ TEST(CivilTime, FirstThursdayInMonth) { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_fixed.cc b/absl/time/internal/cctz/src/time_zone_fixed.cc index 65eba3569d9..4b608f68db1 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.cc +++ b/absl/time/internal/cctz/src/time_zone_fixed.cc @@ -21,6 +21,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -120,4 +121,5 @@ std::string FixedOffsetToAbbr(const sys_seconds& offset) { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_fixed.h b/absl/time/internal/cctz/src/time_zone_fixed.h index 7c9d11db9c5..15e9db1ec55 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.h +++ b/absl/time/internal/cctz/src/time_zone_fixed.h @@ -20,6 +20,7 @@ #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -44,6 +45,7 @@ std::string FixedOffsetToAbbr(const sys_seconds& offset); } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_ diff --git a/absl/time/internal/cctz/src/time_zone_format.cc b/absl/time/internal/cctz/src/time_zone_format.cc index 6d5ccba1ce4..0efbbc79561 100644 --- a/absl/time/internal/cctz/src/time_zone_format.cc +++ b/absl/time/internal/cctz/src/time_zone_format.cc @@ -38,6 +38,7 @@ #include "time_zone_if.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { namespace detail { @@ -845,4 +846,5 @@ bool parse(const std::string& format, const std::string& input, } // namespace detail } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc index 7d5b02ad3a6..649e9b9a5e9 100644 --- a/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -34,6 +34,7 @@ using std::chrono::hours; using testing::HasSubstr; namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -1409,4 +1410,5 @@ TEST(FormatParse, RoundTripDistantPast) { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_if.cc b/absl/time/internal/cctz/src/time_zone_if.cc index 380834a172a..d289a5c9102 100644 --- a/absl/time/internal/cctz/src/time_zone_if.cc +++ b/absl/time/internal/cctz/src/time_zone_if.cc @@ -17,6 +17,7 @@ #include "time_zone_libc.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -38,4 +39,5 @@ TimeZoneIf::~TimeZoneIf() {} } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_if.h b/absl/time/internal/cctz/src/time_zone_if.h index ce4da1b728b..2d5f885dbb6 100644 --- a/absl/time/internal/cctz/src/time_zone_if.h +++ b/absl/time/internal/cctz/src/time_zone_if.h @@ -24,6 +24,7 @@ #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -65,6 +66,7 @@ inline time_point FromUnixSeconds(std::int_fast64_t t) { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_ diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc index b3f635f786f..3ba40ac2f14 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.cc +++ b/absl/time/internal/cctz/src/time_zone_impl.cc @@ -22,6 +22,7 @@ #include "time_zone_fixed.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -114,4 +115,5 @@ const time_zone::Impl* time_zone::Impl::UTCImpl() { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_impl.h b/absl/time/internal/cctz/src/time_zone_impl.h index 2c1c30b690e..a955e40e14a 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.h +++ b/absl/time/internal/cctz/src/time_zone_impl.h @@ -24,6 +24,7 @@ #include "time_zone_info.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -92,6 +93,7 @@ class time_zone::Impl { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_ diff --git a/absl/time/internal/cctz/src/time_zone_info.cc b/absl/time/internal/cctz/src/time_zone_info.cc index 20bba28b363..7f5a8fad51f 100644 --- a/absl/time/internal/cctz/src/time_zone_info.cc +++ b/absl/time/internal/cctz/src/time_zone_info.cc @@ -50,6 +50,7 @@ #include "time_zone_posix.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -953,4 +954,5 @@ bool TimeZoneInfo::PrevTransition(time_point* tp) const { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_info.h b/absl/time/internal/cctz/src/time_zone_info.h index b4d1696bf61..5abf2811f66 100644 --- a/absl/time/internal/cctz/src/time_zone_info.h +++ b/absl/time/internal/cctz/src/time_zone_info.h @@ -28,6 +28,7 @@ #include "tzfile.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -127,6 +128,7 @@ class TimeZoneInfo : public TimeZoneIf { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_ diff --git a/absl/time/internal/cctz/src/time_zone_libc.cc b/absl/time/internal/cctz/src/time_zone_libc.cc index b0b56a52233..32a7a92779b 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.cc +++ b/absl/time/internal/cctz/src/time_zone_libc.cc @@ -27,6 +27,7 @@ #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -153,4 +154,5 @@ bool TimeZoneLibC::PrevTransition(time_point* tp) const { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_libc.h b/absl/time/internal/cctz/src/time_zone_libc.h index 41f7dde2b06..0856b200db1 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.h +++ b/absl/time/internal/cctz/src/time_zone_libc.h @@ -20,6 +20,7 @@ #include "time_zone_if.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -45,6 +46,7 @@ class TimeZoneLibC : public TimeZoneIf { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_ diff --git a/absl/time/internal/cctz/src/time_zone_lookup.cc b/absl/time/internal/cctz/src/time_zone_lookup.cc index d549d862a76..18d1565f216 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup.cc @@ -28,6 +28,7 @@ #include "time_zone_impl.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -142,4 +143,5 @@ time_zone local_time_zone() { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 06b172a8032..a1b6c6871c8 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -34,6 +34,7 @@ using std::chrono::minutes; using std::chrono::hours; namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -1258,4 +1259,5 @@ TEST(TimeZoneEdgeCase, UTC5DigitYear) { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_posix.cc b/absl/time/internal/cctz/src/time_zone_posix.cc index 75ad8bcba93..b6cf287557c 100644 --- a/absl/time/internal/cctz/src/time_zone_posix.cc +++ b/absl/time/internal/cctz/src/time_zone_posix.cc @@ -20,6 +20,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -152,4 +153,5 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) { } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/cctz/src/time_zone_posix.h b/absl/time/internal/cctz/src/time_zone_posix.h index 6619f27edcf..91443a21fda 100644 --- a/absl/time/internal/cctz/src/time_zone_posix.h +++ b/absl/time/internal/cctz/src/time_zone_posix.h @@ -56,6 +56,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -113,6 +114,7 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res); } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_ diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc index b77c0a585a1..a73e1c929b6 100644 --- a/absl/time/internal/cctz/src/zone_info_source.cc +++ b/absl/time/internal/cctz/src/zone_info_source.cc @@ -15,6 +15,7 @@ #include "absl/time/internal/cctz/include/cctz/zone_info_source.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz { @@ -23,9 +24,11 @@ ZoneInfoSource::~ZoneInfoSource() {} } // namespace cctz } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz_extension { @@ -52,11 +55,11 @@ ZoneInfoSourceFactory default_factory = DefaultFactory; #if defined(_M_IX86) #pragma comment( \ linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZA=?default_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZA") + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA=?default_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA") #elif defined(_M_IA_64) || defined(_M_AMD64) #pragma comment( \ linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZEA=?default_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZEA") + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA=?default_factory@cctz_extension@time_internal@lts_2018_06_20@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_06_20@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA") #else #error Unsupported MSVC platform #endif @@ -67,4 +70,5 @@ ZoneInfoSourceFactory zone_info_source_factory } // namespace cctz_extension } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/get_current_time_ios.inc b/absl/time/internal/get_current_time_ios.inc index f3db32bf785..abd43cb4756 100644 --- a/absl/time/internal/get_current_time_ios.inc +++ b/absl/time/internal/get_current_time_ios.inc @@ -26,6 +26,7 @@ #endif namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { static int64_t GetCurrentTimeNanosFromSystem() { @@ -77,4 +78,5 @@ static int64_t GetCurrentTimeNanosFromSystem() { } } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/get_current_time_posix.inc b/absl/time/internal/get_current_time_posix.inc index 65474ca6da1..dfbcf8a6e0f 100644 --- a/absl/time/internal/get_current_time_posix.inc +++ b/absl/time/internal/get_current_time_posix.inc @@ -7,6 +7,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { static int64_t GetCurrentTimeNanosFromSystem() { @@ -19,4 +20,5 @@ static int64_t GetCurrentTimeNanosFromSystem() { } } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/get_current_time_windows.inc b/absl/time/internal/get_current_time_windows.inc index b22a9c9e9e2..273d2d80e70 100644 --- a/absl/time/internal/get_current_time_windows.inc +++ b/absl/time/internal/get_current_time_windows.inc @@ -4,6 +4,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { static int64_t GetCurrentTimeNanosFromSystem() { @@ -14,4 +15,5 @@ static int64_t GetCurrentTimeNanosFromSystem() { } } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc index bbbef7da70c..1cc89b5e401 100644 --- a/absl/time/internal/test_util.cc +++ b/absl/time/internal/test_util.cc @@ -24,6 +24,7 @@ namespace cctz = absl::time_internal::cctz; namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { #if GTEST_USES_SIMPLE_RE @@ -39,9 +40,11 @@ TimeZone LoadTimeZone(const std::string& name) { } } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { namespace cctz_extension { namespace { @@ -126,4 +129,5 @@ ZoneInfoSourceFactory zone_info_source_factory = TestFactory; } // namespace cctz_extension } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/internal/test_util.h b/absl/time/internal/test_util.h index 8fd5fb9fd03..fb65f9bd8e7 100644 --- a/absl/time/internal/test_util.h +++ b/absl/time/internal/test_util.h @@ -41,6 +41,7 @@ } while (0) namespace absl { +inline namespace lts_2018_06_20 { namespace time_internal { // A regular expression that matches all zone abbreviations (%Z). @@ -50,6 +51,7 @@ extern const char kZoneAbbrRE[]; absl::TimeZone LoadTimeZone(const std::string& name); } // namespace time_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_INTERNAL_TEST_UTIL_H_ diff --git a/absl/time/time.cc b/absl/time/time.cc index 03720f62b00..76ce12c99f9 100644 --- a/absl/time/time.cc +++ b/absl/time/time.cc @@ -41,6 +41,7 @@ namespace cctz = absl::time_internal::cctz; namespace absl { +inline namespace lts_2018_06_20 { namespace { @@ -382,4 +383,5 @@ std::chrono::system_clock::time_point ToChronoTime(absl::Time t) { time_internal::ToChronoDuration(d); } +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/time/time.h b/absl/time/time.h index 99c12bbd87e..3b5739ff2e3 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -64,9 +64,11 @@ #include #include "absl/base/port.h" // Needed for string vs std::string +#include "absl/strings/string_view.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { +inline namespace lts_2018_06_20 { class Duration; // Defined below class Time; // Defined below @@ -491,9 +493,6 @@ inline std::ostream& operator<<(std::ostream& os, Duration d) { // `ZeroDuration()`. Parses "inf" and "-inf" as +/- `InfiniteDuration()`. bool ParseDuration(const std::string& dur_string, Duration* d); -// Flag Support -// TODO(absl-team): Remove once dependencies are removed. - // ParseFlag() // bool ParseFlag(const std::string& text, Duration* dst, std::string* error); @@ -993,8 +992,6 @@ bool ParseTime(const std::string& format, const std::string& input, Time* time, bool ParseTime(const std::string& format, const std::string& input, TimeZone tz, Time* time, std::string* err); -// TODO(absl-team): Remove once dependencies are removed. - // ParseFlag() // UnparseFlag() // @@ -1335,6 +1332,7 @@ constexpr Time FromTimeT(time_t t) { return time_internal::FromUnixDuration(Seconds(t)); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TIME_TIME_H_ diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 1fc4c098e9b..c50ec425aea 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel @@ -247,6 +247,19 @@ cc_test( ], ) +cc_test( + name = "variant_benchmark", + srcs = [ + "variant_benchmark.cc", + ], + copts = ABSL_TEST_COPTS, + deps = [ + ":variant", + "//absl/utility", + "@com_github_google_benchmark//:benchmark_main", + ], +) + cc_test( name = "variant_exception_safety_test", size = "small", diff --git a/absl/types/any.h b/absl/types/any.h index a973c6dab7e..9d297dc8ec1 100644 --- a/absl/types/any.h +++ b/absl/types/any.h @@ -61,10 +61,12 @@ #include namespace absl { +inline namespace lts_2018_06_20 { using std::any; using std::any_cast; using std::bad_any_cast; using std::make_any; +} // inline namespace lts_2018_06_20 } // namespace absl #else // ABSL_HAVE_STD_ANY @@ -91,6 +93,7 @@ using std::make_any; #endif // !defined(__GNUC__) || defined(__GXX_RTTI) namespace absl { +inline namespace lts_2018_06_20 { namespace any_internal { @@ -530,6 +533,7 @@ T* any_cast(any* operand) noexcept { : nullptr; } +} // inline namespace lts_2018_06_20 } // namespace absl #undef ABSL_ANY_DETAIL_HAS_RTTI diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc index c9b73300690..537767cdbde 100644 --- a/absl/types/bad_any_cast.cc +++ b/absl/types/bad_any_cast.cc @@ -20,6 +20,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { bad_any_cast::~bad_any_cast() = default; @@ -37,4 +38,5 @@ void ThrowBadAnyCast() { } } // namespace any_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/types/bad_any_cast.h b/absl/types/bad_any_cast.h index 3b963077d41..3f89e75d742 100644 --- a/absl/types/bad_any_cast.h +++ b/absl/types/bad_any_cast.h @@ -24,6 +24,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { // ----------------------------------------------------------------------------- // bad_any_cast @@ -52,6 +53,7 @@ namespace any_internal { [[noreturn]] void ThrowBadAnyCast(); } // namespace any_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TYPES_BAD_ANY_CAST_H_ diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc index 6bc67df77c9..c5508678542 100644 --- a/absl/types/bad_optional_access.cc +++ b/absl/types/bad_optional_access.cc @@ -20,6 +20,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { bad_optional_access::~bad_optional_access() = default; @@ -39,4 +40,5 @@ void throw_bad_optional_access() { } } // namespace optional_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h index e9aa8b83eda..d468211ae10 100644 --- a/absl/types/bad_optional_access.h +++ b/absl/types/bad_optional_access.h @@ -24,6 +24,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { // ----------------------------------------------------------------------------- // bad_optional_access @@ -55,6 +56,7 @@ namespace optional_internal { [[noreturn]] void throw_bad_optional_access(); } // namespace optional_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_ diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc index 817fd78909a..a796cfaa16a 100644 --- a/absl/types/bad_variant_access.cc +++ b/absl/types/bad_variant_access.cc @@ -21,6 +21,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { +inline namespace lts_2018_06_20 { ////////////////////////// // [variant.bad.access] // @@ -55,4 +56,5 @@ void Rethrow() { } } // namespace variant_internal +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h index 67abe7136b7..2e00731572a 100644 --- a/absl/types/bad_variant_access.h +++ b/absl/types/bad_variant_access.h @@ -24,6 +24,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { // ----------------------------------------------------------------------------- // bad_variant_access @@ -59,6 +60,7 @@ namespace variant_internal { [[noreturn]] void Rethrow(); } // namespace variant_internal +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TYPES_BAD_VARIANT_ACCESS_H_ diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h index 61c56ddf4e8..3b884647eec 100644 --- a/absl/types/internal/variant.h +++ b/absl/types/internal/variant.h @@ -19,21 +19,26 @@ #ifndef ABSL_TYPES_variant_internal_H_ #define ABSL_TYPES_variant_internal_H_ +#include #include +#include #include #include #include #include +#include "absl/base/config.h" #include "absl/base/internal/identity.h" #include "absl/base/internal/inline_variable.h" #include "absl/base/internal/invoke.h" +#include "absl/base/macros.h" #include "absl/base/optimization.h" #include "absl/meta/type_traits.h" #include "absl/types/bad_variant_access.h" #include "absl/utility/utility.h" namespace absl { +inline namespace lts_2018_06_20 { template class variant; @@ -119,6 +124,8 @@ using GiveQualsToT = typename GiveQualsTo::type; template using SizeT = std::integral_constant; +using NPos = SizeT; + template struct IndexOfConstructedType {}; @@ -248,19 +255,270 @@ struct MakeVisitationMatrix, absl::make_index_sequence, BoundIndices...> {}; -template -VisitIndicesResultT visit_indices(Op&& op, SizeT... indices) { - return AccessSimpleArray( - MakeVisitationMatrix, Op, - index_sequence<(EndIndices + 1)...>>::Run(), - (indices + 1)...)(absl::forward(op)); -} +struct UnreachableSwitchCase { + template + [[noreturn]] static VisitIndicesResultT Run( + Op&& /*ignored*/) { +#if ABSL_HAVE_BUILTIN(__builtin_unreachable) || \ + (defined(__GNUC__) && !defined(__clang__)) + __builtin_unreachable(); +#elif defined(_MSC_VER) + __assume(false); +#else + // Try to use assert of false being identified as an unreachable intrinsic. + // NOTE: We use assert directly to increase chances of exploiting an assume + // intrinsic. + assert(false); // NOLINT + + // Hack to silence potential no return warning -- cause an infinite loop. + return Run(absl::forward(op)); +#endif // Checks for __builtin_unreachable + } +}; + +template +struct ReachableSwitchCase { + static VisitIndicesResultT Run(Op&& op) { + return absl::base_internal::Invoke(absl::forward(op), SizeT()); + } +}; + +// The number 33 is just a guess at a reasonable maximum to our switch. It is +// not based on any analysis. The reason it is a power of 2 plus 1 instead of a +// power of 2 is because the number was picked to correspond to a power of 2 +// amount of "normal" alternatives, plus one for the possibility of the user +// providing "monostate" in addition to the more natural alternatives. +ABSL_INTERNAL_INLINE_CONSTEXPR(std::size_t, MaxUnrolledVisitCases, 33); + +// Note: The default-definition is for unreachable cases. +template +struct PickCaseImpl { + template + using Apply = UnreachableSwitchCase; +}; + +template <> +struct PickCaseImpl { + template + using Apply = ReachableSwitchCase; +}; + +// Note: This form of dance with template aliases is to make sure that we +// instantiate a number of templates proportional to the number of variant +// alternatives rather than a number of templates proportional to our +// maximum unrolled amount of visitation cases (aliases are effectively +// "free" whereas other template instantiations are costly). +template +using PickCase = typename PickCaseImpl<(I < EndIndex)>::template Apply; template [[noreturn]] ReturnType TypedThrowBadVariantAccess() { absl::variant_internal::ThrowBadVariantAccess(); } +// Given N variant sizes, determine the number of cases there would need to be +// in a single switch-statement that would cover every possibility in the +// corresponding N-ary visit operation. +template +struct NumCasesOfSwitch; + +template +struct NumCasesOfSwitch { + static constexpr std::size_t value = + (HeadNumAlternatives + 1) * + NumCasesOfSwitch::value; +}; + +template <> +struct NumCasesOfSwitch<> { + static constexpr std::size_t value = 1; +}; + +// A switch statement optimizes better than the table of function pointers. +template +struct VisitIndicesSwitch { + static_assert(EndIndex <= MaxUnrolledVisitCases, + "Maximum unrolled switch size exceeded."); + + template + static VisitIndicesResultT Run(Op&& op, std::size_t i) { + switch (i) { + case 0: + return PickCase::Run(absl::forward(op)); + case 1: + return PickCase::Run(absl::forward(op)); + case 2: + return PickCase::Run(absl::forward(op)); + case 3: + return PickCase::Run(absl::forward(op)); + case 4: + return PickCase::Run(absl::forward(op)); + case 5: + return PickCase::Run(absl::forward(op)); + case 6: + return PickCase::Run(absl::forward(op)); + case 7: + return PickCase::Run(absl::forward(op)); + case 8: + return PickCase::Run(absl::forward(op)); + case 9: + return PickCase::Run(absl::forward(op)); + case 10: + return PickCase::Run(absl::forward(op)); + case 11: + return PickCase::Run(absl::forward(op)); + case 12: + return PickCase::Run(absl::forward(op)); + case 13: + return PickCase::Run(absl::forward(op)); + case 14: + return PickCase::Run(absl::forward(op)); + case 15: + return PickCase::Run(absl::forward(op)); + case 16: + return PickCase::Run(absl::forward(op)); + case 17: + return PickCase::Run(absl::forward(op)); + case 18: + return PickCase::Run(absl::forward(op)); + case 19: + return PickCase::Run(absl::forward(op)); + case 20: + return PickCase::Run(absl::forward(op)); + case 21: + return PickCase::Run(absl::forward(op)); + case 22: + return PickCase::Run(absl::forward(op)); + case 23: + return PickCase::Run(absl::forward(op)); + case 24: + return PickCase::Run(absl::forward(op)); + case 25: + return PickCase::Run(absl::forward(op)); + case 26: + return PickCase::Run(absl::forward(op)); + case 27: + return PickCase::Run(absl::forward(op)); + case 28: + return PickCase::Run(absl::forward(op)); + case 29: + return PickCase::Run(absl::forward(op)); + case 30: + return PickCase::Run(absl::forward(op)); + case 31: + return PickCase::Run(absl::forward(op)); + case 32: + return PickCase::Run(absl::forward(op)); + default: + ABSL_ASSERT(i == variant_npos); + return absl::base_internal::Invoke(absl::forward(op), NPos()); + } + } +}; + +template +struct VisitIndicesFallback { + template + static VisitIndicesResultT Run(Op&& op, SizeT... indices) { + return AccessSimpleArray( + MakeVisitationMatrix, Op, + index_sequence<(EndIndices + 1)...>>::Run(), + (indices + 1)...)(absl::forward(op)); + } +}; + +// Take an N-dimensional series of indices and convert them into a single index +// without loss of information. The purpose of this is to be able to convert an +// N-ary visit operation into a single switch statement. +template +struct FlattenIndices; + +template +struct FlattenIndices { + template + static constexpr std::size_t Run(std::size_t head, SizeType... tail) { + return head + HeadSize * FlattenIndices::Run(tail...); + } +}; + +template <> +struct FlattenIndices<> { + static constexpr std::size_t Run() { return 0; } +}; + +// Take a single "flattened" index (flattened by FlattenIndices) and determine +// the value of the index of one of the logically represented dimensions. +template +struct UnflattenIndex { + static constexpr std::size_t value = + UnflattenIndex::value; +}; + +template +struct UnflattenIndex { + static constexpr std::size_t value = (I % HeadSize); +}; + +// The backend for converting an N-ary visit operation into a unary visit. +template +struct VisitIndicesVariadicImpl; + +template +struct VisitIndicesVariadicImpl, EndIndices...> { + // A type that can take an N-ary function object and converts it to a unary + // function object that takes a single, flattened index, and "unflattens" it + // into its individual dimensions when forwarding to the wrapped object. + template + struct FlattenedOp { + template + VisitIndicesResultT operator()( + SizeT /*index*/) && { + return base_internal::Invoke( + absl::forward(op), + SizeT::value - + std::size_t{1}>()...); + } + + Op&& op; + }; + + template + static VisitIndicesResultT Run( + Op&& op, SizeType... i) { + return VisitIndicesSwitch::value>::Run( + FlattenedOp{absl::forward(op)}, + FlattenIndices<(EndIndices + std::size_t{1})...>::Run( + (i + std::size_t{1})...)); + } +}; + +template +struct VisitIndicesVariadic + : VisitIndicesVariadicImpl, + EndIndices...> {}; + +// This implementation will flatten N-ary visit operations into a single switch +// statement when the number of cases would be less than our maximum specified +// switch-statement size. +// TODO(calabrese) +// Based on benchmarks, determine whether the function table approach actually +// does optimize better than a chain of switch statements and possibly update +// the implementation accordingly. Also consider increasing the maximum switch +// size. +template +struct VisitIndices + : absl::conditional_t<(NumCasesOfSwitch::value <= + MaxUnrolledVisitCases), + VisitIndicesVariadic, + VisitIndicesFallback> {}; + +template +struct VisitIndices + : absl::conditional_t<(EndIndex <= MaxUnrolledVisitCases), + VisitIndicesSwitch, + VisitIndicesFallback> {}; + // Suppress bogus warning on MSVC: MSVC complains that the `reinterpret_cast` // below is returning the address of a temporary or local object. #ifdef _MSC_VER @@ -270,8 +528,10 @@ template // TODO(calabrese) std::launder // TODO(calabrese) constexpr +// NOTE: DO NOT REMOVE the `inline` keyword as it is necessary to work around a +// MSVC bug. See https://github.com/abseil/abseil-cpp/issues/129 for details. template -VariantAccessResult AccessUnion(Self&& self, SizeT /*i*/) { +inline VariantAccessResult AccessUnion(Self&& self, SizeT /*i*/) { return reinterpret_cast>(self); } @@ -313,7 +573,7 @@ struct VariantCoreAccess { template static void InitFrom(Variant& self, Variant&& other) { // NOLINT - variant_internal::visit_indices::value>( + VisitIndices::value>::Run( InitFromVisitor{&self, std::forward(other)}, other.index()); @@ -841,49 +1101,40 @@ using EqualResult = decltype(std::declval() == std::declval()); template using NotEqualResult = decltype(std::declval() != std::declval()); -template -using HasLessThan = is_detected_convertible; - -template -using HasGreaterThan = is_detected_convertible; - -template -using HasLessThanOrEqual = - is_detected_convertible; - -template -using HasGreaterThanOrEqual = - is_detected_convertible; - -template -using HasEqual = is_detected_convertible; - -template -using HasNotEqual = is_detected_convertible; - template -using RequireAllHaveEqualT = - absl::enable_if_t...>::value, bool>; +using RequireAllHaveEqualT = absl::enable_if_t< + absl::conjunction...>::value, + bool>; template using RequireAllHaveNotEqualT = - absl::enable_if_t...>::value, bool>; + absl::enable_if_t...>::value, + bool>; template using RequireAllHaveLessThanT = - absl::enable_if_t...>::value, bool>; + absl::enable_if_t...>::value, + bool>; template using RequireAllHaveLessThanOrEqualT = - absl::enable_if_t...>::value, bool>; + absl::enable_if_t...>::value, + bool>; template using RequireAllHaveGreaterThanOrEqualT = - absl::enable_if_t...>::value, bool>; + absl::enable_if_t...>::value, + bool>; template using RequireAllHaveGreaterThanT = - absl::enable_if_t...>::value, bool>; + absl::enable_if_t...>::value, + bool>; // Helper template containing implementations details of variant that can't go // in the private section. For convenience, this takes the variant type as a @@ -1049,9 +1300,7 @@ class VariantStateBaseDestructorNontrivial : protected VariantStateBase { VariantStateBaseDestructorNontrivial* self; }; - void destroy() { - variant_internal::visit_indices(Destroyer{this}, index_); - } + void destroy() { VisitIndices::Run(Destroyer{this}, index_); } ~VariantStateBaseDestructorNontrivial() { destroy(); } @@ -1087,8 +1336,7 @@ class VariantMoveBaseNontrivial : protected VariantStateBaseDestructor { VariantMoveBaseNontrivial(VariantMoveBaseNontrivial&& other) noexcept( absl::conjunction...>::value) : Base(NoopConstructorTag()) { - variant_internal::visit_indices(Construct{this, &other}, - other.index_); + VisitIndices::Run(Construct{this, &other}, other.index_); index_ = other.index_; } @@ -1131,8 +1379,7 @@ class VariantCopyBaseNontrivial : protected VariantMoveBase { VariantCopyBaseNontrivial(VariantCopyBaseNontrivial const& other) : Base(NoopConstructorTag()) { - variant_internal::visit_indices(Construct{this, &other}, - other.index_); + VisitIndices::Run(Construct{this, &other}, other.index_); index_ = other.index_; } @@ -1166,7 +1413,7 @@ class VariantMoveAssignBaseNontrivial : protected VariantCopyBase { operator=(VariantMoveAssignBaseNontrivial&& other) noexcept( absl::conjunction..., std::is_nothrow_move_assignable...>::value) { - variant_internal::visit_indices( + VisitIndices::Run( VariantCoreAccess::MakeMoveAssignVisitor(this, &other), other.index_); return *this; } @@ -1195,7 +1442,7 @@ class VariantCopyAssignBaseNontrivial : protected VariantMoveAssignBase { VariantCopyAssignBaseNontrivial& operator=( const VariantCopyAssignBaseNontrivial& other) { - variant_internal::visit_indices( + VisitIndices::Run( VariantCoreAccess::MakeCopyAssignVisitor(this, other), other.index_); return *this; } @@ -1336,7 +1583,7 @@ struct Swap { template void operator()(SizeT /*w_i*/) { if (v->index() == Wi) { - visit_indices(SwapSameIndex{v, w}, Wi); + VisitIndices::Run(SwapSameIndex{v, w}, Wi); } else { generic_swap(); } @@ -1370,11 +1617,10 @@ struct VariantHashBase::value>( - PerformVisitation{ - std::forward_as_tuple(var), VariantHashVisitor{}}, - var.index()); + size_t result = VisitIndices::value>::Run( + PerformVisitation{ + std::forward_as_tuple(var), VariantHashVisitor{}}, + var.index()); // Combine the index and the hash result in order to distinguish // std::variant holding the same value as different alternative. return result ^ var.index(); @@ -1382,6 +1628,7 @@ struct VariantHashBase namespace absl { +inline namespace lts_2018_06_20 { using std::bad_optional_access; using std::optional; using std::make_optional; using std::nullopt_t; using std::nullopt; -} +} // inline namespace lts_2018_06_20 +} // namespace absl #else // ABSL_HAVE_STD_OPTIONAL @@ -92,6 +94,7 @@ using std::nullopt; #endif namespace absl { +inline namespace lts_2018_06_20 { // ----------------------------------------------------------------------------- // absl::optional @@ -1122,6 +1125,7 @@ constexpr auto operator>=(const U& v, const optional& x) return static_cast(x) ? static_cast(v >= *x) : true; } +} // inline namespace lts_2018_06_20 } // namespace absl namespace std { diff --git a/absl/types/optional_exception_safety_test.cc b/absl/types/optional_exception_safety_test.cc index d2ef04b8d1b..e136d0a092a 100644 --- a/absl/types/optional_exception_safety_test.cc +++ b/absl/types/optional_exception_safety_test.cc @@ -18,6 +18,7 @@ #include "absl/base/internal/exception_safety_testing.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { @@ -279,4 +280,5 @@ TEST(OptionalExceptionSafety, NothrowMoveAssign) { } // namespace +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/types/span.h b/absl/types/span.h index 76be819ecca..1b5edf252c3 100644 --- a/absl/types/span.h +++ b/absl/types/span.h @@ -72,6 +72,7 @@ #include "absl/meta/type_traits.h" namespace absl { +inline namespace lts_2018_06_20 { template class Span; @@ -745,5 +746,6 @@ template constexpr Span MakeConstSpan(const T (&array)[N]) noexcept { return Span(array, N); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TYPES_SPAN_H_ diff --git a/absl/types/variant.h b/absl/types/variant.h index 7ae65abe0e6..b0898d984e2 100644 --- a/absl/types/variant.h +++ b/absl/types/variant.h @@ -50,6 +50,7 @@ #include namespace absl { +inline namespace lts_2018_06_20 { using std::bad_variant_access; using std::get; using std::get_if; @@ -62,6 +63,7 @@ using std::variant_npos; using std::variant_size; using std::variant_size_v; using std::visit; +} // inline namespace lts_2018_06_20 } // namespace absl #else // ABSL_HAVE_STD_VARIANT @@ -77,6 +79,7 @@ using std::visit; #include "absl/types/internal/variant.h" namespace absl { +inline namespace lts_2018_06_20 { // ----------------------------------------------------------------------------- // absl::variant @@ -416,12 +419,12 @@ constexpr absl::add_pointer_t get_if( template variant_internal::VisitResult visit(Visitor&& vis, Variants&&... vars) { - return variant_internal::visit_indices< - variant_size>::value...>( - variant_internal::PerformVisitation{ - std::forward_as_tuple(absl::forward(vars)...), - absl::forward(vis)}, - vars.index()...); + return variant_internal:: + VisitIndices >::value...>::Run( + variant_internal::PerformVisitation{ + std::forward_as_tuple(absl::forward(vars)...), + absl::forward(vis)}, + vars.index()...); } // monostate @@ -445,9 +448,11 @@ constexpr bool operator!=(monostate, monostate) noexcept { return false; } //------------------------------------------------------------------------------ template class variant : private variant_internal::VariantBase { + // Intentionally not qualifing `negation` with `absl::` to work around a bug + // in MSVC 2015 with inline namespace and variadic template. static_assert(absl::conjunction, std::is_object..., - absl::negation>, - absl::negation>..., + negation >, + negation >..., std::is_nothrow_destructible, std::is_nothrow_destructible...>::value, "Attempted to instantiate a variant with an unsupported type."); @@ -573,7 +578,7 @@ class variant : private variant_internal::VariantBase { variant& operator=(T&& t) noexcept( std::is_nothrow_assignable::value&& std::is_nothrow_constructible::value) { - variant_internal::visit_indices( + variant_internal::VisitIndices::Run( variant_internal::VariantCoreAccess::MakeConversionAssignVisitor( this, absl::forward(t)), index()); @@ -682,7 +687,7 @@ class variant : private variant_internal::VariantBase { // true and `is_nothrow_swappable()` is same as `std::is_trivial()`. void swap(variant& rhs) noexcept( absl::conjunction, std::is_trivial...>::value) { - return variant_internal::visit_indices( + return variant_internal::VisitIndices::Run( variant_internal::Swap{this, &rhs}, rhs.index()); } }; @@ -722,7 +727,7 @@ template constexpr variant_internal::RequireAllHaveEqualT operator==( const variant& a, const variant& b) { return (a.index() == b.index()) && - variant_internal::visit_indices( + variant_internal::VisitIndices::Run( variant_internal::EqualsOp{&a, &b}, a.index()); } @@ -731,7 +736,7 @@ template constexpr variant_internal::RequireAllHaveNotEqualT operator!=( const variant& a, const variant& b) { return (a.index() != b.index()) || - variant_internal::visit_indices( + variant_internal::VisitIndices::Run( variant_internal::NotEqualsOp{&a, &b}, a.index()); } @@ -741,7 +746,7 @@ constexpr variant_internal::RequireAllHaveLessThanT operator<( const variant& a, const variant& b) { return (a.index() != b.index()) ? (a.index() + 1) < (b.index() + 1) - : variant_internal::visit_indices( + : variant_internal::VisitIndices::Run( variant_internal::LessThanOp{&a, &b}, a.index()); } @@ -751,7 +756,7 @@ constexpr variant_internal::RequireAllHaveGreaterThanT operator>( const variant& a, const variant& b) { return (a.index() != b.index()) ? (a.index() + 1) > (b.index() + 1) - : variant_internal::visit_indices( + : variant_internal::VisitIndices::Run( variant_internal::GreaterThanOp{&a, &b}, a.index()); } @@ -762,7 +767,7 @@ constexpr variant_internal::RequireAllHaveLessThanOrEqualT operator<=( const variant& a, const variant& b) { return (a.index() != b.index()) ? (a.index() + 1) < (b.index() + 1) - : variant_internal::visit_indices( + : variant_internal::VisitIndices::Run( variant_internal::LessThanOrEqualsOp{&a, &b}, a.index()); } @@ -773,11 +778,12 @@ constexpr variant_internal::RequireAllHaveGreaterThanOrEqualT operator>=(const variant& a, const variant& b) { return (a.index() != b.index()) ? (a.index() + 1) > (b.index() + 1) - : variant_internal::visit_indices( + : variant_internal::VisitIndices::Run( variant_internal::GreaterThanOrEqualsOp{&a, &b}, a.index()); } +} // inline namespace lts_2018_06_20 } // namespace absl namespace std { @@ -798,6 +804,7 @@ struct hash> #endif // ABSL_HAVE_STD_VARIANT namespace absl { +inline namespace lts_2018_06_20 { namespace variant_internal { // Helper visitor for converting a variant` into another type (mostly @@ -833,6 +840,7 @@ To ConvertVariantTo(Variant&& variant) { std::forward(variant)); } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_TYPES_VARIANT_H_ diff --git a/absl/types/variant_benchmark.cc b/absl/types/variant_benchmark.cc new file mode 100644 index 00000000000..93acc2b6ba6 --- /dev/null +++ b/absl/types/variant_benchmark.cc @@ -0,0 +1,222 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 tests for the variant template. The 'is' and 'IsEmpty' methods +// of variant are not explicitly tested because they are used repeatedly +// in building other tests. All other public variant methods should have +// explicit tests. + +#include "absl/types/variant.h" + +#include +#include +#include +#include + +#include "benchmark/benchmark.h" +#include "absl/utility/utility.h" + +namespace absl { +inline namespace lts_2018_06_20 { +namespace { + +template +struct VariantAlternative { + char member; +}; + +template +struct VariantOfAlternativesImpl; + +template +struct VariantOfAlternativesImpl> { + using type = absl::variant...>; +}; + +template +using VariantOfAlternatives = typename VariantOfAlternativesImpl< + absl::make_index_sequence>::type; + +struct Empty {}; + +template +void Ignore(T...) noexcept {} + +template +Empty DoNotOptimizeAndReturnEmpty(T&& arg) noexcept { + benchmark::DoNotOptimize(arg); + return {}; +} + +struct VisitorApplier { + struct Visitor { + template + void operator()(T&&... args) const noexcept { + Ignore(DoNotOptimizeAndReturnEmpty(args)...); + } + }; + + template + void operator()(const Vars&... vars) const noexcept { + absl::visit(Visitor(), vars...); + } +}; + +template +struct MakeWithIndex { + using Variant = VariantOfAlternatives; + + static Variant Run(std::size_t index) { + return index == CurrIndex + ? Variant(absl::in_place_index_t()) + : MakeWithIndex::Run(index); + } +}; + +template +struct MakeWithIndex { + using Variant = VariantOfAlternatives; + + static Variant Run(std::size_t /*index*/) { return Variant(); } +}; + +template +struct MakeVariantTuple; + +template +using always_t = T; + +template +VariantOfAlternatives MakeVariant(std::size_t dimension, + std::size_t index) { + return dimension == 0 + ? MakeWithIndex::Run(index % NumIndices) + : MakeVariant(dimension - 1, index / NumIndices); +} + +template +struct MakeVariantTuple> { + using VariantTuple = + std::tuple, Dimensions>...>; + + static VariantTuple Run(int index) { + return std::make_tuple(MakeVariant(Dimensions, index)...); + } +}; + +constexpr std::size_t integral_pow(std::size_t base, std::size_t power) { + return power == 0 ? 1 : base * integral_pow(base, power - 1); +} + +template +struct VisitTestBody { + template + static bool Run(Vars& vars, State& state) { + if (state.KeepRunning()) { + absl::apply(VisitorApplier(), vars[I]); + return VisitTestBody::Run(vars, state); + } + return false; + } +}; + +template +struct VisitTestBody { + template + static bool Run(Vars& /*vars*/, State& /*state*/) { + return true; + } +}; + +// Visit operations where branch prediction is likely to give a boost. +template +void BM_RedundantVisit(benchmark::State& state) { + auto vars = + MakeVariantTuple>:: + Run(static_cast(state.range(0))); + + for (auto _ : state) { // NOLINT + benchmark::DoNotOptimize(vars); + absl::apply(VisitorApplier(), vars); + } +} + +// Visit operations where branch prediction is unlikely to give a boost. +template +void BM_Visit(benchmark::State& state) { + constexpr std::size_t num_possibilities = + integral_pow(NumIndices, NumDimensions); + + using VariantTupleMaker = + MakeVariantTuple>; + using Tuple = typename VariantTupleMaker::VariantTuple; + + Tuple vars[num_possibilities]; + for (std::size_t i = 0; i < num_possibilities; ++i) + vars[i] = VariantTupleMaker::Run(i); + + while (VisitTestBody::Run(vars, state)) { + } +} + +// Visitation +// Each visit is on a different variant with a different active alternative) + +// Unary visit +BENCHMARK_TEMPLATE(BM_Visit, 1); +BENCHMARK_TEMPLATE(BM_Visit, 2); +BENCHMARK_TEMPLATE(BM_Visit, 3); +BENCHMARK_TEMPLATE(BM_Visit, 4); +BENCHMARK_TEMPLATE(BM_Visit, 5); +BENCHMARK_TEMPLATE(BM_Visit, 6); +BENCHMARK_TEMPLATE(BM_Visit, 7); +BENCHMARK_TEMPLATE(BM_Visit, 8); +BENCHMARK_TEMPLATE(BM_Visit, 16); +BENCHMARK_TEMPLATE(BM_Visit, 32); +BENCHMARK_TEMPLATE(BM_Visit, 64); + +// Binary visit +BENCHMARK_TEMPLATE(BM_Visit, 1, 2); +BENCHMARK_TEMPLATE(BM_Visit, 2, 2); +BENCHMARK_TEMPLATE(BM_Visit, 3, 2); +BENCHMARK_TEMPLATE(BM_Visit, 4, 2); +BENCHMARK_TEMPLATE(BM_Visit, 5, 2); + +// Ternary visit +BENCHMARK_TEMPLATE(BM_Visit, 1, 3); +BENCHMARK_TEMPLATE(BM_Visit, 2, 3); +BENCHMARK_TEMPLATE(BM_Visit, 3, 3); + +// Quaternary visit +BENCHMARK_TEMPLATE(BM_Visit, 1, 4); +BENCHMARK_TEMPLATE(BM_Visit, 2, 4); + +// Redundant Visitation +// Each visit consistently has the same alternative active + +// Unary visit +BENCHMARK_TEMPLATE(BM_RedundantVisit, 1)->Arg(0); +BENCHMARK_TEMPLATE(BM_RedundantVisit, 2)->DenseRange(0, 1); +BENCHMARK_TEMPLATE(BM_RedundantVisit, 8)->DenseRange(0, 7); + +// Binary visit +BENCHMARK_TEMPLATE(BM_RedundantVisit, 1, 2)->Arg(0); +BENCHMARK_TEMPLATE(BM_RedundantVisit, 2, 2) + ->DenseRange(0, integral_pow(2, 2) - 1); +BENCHMARK_TEMPLATE(BM_RedundantVisit, 4, 2) + ->DenseRange(0, integral_pow(4, 2) - 1); + +} // namespace +} // inline namespace lts_2018_06_20 +} // namespace absl diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc index 27c0b96ca6f..d7bc90d2134 100644 --- a/absl/types/variant_exception_safety_test.cc +++ b/absl/types/variant_exception_safety_test.cc @@ -24,6 +24,7 @@ #include "absl/memory/memory.h" namespace absl { +inline namespace lts_2018_06_20 { namespace { using ::testing::MakeExceptionSafetyTester; @@ -505,4 +506,5 @@ TEST(VariantExceptionSafetyTest, Swap) { } } // namespace +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc index 262bd9446c1..295023ae770 100644 --- a/absl/types/variant_test.cc +++ b/absl/types/variant_test.cc @@ -67,6 +67,7 @@ struct hash { struct NonHashable {}; namespace absl { +inline namespace lts_2018_06_20 { namespace { using ::testing::DoubleEq; @@ -2609,4 +2610,5 @@ TEST(VariantTest, MoveCtorBug) { } } // namespace +} // inline namespace lts_2018_06_20 } // namespace absl diff --git a/absl/utility/utility.h b/absl/utility/utility.h index d73602c47d3..abb2f8a1ff8 100644 --- a/absl/utility/utility.h +++ b/absl/utility/utility.h @@ -51,6 +51,7 @@ #include "absl/meta/type_traits.h" namespace absl { +inline namespace lts_2018_06_20 { // integer_sequence // @@ -286,6 +287,7 @@ T exchange(T& obj, U&& new_value) { return old_value; } +} // inline namespace lts_2018_06_20 } // namespace absl #endif // ABSL_UTILITY_UTILITY_H_ From fcb104594b0bb4b8ac306cb2f55ecdad40974683 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 4 Dec 2018 11:01:12 -0800 Subject: [PATCH 2/5] Creation of LTS branch "lts_2018_12_18" - 44b0fafc62d9b8f192e8180cbe9c4b806b339d57 Export of internal Abseil changes. by Abseil Team - 926bfeb9fff223429c12224b7514243886323e8d Export of internal Abseil changes. by Abseil Team - 13327debebc5c2d1d4991b69fe50450e340e50e4 Export of internal Abseil changes. by Abseil Team - 3088e76c597e068479e82508b1770a7ad0c806b6 Export of internal Abseil changes. by Abseil Team - f6ae816808cd913e0e2b3e2af14f328fa1071af0 Export of internal Abseil changes. by Abseil Team - a06c4a1d9093137b7217a5aaba8920d62e835dc0 Export of internal Abseil changes. by Abseil Team - 7b46e1d31a6b08b1c6da2a13e7b151a20446fa07 Export of internal Abseil changes. by Abseil Team - 070f6e47b33a2909d039e620c873204f78809492 Export of internal Abseil changes. by Abseil Team - 7990fd459e9339467814ddb95000c87cb1e4d945 Export of internal Abseil changes. by Abseil Team - f95179062eb65ce40895cc76f1398cce25394369 Export of internal Abseil changes. by Abseil Team - cc8dcd307b76a575d2e3e0958a4fe4c7193c2f68 Export of internal Abseil changes. by Abseil Team - a705aa78dc76fc5c79d501e61dcc077eca68a8a4 Merge pull request #194 from Mizux/windows by Xiaoyi Zhang - a4c3ffff11eec0ee45742f915c255e9f870b7e0f Export of internal Abseil changes. by Abseil Team - 01174578651b73021d9b8c3820f6fea707dacdf0 Merge pull request #201 from ccawley2011/fix-byteswap by Matt Calabrese <38107210+mattcalabrese-google@users.noreply.github.com> - f86f9413856b65afdd61fea938d684b8ab73115a Export of internal Abseil changes. by Abseil Team - 94c298e2a0ae409e283cab96c954a685bd865a70 Export of internal Abseil changes. by Abseil Team - 0884a6a04e4497d11b1b398cc0e422b118bf977a Export of internal Abseil changes. by Abseil Team - c16d5557cd05119b5b7b1318ef778ebe3195b4a1 Export of internal Abseil changes. by Abseil Team - 45221ccc4ed643e4209b0cc5798e97203f108fa8 Export of internal Abseil changes. by Abseil Team - 2019e17a520575ab365b2b5134d71068182c70b8 Export of internal Abseil changes. by Abseil Team - 5b70a8910b2e6fb0ce5193a41873139a126d2f7f Export of internal Abseil changes. by Abseil Team - a00bdd176d66ef0b417d9576052a19091fbdf891 Export of internal Abseil changes. by Abseil Team - f340f773edab951656b19b6f1a77c964a78ec4c2 Export of internal Abseil changes. by Abseil Team - 445998d7ac4e5d3c50411d377e3b50e960d2d6c2 Export of internal Abseil changes. by Abseil Team - e821380d69a549dc64900693942789d21aa4df5e Export of internal Abseil changes. by Abseil Team - f21d187b80e3b7f08fb279775ea9c8b48c636030 Export of internal Abseil changes. by Abseil Team - 5441bbe1db5d0f2ca24b5b60166367b0966790af Fix code snippet in comment (#174) by Loo Rong Jie - 5aae0cffae8ffaacab965756169b34e511b353df Fix CMake build (#173) by Stephan Dollberg - 48cd2c3f351ff188bc85684b84a91b6e6d17d896 Export of internal Abseil changes. by Abseil Team - e291c279e458761e77a69b09b129d3d1e81f1e80 Export of internal Abseil changes. by Abseil Team - e01d95528ea2137a4a27a88d1f57c6cb260aafed Export of internal Abseil changes. by Abseil Team - 8ff1374008259719b54a8cb128ef951c02da164c Export of internal Abseil changes. by Abseil Team - 02451914b9ad5320f81f56a89f3eef1f8683227c Export of internal Abseil changes. by Abseil Team - 921fd5cf02ec0d665439a790148d59faa7d4a72c Merge pull request #166 from rongjiecomputer/cmake-test by Gennadiy Civil - fb462224c058487763f263b7995d70efd0242c17 Export of internal Abseil changes. by Abseil Team - c075ad321696fa5072e097f0a51e4fe76a6fe13e Export of internal Abseil changes. by Abseil Team - 0f4bc966754ec6cd28d5f03467d56f1efdc598e3 Export of internal Abseil changes. by Abseil Team - 6c7e5ffc43decd92f7bdfc510ad8a245a20b6dea Export of internal Abseil changes. by Abseil Team - d6df769173bf0263489f98874b93034db0e479a2 Export of internal Abseil changes. by Abseil Team - 28080f5f050c9530aa9f2b39c60d8217038d64ff Export of internal Abseil changes. by Abseil Team - 9c987f429bba32fb4446280fd3b91e2472d71d4d Export of internal Abseil changes. by Abseil Team - 5e7d459eeca7bc53deab0ee9634601386b53d7c0 Export of internal Abseil changes. by Abseil Team - bed5bd6e185c7e0311f3a1f2dab4c96083dac636 Export of internal Abseil changes. by Abseil Team - fefc83638fb69395d259ed245699310610429064 Export of internal Abseil changes. by Abseil Team - d8cfe9f2a77fbee02c09642491e62a3f3677e0f6 Export of internal Abseil changes. by Abseil Team - ad5c960b2eb914881d1ceba0e996a0a8f3f6ca59 Export of internal Abseil changes. by Abseil Team - 86f0fe93ad9d6d033a319476736a3256369c1f75 Export of internal Abseil changes. by Abseil Team - f0f15c2778b0e4959244dd25e63f445a455870f5 Export of internal Abseil changes. by Abseil Team - 29ff6d4860070bf8fcbd39c8805d0c32d56628a3 Removed "warning treated as error" flag from MSVC (#153) by vocaviking - 083d04dd4a62ebbf037079b06e49b323c5e1192a Export of internal Abseil changes. by Abseil Team - bea85b52733022294eef108a2e42d77b616ddca2 Export of internal Abseil changes. by Abseil Team - 8f96be6ca60d967bd4b37f93d0a03bcff4145200 Export of internal Abseil changes. by Abseil Team - 92e07e5590752d6b8e67f7f2f86c6286561e8cea Merge pull request #152 from clnperez/fix-multi-defines-p... by Derek Mauro <761129+derekmauro@users.noreply.github.com> - 2125e6444a9de9e41f21ecdc674dd7d8759c149d Export of internal Abseil changes. by Abseil Team - 9acad869d21731f5bc50430a33fe61cc0ffcbb0b Merge pull request #150 from OlafvdSpek/patch-2 by Jonathan Cohen - c2e00d341913bf03b4597ade5b056042e23e8c58 Export of internal Abseil changes. by Abseil Team - 9e060686d1c325f34f9806b45fe77bafeed00aee Export of internal Abseil changes. by Abseil Team - 7aa411ceafc1272a28579cca739a97a2fb79055a Export of internal Abseil changes. by Abseil Team - 2c5af55ed34850d8b7dd46177c8ca53fdfda920e Export of internal Abseil changes. by Abseil Team - 44aa275286baf97fc13529aca547a88b180beb08 Merge pull request #143 from rongjiecomputer/kernel by Xiaoyi Zhang - 42f22a28401c952f1fc5942231c7fdac80811bf5 Export of internal Abseil changes. by Abseil Team - b973bc53ef366f0253b85eeed9a79b241884a843 Merge pull request #139 from siepkes/smartos-support by ahedberg - e0def7473e52336f58759e11db4cd9467e5e0356 Export of internal Abseil changes. by Abseil Team - f826f1d489b61b64df1d94afbe5981841a82e5fa Merge pull request #138 from edbaunton/remove-deprecated-... by ahedberg - 7b50a4a94b0c7df68b3a854c850b551aaef0a8b4 Export of internal Abseil changes. by Abseil Team - a5030ca5125b9d557ecfeea8acc8b1a8e49f6d27 Merge pull request #144 from rongjiecomputer/winsock2 by Xiaoyi Zhang - 02687955b7ca8fc02ada9b14bc247deeb108d341 Export of internal Abseil changes. by Abseil Team - 8f612ebb152fb7e05643a2bcf78cb89a8c0641ad Export of internal Abseil changes. by Abseil Team - 134496a31d8b324f762de3bee9a002658c984456 Export of internal Abseil changes. by Abseil Team - ba8d6cf07766263723e86736f20a51c1c9c67b19 Export of internal Abseil changes. by Abseil Team - be1e84b988fceabcea4fc9e93f899539f0c81901 Export of internal Abseil changes. by Abseil Team - 16ac2ec2e38cdf47f9330a312e319d57da659c10 Merge pull request #134 from rongjiecomputer/cmake by Alex Strelnikov - 7efd8dc0f1075356e9c7caa950afd1ecf854e8b9 Export of internal Abseil changes. by Abseil Team - 87a4c07856e7dc69958019d47b2f02ae47746ec0 Export of internal Abseil changes. by Abseil Team - 4491d606df34c44efda47b6d17b605262f17e182 Export of internal Abseil changes. by Abseil Team GitOrigin-RevId: 44b0fafc62d9b8f192e8180cbe9c4b806b339d57 Change-Id: I2c427b5b41b2d34101922048b00f3d9dafcb498d --- .gitignore | 10 + CMake/AbseilConfigureCopts.cmake | 145 ++ CMake/AbseilHelpers.cmake | 225 +- CMake/DownloadGTest.cmake | 2 +- CMakeLists.txt | 26 +- CONTRIBUTING.md | 47 + README.md | 10 +- WORKSPACE | 24 +- absl/BUILD.bazel | 7 +- absl/CMakeLists.txt | 1 + absl/algorithm/CMakeLists.txt | 74 +- absl/algorithm/algorithm.h | 4 +- absl/algorithm/container.h | 88 +- absl/algorithm/container_test.cc | 15 + absl/base/BUILD.bazel | 70 +- absl/base/CMakeLists.txt | 634 +++--- absl/base/attributes.h | 57 +- absl/base/bit_cast_test.cc | 4 +- absl/base/call_once.h | 12 +- absl/base/call_once_test.cc | 4 +- absl/base/casts.h | 57 +- absl/base/config.h | 57 +- absl/base/exception_safety_testing_test.cc | 80 +- absl/base/inline_variable_test.cc | 4 +- absl/base/inline_variable_test_a.cc | 4 +- absl/base/inline_variable_test_b.cc | 4 +- absl/base/internal/atomic_hook.h | 4 +- absl/base/internal/bits.h | 195 ++ absl/base/internal/bits_test.cc | 97 + absl/base/internal/cycleclock.cc | 4 +- absl/base/internal/cycleclock.h | 4 +- absl/base/internal/direct_mmap.h | 24 +- absl/base/internal/endian.h | 29 +- absl/base/internal/endian_test.cc | 24 +- .../base/internal/exception_safety_testing.cc | 4 + absl/base/internal/exception_safety_testing.h | 351 ++- absl/base/internal/exception_testing.h | 2 +- absl/base/internal/hide_ptr.h | 4 +- absl/base/internal/identity.h | 4 +- absl/base/internal/inline_variable_testing.h | 4 +- absl/base/internal/invoke.h | 4 +- absl/base/internal/low_level_alloc.cc | 24 +- absl/base/internal/low_level_alloc.h | 11 +- absl/base/internal/low_level_alloc_test.cc | 4 +- absl/base/internal/low_level_scheduling.h | 4 +- absl/base/internal/raw_logging.cc | 22 +- absl/base/internal/raw_logging.h | 49 +- absl/base/internal/scheduling_mode.h | 4 +- absl/base/internal/spinlock.cc | 53 +- absl/base/internal/spinlock.h | 10 +- absl/base/internal/spinlock_benchmark.cc | 52 + absl/base/internal/spinlock_linux.inc | 72 + absl/base/internal/spinlock_wait.cc | 15 +- absl/base/internal/spinlock_wait.h | 4 +- absl/base/internal/sysinfo.cc | 4 +- absl/base/internal/sysinfo.h | 4 +- absl/base/internal/sysinfo_test.cc | 4 +- absl/base/internal/thread_identity.cc | 14 +- absl/base/internal/thread_identity.h | 4 +- absl/base/internal/thread_identity_test.cc | 4 +- absl/base/internal/throw_delegate.cc | 8 +- absl/base/internal/throw_delegate.h | 4 +- absl/base/internal/unaligned_access.h | 131 +- absl/base/internal/unscaledcycleclock.cc | 4 +- absl/base/internal/unscaledcycleclock.h | 4 +- absl/base/invoke_test.cc | 4 +- absl/base/log_severity.h | 6 +- absl/base/macros.h | 18 +- absl/base/raw_logging_test.cc | 31 +- absl/base/spinlock_test_common.cc | 7 +- absl/base/thread_annotations.h | 13 +- absl/compiler_config_setting.bzl | 39 + absl/container/BUILD.bazel | 498 +++++ absl/container/CMakeLists.txt | 707 +++++- absl/container/fixed_array.h | 382 ++-- .../fixed_array_exception_safety_test.cc | 119 + absl/container/fixed_array_test.cc | 213 ++ absl/container/flat_hash_map.h | 582 +++++ absl/container/flat_hash_map_test.cc | 243 ++ absl/container/flat_hash_set.h | 491 +++++ absl/container/flat_hash_set_test.cc | 128 ++ absl/container/inlined_vector.h | 1255 ++++++----- absl/container/inlined_vector_benchmark.cc | 2 +- absl/container/inlined_vector_test.cc | 78 +- absl/container/internal/compressed_tuple.h | 177 ++ .../internal/compressed_tuple_test.cc | 168 ++ absl/container/internal/container_memory.h | 407 ++++ .../internal/container_memory_test.cc | 190 ++ .../internal/hash_function_defaults.h | 145 ++ .../internal/hash_function_defaults_test.cc | 303 +++ .../internal/hash_generator_testing.cc | 74 + .../internal/hash_generator_testing.h | 152 ++ absl/container/internal/hash_policy_testing.h | 184 ++ .../internal/hash_policy_testing_test.cc | 45 + absl/container/internal/hash_policy_traits.h | 191 ++ .../internal/hash_policy_traits_test.cc | 144 ++ absl/container/internal/hashtable_debug.h | 110 + .../internal/hashtable_debug_hooks.h | 83 + absl/container/internal/layout.h | 740 +++++++ absl/container/internal/layout_test.cc | 1557 +++++++++++++ absl/container/internal/node_hash_policy.h | 90 + .../internal/node_hash_policy_test.cc | 69 + absl/container/internal/raw_hash_map.h | 187 ++ absl/container/internal/raw_hash_set.cc | 48 + absl/container/internal/raw_hash_set.h | 1950 +++++++++++++++++ .../internal/raw_hash_set_allocator_test.cc | 430 ++++ absl/container/internal/raw_hash_set_test.cc | 1830 ++++++++++++++++ .../internal/test_instance_tracker.cc | 5 +- .../internal/test_instance_tracker.h | 56 +- .../internal/test_instance_tracker_test.cc | 22 + absl/container/internal/tracked.h | 80 + .../internal/unordered_map_constructor_test.h | 407 ++++ .../internal/unordered_map_lookup_test.h | 117 + .../internal/unordered_map_modifiers_test.h | 275 +++ absl/container/internal/unordered_map_test.cc | 40 + .../internal/unordered_set_constructor_test.h | 411 ++++ .../internal/unordered_set_lookup_test.h | 91 + .../internal/unordered_set_modifiers_test.h | 190 ++ absl/container/internal/unordered_set_test.cc | 39 + absl/container/node_hash_map.h | 584 +++++ absl/container/node_hash_map_test.cc | 220 ++ absl/container/node_hash_set.h | 490 +++++ absl/container/node_hash_set_test.cc | 105 + absl/copts.bzl | 23 +- absl/debugging/BUILD.bazel | 1 + absl/debugging/CMakeLists.txt | 413 ++-- absl/debugging/failure_signal_handler.cc | 25 +- absl/debugging/failure_signal_handler.h | 4 +- .../debugging/internal/address_is_readable.cc | 8 +- absl/debugging/internal/address_is_readable.h | 4 +- absl/debugging/internal/demangle.cc | 17 +- absl/debugging/internal/demangle.h | 4 +- absl/debugging/internal/demangle_test.cc | 4 +- absl/debugging/internal/elf_mem_image.cc | 4 +- absl/debugging/internal/elf_mem_image.h | 4 +- absl/debugging/internal/examine_stack.cc | 8 +- absl/debugging/internal/examine_stack.h | 4 +- absl/debugging/internal/stack_consumption.cc | 4 +- absl/debugging/internal/stack_consumption.h | 4 +- .../internal/stack_consumption_test.cc | 4 +- .../internal/stacktrace_aarch64-inl.inc | 4 +- .../debugging/internal/stacktrace_arm-inl.inc | 4 +- absl/debugging/internal/stacktrace_config.h | 40 +- .../internal/stacktrace_generic-inl.inc | 5 +- .../internal/stacktrace_powerpc-inl.inc | 13 +- .../internal/stacktrace_unimplemented-inl.inc | 4 +- .../internal/stacktrace_win32-inl.inc | 4 +- .../debugging/internal/stacktrace_x86-inl.inc | 4 +- absl/debugging/internal/symbolize.h | 8 +- absl/debugging/internal/vdso_support.cc | 4 +- absl/debugging/internal/vdso_support.h | 4 +- absl/debugging/leak_check.cc | 8 +- absl/debugging/leak_check.h | 4 +- absl/debugging/stacktrace.cc | 22 +- absl/debugging/stacktrace.h | 4 +- absl/debugging/symbolize.h | 4 +- absl/debugging/symbolize_elf.inc | 8 +- absl/debugging/symbolize_test.cc | 2 +- absl/debugging/symbolize_unimplemented.inc | 4 +- absl/debugging/symbolize_win32.inc | 4 +- absl/hash/BUILD.bazel | 113 + absl/hash/CMakeLists.txt | 111 + absl/hash/hash.h | 314 +++ absl/hash/hash_test.cc | 427 ++++ absl/hash/hash_testing.h | 376 ++++ absl/hash/internal/city.cc | 346 +++ absl/hash/internal/city.h | 94 + absl/hash/internal/city_test.cc | 595 +++++ absl/hash/internal/hash.cc | 25 + absl/hash/internal/hash.h | 898 ++++++++ absl/hash/internal/print_hash_of.cc | 23 + absl/hash/internal/spy_hash_state.h | 220 ++ absl/memory/BUILD.bazel | 2 + absl/memory/CMakeLists.txt | 79 +- absl/memory/memory.h | 74 +- absl/memory/memory_exception_safety_test.cc | 13 +- absl/memory/memory_test.cc | 42 +- absl/meta/type_traits.h | 74 +- absl/meta/type_traits_test.cc | 161 +- absl/numeric/BUILD.bazel | 1 + absl/numeric/int128.cc | 32 +- absl/numeric/int128.h | 94 +- absl/numeric/int128_test.cc | 12 + absl/strings/BUILD.bazel | 196 +- absl/strings/CMakeLists.txt | 146 +- absl/strings/ascii.cc | 4 +- absl/strings/ascii.h | 18 +- absl/strings/ascii_benchmark.cc | 120 + absl/strings/charconv.cc | 12 +- absl/strings/charconv.h | 10 +- absl/strings/charconv_test.cc | 20 +- absl/strings/escaping.cc | 24 +- absl/strings/escaping.h | 37 +- absl/strings/escaping_test.cc | 17 +- absl/strings/internal/bits.h | 55 - absl/strings/internal/char_map.h | 4 +- absl/strings/internal/charconv_bigint.cc | 4 +- absl/strings/internal/charconv_bigint.h | 19 +- absl/strings/internal/charconv_bigint_test.cc | 4 +- absl/strings/internal/charconv_parse.cc | 6 +- absl/strings/internal/charconv_parse.h | 4 +- absl/strings/internal/charconv_parse_test.cc | 16 +- absl/strings/internal/escaping_test_common.h | 4 +- absl/strings/internal/memutil.cc | 4 +- absl/strings/internal/memutil.h | 12 +- absl/strings/internal/numbers_test_common.h | 27 +- absl/strings/internal/ostringstream.cc | 4 +- absl/strings/internal/ostringstream.h | 16 +- absl/strings/internal/pow10_helper.cc | 122 ++ absl/strings/internal/pow10_helper.h | 38 + absl/strings/internal/pow10_helper_test.cc | 122 ++ absl/strings/internal/resize_uninitialized.h | 10 +- absl/strings/internal/stl_type_traits.h | 4 +- absl/strings/internal/str_format/arg.cc | 379 ++++ absl/strings/internal/str_format/arg.h | 422 ++++ absl/strings/internal/str_format/arg_test.cc | 113 + absl/strings/internal/str_format/bind.cc | 231 ++ absl/strings/internal/str_format/bind.h | 199 ++ absl/strings/internal/str_format/bind_test.cc | 133 ++ absl/strings/internal/str_format/checker.h | 327 +++ .../internal/str_format/checker_test.cc | 152 ++ .../internal/str_format/convert_test.cc | 577 +++++ absl/strings/internal/str_format/extension.cc | 86 + absl/strings/internal/str_format/extension.h | 414 ++++ .../internal/str_format/extension_test.cc | 65 + .../internal/str_format/float_conversion.cc | 485 ++++ .../internal/str_format/float_conversion.h | 23 + absl/strings/internal/str_format/output.cc | 72 + absl/strings/internal/str_format/output.h | 103 + .../internal/str_format/output_test.cc | 80 + absl/strings/internal/str_format/parser.cc | 311 +++ absl/strings/internal/str_format/parser.h | 294 +++ .../internal/str_format/parser_test.cc | 391 ++++ absl/strings/internal/str_join_internal.h | 16 +- absl/strings/internal/str_split_internal.h | 36 +- absl/strings/internal/utf8.cc | 4 +- absl/strings/internal/utf8.h | 4 +- absl/strings/match.cc | 11 +- absl/strings/match.h | 26 +- absl/strings/match_test.cc | 11 + absl/strings/numbers.cc | 16 +- absl/strings/numbers.h | 28 +- absl/strings/numbers_benchmark.cc | 263 +++ absl/strings/numbers_test.cc | 17 +- absl/strings/str_cat.cc | 12 +- absl/strings/str_cat.h | 38 +- absl/strings/str_cat_test.cc | 11 +- absl/strings/str_format.h | 514 +++++ absl/strings/str_format_test.cc | 628 ++++++ absl/strings/str_join.h | 61 +- absl/strings/str_replace.cc | 4 +- absl/strings/str_replace.h | 34 +- absl/strings/str_replace_benchmark.cc | 8 +- absl/strings/str_split.cc | 17 +- absl/strings/str_split.h | 77 +- absl/strings/str_split_benchmark.cc | 8 +- absl/strings/str_split_test.cc | 34 +- absl/strings/string_view.cc | 4 +- absl/strings/string_view.h | 65 +- absl/strings/string_view_benchmark.cc | 10 +- absl/strings/string_view_test.cc | 20 +- absl/strings/strip.h | 22 +- absl/strings/strip_test.cc | 4 +- absl/strings/substitute.cc | 4 +- absl/strings/substitute.h | 40 +- absl/synchronization/BUILD.bazel | 36 +- absl/synchronization/CMakeLists.txt | 2 +- absl/synchronization/barrier.cc | 4 +- absl/synchronization/barrier.h | 4 +- absl/synchronization/blocking_counter.cc | 4 +- absl/synchronization/blocking_counter.h | 4 +- absl/synchronization/blocking_counter_test.cc | 4 +- .../internal/create_thread_identity.cc | 4 +- .../internal/create_thread_identity.h | 4 +- absl/synchronization/internal/graphcycles.cc | 10 +- absl/synchronization/internal/graphcycles.h | 4 +- .../internal/graphcycles_test.cc | 4 +- .../synchronization/internal/kernel_timeout.h | 30 +- .../synchronization/internal/mutex_nonprod.cc | 4 +- .../internal/mutex_nonprod.inc | 4 +- .../internal/per_thread_sem.cc | 4 +- .../synchronization/internal/per_thread_sem.h | 4 +- .../internal/per_thread_sem_test.cc | 11 +- absl/synchronization/internal/thread_pool.h | 4 +- absl/synchronization/internal/waiter.cc | 4 +- absl/synchronization/internal/waiter.h | 4 +- absl/synchronization/lifetime_test.cc | 15 +- absl/synchronization/mutex.cc | 12 +- absl/synchronization/mutex.h | 14 +- absl/synchronization/mutex_benchmark.cc | 159 +- absl/synchronization/mutex_test.cc | 575 +++-- absl/synchronization/notification.cc | 5 +- absl/synchronization/notification.h | 5 +- absl/synchronization/notification_test.cc | 13 +- absl/time/BUILD.bazel | 9 +- absl/time/CMakeLists.txt | 4 +- absl/time/civil_time.cc | 90 + absl/time/civil_time.h | 488 +++++ absl/time/civil_time_benchmark.cc | 107 + absl/time/civil_time_test.cc | 1073 +++++++++ absl/time/clock.cc | 22 +- absl/time/clock.h | 4 +- absl/time/clock_test.cc | 94 +- absl/time/duration.cc | 31 +- absl/time/duration_benchmark.cc | 78 +- absl/time/duration_test.cc | 169 +- absl/time/format.cc | 20 +- absl/time/format_benchmark.cc | 7 +- absl/time/format_test.cc | 46 +- absl/time/internal/cctz/BUILD.bazel | 3 + .../internal/cctz/include/cctz/civil_time.h | 6 +- .../cctz/include/cctz/civil_time_detail.h | 78 +- .../internal/cctz/include/cctz/time_zone.h | 133 +- .../cctz/include/cctz/zone_info_source.h | 13 +- absl/time/internal/cctz/src/cctz_benchmark.cc | 64 +- .../internal/cctz/src/civil_time_detail.cc | 4 +- .../time/internal/cctz/src/civil_time_test.cc | 6 +- .../time/internal/cctz/src/time_zone_fixed.cc | 41 +- absl/time/internal/cctz/src/time_zone_fixed.h | 10 +- .../internal/cctz/src/time_zone_format.cc | 23 +- .../cctz/src/time_zone_format_test.cc | 314 +-- absl/time/internal/cctz/src/time_zone_if.cc | 4 +- absl/time/internal/cctz/src/time_zone_if.h | 32 +- absl/time/internal/cctz/src/time_zone_impl.cc | 17 +- absl/time/internal/cctz/src/time_zone_impl.h | 45 +- absl/time/internal/cctz/src/time_zone_info.cc | 114 +- absl/time/internal/cctz/src/time_zone_info.h | 16 +- absl/time/internal/cctz/src/time_zone_libc.cc | 222 +- absl/time/internal/cctz/src/time_zone_libc.h | 13 +- .../internal/cctz/src/time_zone_lookup.cc | 41 +- .../cctz/src/time_zone_lookup_test.cc | 456 ++-- .../time/internal/cctz/src/time_zone_posix.cc | 4 +- absl/time/internal/cctz/src/time_zone_posix.h | 46 +- absl/time/internal/cctz/src/tzfile.h | 2 + .../internal/cctz/src/zone_info_source.cc | 23 +- absl/time/internal/cctz/testdata/version | 2 +- .../cctz/testdata/zoneinfo/Africa/Abidjan | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Africa/Accra | Bin 842 -> 828 bytes .../cctz/testdata/zoneinfo/Africa/Addis_Ababa | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Africa/Algiers | Bin 760 -> 751 bytes .../cctz/testdata/zoneinfo/Africa/Asmara | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Africa/Asmera | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Africa/Bamako | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Africa/Bangui | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Banjul | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Africa/Bissau | Bin 208 -> 194 bytes .../cctz/testdata/zoneinfo/Africa/Blantyre | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Brazzaville | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Bujumbura | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Cairo | Bin 1972 -> 1963 bytes .../cctz/testdata/zoneinfo/Africa/Casablanca | Bin 1643 -> 1533 bytes .../cctz/testdata/zoneinfo/Africa/Ceuta | Bin 2059 -> 2050 bytes .../cctz/testdata/zoneinfo/Africa/Conakry | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Africa/Dakar | Bin 170 -> 156 bytes .../testdata/zoneinfo/Africa/Dar_es_Salaam | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Africa/Djibouti | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Africa/Douala | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/El_Aaiun | Bin 1473 -> 1403 bytes .../cctz/testdata/zoneinfo/Africa/Freetown | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Africa/Gaborone | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Harare | Bin 171 -> 157 bytes .../testdata/zoneinfo/Africa/Johannesburg | Bin 271 -> 262 bytes .../cctz/testdata/zoneinfo/Africa/Juba | Bin 683 -> 669 bytes .../cctz/testdata/zoneinfo/Africa/Kampala | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Africa/Khartoum | Bin 713 -> 699 bytes .../cctz/testdata/zoneinfo/Africa/Kigali | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Kinshasa | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Lagos | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Libreville | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Lome | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Africa/Luanda | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Lubumbashi | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Lusaka | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Malabo | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Maputo | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Maseru | Bin 271 -> 262 bytes .../cctz/testdata/zoneinfo/Africa/Mbabane | Bin 271 -> 262 bytes .../cctz/testdata/zoneinfo/Africa/Mogadishu | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Africa/Monrovia | Bin 233 -> 224 bytes .../cctz/testdata/zoneinfo/Africa/Nairobi | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Africa/Ndjamena | Bin 225 -> 211 bytes .../cctz/testdata/zoneinfo/Africa/Niamey | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Nouakchott | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Africa/Ouagadougou | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Africa/Porto-Novo | Bin 171 -> 157 bytes .../cctz/testdata/zoneinfo/Africa/Sao_Tome | Bin 234 -> 225 bytes .../cctz/testdata/zoneinfo/Africa/Timbuktu | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Africa/Tripoli | Bin 655 -> 641 bytes .../cctz/testdata/zoneinfo/Africa/Tunis | Bin 710 -> 701 bytes .../cctz/testdata/zoneinfo/Africa/Windhoek | Bin 988 -> 979 bytes .../cctz/testdata/zoneinfo/America/Adak | Bin 2365 -> 2356 bytes .../cctz/testdata/zoneinfo/America/Anchorage | Bin 2380 -> 2371 bytes .../cctz/testdata/zoneinfo/America/Anguilla | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Antigua | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Araguaina | Bin 910 -> 896 bytes .../zoneinfo/America/Argentina/Buenos_Aires | Bin 1109 -> 1100 bytes .../zoneinfo/America/Argentina/Catamarca | Bin 1109 -> 1100 bytes .../zoneinfo/America/Argentina/ComodRivadavia | Bin 1109 -> 1100 bytes .../zoneinfo/America/Argentina/Cordoba | Bin 1109 -> 1100 bytes .../testdata/zoneinfo/America/Argentina/Jujuy | Bin 1081 -> 1072 bytes .../zoneinfo/America/Argentina/La_Rioja | Bin 1123 -> 1114 bytes .../zoneinfo/America/Argentina/Mendoza | Bin 1109 -> 1100 bytes .../zoneinfo/America/Argentina/Rio_Gallegos | Bin 1109 -> 1100 bytes .../testdata/zoneinfo/America/Argentina/Salta | Bin 1081 -> 1072 bytes .../zoneinfo/America/Argentina/San_Juan | Bin 1123 -> 1114 bytes .../zoneinfo/America/Argentina/San_Luis | Bin 1139 -> 1130 bytes .../zoneinfo/America/Argentina/Tucuman | Bin 1137 -> 1128 bytes .../zoneinfo/America/Argentina/Ushuaia | Bin 1109 -> 1100 bytes .../cctz/testdata/zoneinfo/America/Aruba | Bin 212 -> 198 bytes .../cctz/testdata/zoneinfo/America/Asuncion | Bin 2077 -> 2068 bytes .../cctz/testdata/zoneinfo/America/Atikokan | Bin 345 -> 336 bytes .../cctz/testdata/zoneinfo/America/Atka | Bin 2365 -> 2356 bytes .../cctz/testdata/zoneinfo/America/Bahia | Bin 1050 -> 1036 bytes .../testdata/zoneinfo/America/Bahia_Banderas | Bin 1588 -> 1574 bytes .../cctz/testdata/zoneinfo/America/Barbados | Bin 344 -> 330 bytes .../cctz/testdata/zoneinfo/America/Belem | Bin 602 -> 588 bytes .../cctz/testdata/zoneinfo/America/Belize | Bin 978 -> 964 bytes .../testdata/zoneinfo/America/Blanc-Sablon | Bin 307 -> 298 bytes .../cctz/testdata/zoneinfo/America/Boa_Vista | Bin 658 -> 644 bytes .../cctz/testdata/zoneinfo/America/Bogota | Bin 271 -> 262 bytes .../cctz/testdata/zoneinfo/America/Boise | Bin 2403 -> 2394 bytes .../testdata/zoneinfo/America/Buenos_Aires | Bin 1109 -> 1100 bytes .../testdata/zoneinfo/America/Cambridge_Bay | Bin 2098 -> 2084 bytes .../testdata/zoneinfo/America/Campo_Grande | Bin 2016 -> 2002 bytes .../cctz/testdata/zoneinfo/America/Cancun | Bin 816 -> 802 bytes .../cctz/testdata/zoneinfo/America/Caracas | Bin 289 -> 280 bytes .../cctz/testdata/zoneinfo/America/Catamarca | Bin 1109 -> 1100 bytes .../cctz/testdata/zoneinfo/America/Cayenne | Bin 224 -> 210 bytes .../cctz/testdata/zoneinfo/America/Cayman | Bin 203 -> 194 bytes .../cctz/testdata/zoneinfo/America/Chicago | Bin 3585 -> 3576 bytes .../cctz/testdata/zoneinfo/America/Chihuahua | Bin 1522 -> 1508 bytes .../testdata/zoneinfo/America/Coral_Harbour | Bin 345 -> 336 bytes .../cctz/testdata/zoneinfo/America/Cordoba | Bin 1109 -> 1100 bytes .../cctz/testdata/zoneinfo/America/Costa_Rica | Bin 341 -> 332 bytes .../cctz/testdata/zoneinfo/America/Creston | Bin 233 -> 224 bytes .../cctz/testdata/zoneinfo/America/Cuiaba | Bin 1988 -> 1974 bytes .../cctz/testdata/zoneinfo/America/Curacao | Bin 212 -> 198 bytes .../testdata/zoneinfo/America/Danmarkshavn | Bin 712 -> 698 bytes .../cctz/testdata/zoneinfo/America/Dawson | Bin 2093 -> 2084 bytes .../testdata/zoneinfo/America/Dawson_Creek | Bin 1059 -> 1050 bytes .../cctz/testdata/zoneinfo/America/Denver | Bin 2453 -> 2444 bytes .../cctz/testdata/zoneinfo/America/Detroit | Bin 2188 -> 2174 bytes .../cctz/testdata/zoneinfo/America/Dominica | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Edmonton | Bin 2402 -> 2388 bytes .../cctz/testdata/zoneinfo/America/Eirunepe | Bin 690 -> 676 bytes .../testdata/zoneinfo/America/El_Salvador | Bin 250 -> 236 bytes .../cctz/testdata/zoneinfo/America/Ensenada | Bin 2356 -> 2342 bytes .../testdata/zoneinfo/America/Fort_Nelson | Bin 2249 -> 2240 bytes .../cctz/testdata/zoneinfo/America/Fort_Wayne | Bin 1675 -> 1666 bytes .../cctz/testdata/zoneinfo/America/Fortaleza | Bin 742 -> 728 bytes .../cctz/testdata/zoneinfo/America/Glace_Bay | Bin 2206 -> 2192 bytes .../cctz/testdata/zoneinfo/America/Godthab | Bin 1892 -> 1878 bytes .../cctz/testdata/zoneinfo/America/Goose_Bay | Bin 3219 -> 3210 bytes .../cctz/testdata/zoneinfo/America/Grand_Turk | Bin 1881 -> 1872 bytes .../cctz/testdata/zoneinfo/America/Grenada | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Guadeloupe | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Guatemala | Bin 306 -> 292 bytes .../cctz/testdata/zoneinfo/America/Guayaquil | Bin 271 -> 262 bytes .../cctz/testdata/zoneinfo/America/Guyana | Bin 266 -> 252 bytes .../cctz/testdata/zoneinfo/America/Halifax | Bin 3438 -> 3424 bytes .../cctz/testdata/zoneinfo/America/Havana | Bin 2437 -> 2428 bytes .../cctz/testdata/zoneinfo/America/Hermosillo | Bin 454 -> 440 bytes .../zoneinfo/America/Indiana/Indianapolis | Bin 1675 -> 1666 bytes .../testdata/zoneinfo/America/Indiana/Knox | Bin 2437 -> 2428 bytes .../testdata/zoneinfo/America/Indiana/Marengo | Bin 1731 -> 1722 bytes .../zoneinfo/America/Indiana/Petersburg | Bin 1913 -> 1904 bytes .../zoneinfo/America/Indiana/Tell_City | Bin 1735 -> 1726 bytes .../testdata/zoneinfo/America/Indiana/Vevay | Bin 1423 -> 1414 bytes .../zoneinfo/America/Indiana/Vincennes | Bin 1703 -> 1694 bytes .../testdata/zoneinfo/America/Indiana/Winamac | Bin 1787 -> 1778 bytes .../testdata/zoneinfo/America/Indianapolis | Bin 1675 -> 1666 bytes .../cctz/testdata/zoneinfo/America/Inuvik | Bin 1928 -> 1914 bytes .../cctz/testdata/zoneinfo/America/Iqaluit | Bin 2046 -> 2032 bytes .../cctz/testdata/zoneinfo/America/Jamaica | Bin 507 -> 498 bytes .../cctz/testdata/zoneinfo/America/Jujuy | Bin 1081 -> 1072 bytes .../cctz/testdata/zoneinfo/America/Juneau | Bin 2362 -> 2353 bytes .../zoneinfo/America/Kentucky/Louisville | Bin 2781 -> 2772 bytes .../zoneinfo/America/Kentucky/Monticello | Bin 2361 -> 2352 bytes .../cctz/testdata/zoneinfo/America/Knox_IN | Bin 2437 -> 2428 bytes .../cctz/testdata/zoneinfo/America/Kralendijk | Bin 212 -> 198 bytes .../cctz/testdata/zoneinfo/America/La_Paz | Bin 257 -> 248 bytes .../cctz/testdata/zoneinfo/America/Lima | Bin 431 -> 422 bytes .../testdata/zoneinfo/America/Los_Angeles | Bin 2845 -> 2836 bytes .../cctz/testdata/zoneinfo/America/Louisville | Bin 2781 -> 2772 bytes .../testdata/zoneinfo/America/Lower_Princes | Bin 212 -> 198 bytes .../cctz/testdata/zoneinfo/America/Maceio | Bin 770 -> 756 bytes .../cctz/testdata/zoneinfo/America/Managua | Bin 463 -> 454 bytes .../cctz/testdata/zoneinfo/America/Manaus | Bin 630 -> 616 bytes .../cctz/testdata/zoneinfo/America/Marigot | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Martinique | Bin 257 -> 248 bytes .../cctz/testdata/zoneinfo/America/Matamoros | Bin 1416 -> 1402 bytes .../cctz/testdata/zoneinfo/America/Mazatlan | Bin 1564 -> 1550 bytes .../cctz/testdata/zoneinfo/America/Mendoza | Bin 1109 -> 1100 bytes .../cctz/testdata/zoneinfo/America/Menominee | Bin 2283 -> 2274 bytes .../cctz/testdata/zoneinfo/America/Merida | Bin 1456 -> 1442 bytes .../cctz/testdata/zoneinfo/America/Metlakatla | Bin 1418 -> 1409 bytes .../testdata/zoneinfo/America/Mexico_City | Bin 1618 -> 1604 bytes .../cctz/testdata/zoneinfo/America/Miquelon | Bin 1696 -> 1682 bytes .../cctz/testdata/zoneinfo/America/Moncton | Bin 3163 -> 3154 bytes .../cctz/testdata/zoneinfo/America/Monterrey | Bin 1416 -> 1402 bytes .../cctz/testdata/zoneinfo/America/Montevideo | Bin 1564 -> 1550 bytes .../cctz/testdata/zoneinfo/America/Montreal | Bin 3503 -> 3494 bytes .../cctz/testdata/zoneinfo/America/Montserrat | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Nassau | Bin 2284 -> 2270 bytes .../cctz/testdata/zoneinfo/America/New_York | Bin 3545 -> 3536 bytes .../cctz/testdata/zoneinfo/America/Nipigon | Bin 2131 -> 2122 bytes .../cctz/testdata/zoneinfo/America/Nome | Bin 2376 -> 2367 bytes .../cctz/testdata/zoneinfo/America/Noronha | Bin 742 -> 728 bytes .../zoneinfo/America/North_Dakota/Beulah | Bin 2389 -> 2380 bytes .../zoneinfo/America/North_Dakota/Center | Bin 2389 -> 2380 bytes .../zoneinfo/America/North_Dakota/New_Salem | Bin 2389 -> 2380 bytes .../cctz/testdata/zoneinfo/America/Ojinaga | Bin 1522 -> 1508 bytes .../cctz/testdata/zoneinfo/America/Panama | Bin 203 -> 194 bytes .../testdata/zoneinfo/America/Pangnirtung | Bin 2108 -> 2094 bytes .../cctz/testdata/zoneinfo/America/Paramaribo | Bin 296 -> 282 bytes .../cctz/testdata/zoneinfo/America/Phoenix | Bin 353 -> 344 bytes .../testdata/zoneinfo/America/Port-au-Prince | Bin 1455 -> 1446 bytes .../testdata/zoneinfo/America/Port_of_Spain | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Porto_Acre | Bin 662 -> 648 bytes .../testdata/zoneinfo/America/Porto_Velho | Bin 602 -> 588 bytes .../testdata/zoneinfo/America/Puerto_Rico | Bin 255 -> 246 bytes .../testdata/zoneinfo/America/Punta_Arenas | Bin 1911 -> 1902 bytes .../testdata/zoneinfo/America/Rainy_River | Bin 2131 -> 2122 bytes .../testdata/zoneinfo/America/Rankin_Inlet | Bin 1930 -> 1916 bytes .../cctz/testdata/zoneinfo/America/Recife | Bin 742 -> 728 bytes .../cctz/testdata/zoneinfo/America/Regina | Bin 994 -> 980 bytes .../cctz/testdata/zoneinfo/America/Resolute | Bin 1930 -> 1916 bytes .../cctz/testdata/zoneinfo/America/Rio_Branco | Bin 662 -> 648 bytes .../cctz/testdata/zoneinfo/America/Rosario | Bin 1109 -> 1100 bytes .../testdata/zoneinfo/America/Santa_Isabel | Bin 2356 -> 2342 bytes .../cctz/testdata/zoneinfo/America/Santarem | Bin 632 -> 618 bytes .../cctz/testdata/zoneinfo/America/Santiago | Bin 2538 -> 2529 bytes .../testdata/zoneinfo/America/Santo_Domingo | Bin 491 -> 482 bytes .../cctz/testdata/zoneinfo/America/Sao_Paulo | Bin 2016 -> 2002 bytes .../testdata/zoneinfo/America/Scoresbysund | Bin 1930 -> 1916 bytes .../cctz/testdata/zoneinfo/America/Shiprock | Bin 2453 -> 2444 bytes .../cctz/testdata/zoneinfo/America/Sitka | Bin 2350 -> 2329 bytes .../testdata/zoneinfo/America/St_Barthelemy | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/St_Johns | Bin 3664 -> 3655 bytes .../cctz/testdata/zoneinfo/America/St_Kitts | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/St_Lucia | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/St_Thomas | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/St_Vincent | Bin 170 -> 156 bytes .../testdata/zoneinfo/America/Swift_Current | Bin 574 -> 560 bytes .../testdata/zoneinfo/America/Tegucigalpa | Bin 278 -> 264 bytes .../cctz/testdata/zoneinfo/America/Thule | Bin 1528 -> 1514 bytes .../testdata/zoneinfo/America/Thunder_Bay | Bin 2211 -> 2202 bytes .../cctz/testdata/zoneinfo/America/Tijuana | Bin 2356 -> 2342 bytes .../cctz/testdata/zoneinfo/America/Toronto | Bin 3503 -> 3494 bytes .../cctz/testdata/zoneinfo/America/Tortola | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Vancouver | Bin 2901 -> 2892 bytes .../cctz/testdata/zoneinfo/America/Virgin | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/America/Whitehorse | Bin 2093 -> 2084 bytes .../cctz/testdata/zoneinfo/America/Winnipeg | Bin 2891 -> 2882 bytes .../cctz/testdata/zoneinfo/America/Yakutat | Bin 2314 -> 2305 bytes .../testdata/zoneinfo/America/Yellowknife | Bin 1980 -> 1966 bytes .../cctz/testdata/zoneinfo/Antarctica/Casey | Bin 311 -> 297 bytes .../cctz/testdata/zoneinfo/Antarctica/Davis | Bin 311 -> 297 bytes .../zoneinfo/Antarctica/DumontDUrville | Bin 216 -> 202 bytes .../testdata/zoneinfo/Antarctica/Macquarie | Bin 1543 -> 1534 bytes .../cctz/testdata/zoneinfo/Antarctica/Mawson | Bin 225 -> 211 bytes .../cctz/testdata/zoneinfo/Antarctica/McMurdo | Bin 2460 -> 2451 bytes .../cctz/testdata/zoneinfo/Antarctica/Palmer | Bin 1432 -> 1418 bytes .../cctz/testdata/zoneinfo/Antarctica/Rothera | Bin 186 -> 172 bytes .../testdata/zoneinfo/Antarctica/South_Pole | Bin 2460 -> 2451 bytes .../cctz/testdata/zoneinfo/Antarctica/Syowa | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Antarctica/Troll | Bin 1176 -> 1162 bytes .../cctz/testdata/zoneinfo/Antarctica/Vostok | Bin 187 -> 173 bytes .../testdata/zoneinfo/Arctic/Longyearbyen | Bin 2251 -> 2242 bytes .../internal/cctz/testdata/zoneinfo/Asia/Aden | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Asia/Almaty | Bin 1031 -> 1017 bytes .../cctz/testdata/zoneinfo/Asia/Amman | Bin 1877 -> 1863 bytes .../cctz/testdata/zoneinfo/Asia/Anadyr | Bin 1222 -> 1208 bytes .../cctz/testdata/zoneinfo/Asia/Aqtau | Bin 1017 -> 1003 bytes .../cctz/testdata/zoneinfo/Asia/Aqtobe | Bin 1047 -> 1033 bytes .../cctz/testdata/zoneinfo/Asia/Ashgabat | Bin 651 -> 637 bytes .../cctz/testdata/zoneinfo/Asia/Ashkhabad | Bin 651 -> 637 bytes .../cctz/testdata/zoneinfo/Asia/Atyrau | Bin 1025 -> 1011 bytes .../cctz/testdata/zoneinfo/Asia/Baghdad | Bin 1004 -> 995 bytes .../cctz/testdata/zoneinfo/Asia/Bahrain | Bin 225 -> 211 bytes .../internal/cctz/testdata/zoneinfo/Asia/Baku | Bin 1269 -> 1255 bytes .../cctz/testdata/zoneinfo/Asia/Bangkok | Bin 220 -> 211 bytes .../cctz/testdata/zoneinfo/Asia/Barnaul | Bin 1255 -> 1241 bytes .../cctz/testdata/zoneinfo/Asia/Beirut | Bin 2175 -> 2166 bytes .../cctz/testdata/zoneinfo/Asia/Bishkek | Bin 1045 -> 999 bytes .../cctz/testdata/zoneinfo/Asia/Brunei | Bin 229 -> 215 bytes .../cctz/testdata/zoneinfo/Asia/Calcutta | Bin 312 -> 303 bytes .../cctz/testdata/zoneinfo/Asia/Chita | Bin 1257 -> 1243 bytes .../cctz/testdata/zoneinfo/Asia/Choibalsan | Bin 991 -> 977 bytes .../cctz/testdata/zoneinfo/Asia/Chongqing | Bin 414 -> 545 bytes .../cctz/testdata/zoneinfo/Asia/Chungking | Bin 414 -> 545 bytes .../cctz/testdata/zoneinfo/Asia/Colombo | Bin 413 -> 404 bytes .../cctz/testdata/zoneinfo/Asia/Dacca | Bin 370 -> 361 bytes .../cctz/testdata/zoneinfo/Asia/Damascus | Bin 2320 -> 2306 bytes .../cctz/testdata/zoneinfo/Asia/Dhaka | Bin 370 -> 361 bytes .../internal/cctz/testdata/zoneinfo/Asia/Dili | Bin 253 -> 239 bytes .../cctz/testdata/zoneinfo/Asia/Dubai | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Asia/Dushanbe | Bin 621 -> 607 bytes .../cctz/testdata/zoneinfo/Asia/Famagusta | Bin 2042 -> 2028 bytes .../internal/cctz/testdata/zoneinfo/Asia/Gaza | Bin 2295 -> 2286 bytes .../cctz/testdata/zoneinfo/Asia/Harbin | Bin 414 -> 545 bytes .../cctz/testdata/zoneinfo/Asia/Hebron | Bin 2323 -> 2314 bytes .../cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh | Bin 389 -> 375 bytes .../cctz/testdata/zoneinfo/Asia/Hong_Kong | Bin 1189 -> 1175 bytes .../internal/cctz/testdata/zoneinfo/Asia/Hovd | Bin 921 -> 907 bytes .../cctz/testdata/zoneinfo/Asia/Irkutsk | Bin 1276 -> 1267 bytes .../cctz/testdata/zoneinfo/Asia/Istanbul | Bin 2166 -> 2157 bytes .../cctz/testdata/zoneinfo/Asia/Jakarta | Bin 392 -> 383 bytes .../cctz/testdata/zoneinfo/Asia/Jayapura | Bin 251 -> 237 bytes .../cctz/testdata/zoneinfo/Asia/Jerusalem | Bin 2265 -> 2256 bytes .../cctz/testdata/zoneinfo/Asia/Kabul | Bin 229 -> 220 bytes .../cctz/testdata/zoneinfo/Asia/Kamchatka | Bin 1198 -> 1184 bytes .../cctz/testdata/zoneinfo/Asia/Karachi | Bin 417 -> 403 bytes .../cctz/testdata/zoneinfo/Asia/Kashgar | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Asia/Kathmandu | Bin 238 -> 224 bytes .../cctz/testdata/zoneinfo/Asia/Katmandu | Bin 238 -> 224 bytes .../cctz/testdata/zoneinfo/Asia/Khandyga | Bin 1311 -> 1297 bytes .../cctz/testdata/zoneinfo/Asia/Kolkata | Bin 312 -> 303 bytes .../cctz/testdata/zoneinfo/Asia/Krasnoyarsk | Bin 1243 -> 1229 bytes .../cctz/testdata/zoneinfo/Asia/Kuala_Lumpur | Bin 424 -> 415 bytes .../cctz/testdata/zoneinfo/Asia/Kuching | Bin 521 -> 507 bytes .../cctz/testdata/zoneinfo/Asia/Kuwait | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Asia/Macao | Bin 771 -> 1241 bytes .../cctz/testdata/zoneinfo/Asia/Macau | Bin 771 -> 1241 bytes .../cctz/testdata/zoneinfo/Asia/Magadan | Bin 1258 -> 1244 bytes .../cctz/testdata/zoneinfo/Asia/Makassar | Bin 288 -> 274 bytes .../cctz/testdata/zoneinfo/Asia/Manila | Bin 367 -> 350 bytes .../cctz/testdata/zoneinfo/Asia/Muscat | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Asia/Nicosia | Bin 2016 -> 2002 bytes .../cctz/testdata/zoneinfo/Asia/Novokuznetsk | Bin 1197 -> 1183 bytes .../cctz/testdata/zoneinfo/Asia/Novosibirsk | Bin 1255 -> 1241 bytes .../internal/cctz/testdata/zoneinfo/Asia/Omsk | Bin 1243 -> 1229 bytes .../internal/cctz/testdata/zoneinfo/Asia/Oral | Bin 1039 -> 1025 bytes .../cctz/testdata/zoneinfo/Asia/Phnom_Penh | Bin 220 -> 211 bytes .../cctz/testdata/zoneinfo/Asia/Pontianak | Bin 395 -> 381 bytes .../cctz/testdata/zoneinfo/Asia/Pyongyang | Bin 267 -> 253 bytes .../cctz/testdata/zoneinfo/Asia/Qatar | Bin 225 -> 211 bytes .../cctz/testdata/zoneinfo/Asia/Qyzylorda | Bin 1047 -> 1017 bytes .../cctz/testdata/zoneinfo/Asia/Rangoon | Bin 297 -> 288 bytes .../cctz/testdata/zoneinfo/Asia/Riyadh | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Asia/Saigon | Bin 389 -> 375 bytes .../cctz/testdata/zoneinfo/Asia/Sakhalin | Bin 1234 -> 1220 bytes .../cctz/testdata/zoneinfo/Asia/Samarkand | Bin 619 -> 605 bytes .../cctz/testdata/zoneinfo/Asia/Seoul | Bin 531 -> 517 bytes .../cctz/testdata/zoneinfo/Asia/Shanghai | Bin 414 -> 545 bytes .../cctz/testdata/zoneinfo/Asia/Singapore | Bin 424 -> 415 bytes .../cctz/testdata/zoneinfo/Asia/Srednekolymsk | Bin 1244 -> 1230 bytes .../cctz/testdata/zoneinfo/Asia/Taipei | Bin 790 -> 781 bytes .../cctz/testdata/zoneinfo/Asia/Tashkent | Bin 635 -> 621 bytes .../cctz/testdata/zoneinfo/Asia/Tbilisi | Bin 1080 -> 1071 bytes .../cctz/testdata/zoneinfo/Asia/Tehran | Bin 1718 -> 1704 bytes .../cctz/testdata/zoneinfo/Asia/Tel_Aviv | Bin 2265 -> 2256 bytes .../cctz/testdata/zoneinfo/Asia/Thimbu | Bin 229 -> 215 bytes .../cctz/testdata/zoneinfo/Asia/Thimphu | Bin 229 -> 215 bytes .../cctz/testdata/zoneinfo/Asia/Tokyo | Bin 318 -> 309 bytes .../cctz/testdata/zoneinfo/Asia/Tomsk | Bin 1255 -> 1241 bytes .../cctz/testdata/zoneinfo/Asia/Ujung_Pandang | Bin 288 -> 274 bytes .../cctz/testdata/zoneinfo/Asia/Ulaanbaatar | Bin 921 -> 907 bytes .../cctz/testdata/zoneinfo/Asia/Ulan_Bator | Bin 921 -> 907 bytes .../cctz/testdata/zoneinfo/Asia/Urumqi | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Asia/Ust-Nera | Bin 1290 -> 1276 bytes .../cctz/testdata/zoneinfo/Asia/Vientiane | Bin 220 -> 211 bytes .../cctz/testdata/zoneinfo/Asia/Vladivostok | Bin 1244 -> 1230 bytes .../cctz/testdata/zoneinfo/Asia/Yakutsk | Bin 1243 -> 1229 bytes .../cctz/testdata/zoneinfo/Asia/Yangon | Bin 297 -> 288 bytes .../cctz/testdata/zoneinfo/Asia/Yekaterinburg | Bin 1281 -> 1267 bytes .../cctz/testdata/zoneinfo/Asia/Yerevan | Bin 1213 -> 1199 bytes .../cctz/testdata/zoneinfo/Atlantic/Azores | Bin 3493 -> 3484 bytes .../cctz/testdata/zoneinfo/Atlantic/Bermuda | Bin 2004 -> 1990 bytes .../cctz/testdata/zoneinfo/Atlantic/Canary | Bin 1911 -> 1897 bytes .../testdata/zoneinfo/Atlantic/Cape_Verde | Bin 284 -> 270 bytes .../cctz/testdata/zoneinfo/Atlantic/Faeroe | Bin 1829 -> 1815 bytes .../cctz/testdata/zoneinfo/Atlantic/Faroe | Bin 1829 -> 1815 bytes .../cctz/testdata/zoneinfo/Atlantic/Jan_Mayen | Bin 2251 -> 2242 bytes .../cctz/testdata/zoneinfo/Atlantic/Madeira | Bin 3484 -> 3475 bytes .../cctz/testdata/zoneinfo/Atlantic/Reykjavik | Bin 1188 -> 1174 bytes .../testdata/zoneinfo/Atlantic/South_Georgia | Bin 181 -> 172 bytes .../cctz/testdata/zoneinfo/Atlantic/St_Helena | Bin 170 -> 156 bytes .../cctz/testdata/zoneinfo/Atlantic/Stanley | Bin 1251 -> 1242 bytes .../cctz/testdata/zoneinfo/Australia/ACT | Bin 2223 -> 2214 bytes .../cctz/testdata/zoneinfo/Australia/Adelaide | Bin 2238 -> 2233 bytes .../cctz/testdata/zoneinfo/Australia/Brisbane | Bin 452 -> 443 bytes .../testdata/zoneinfo/Australia/Broken_Hill | Bin 2274 -> 2269 bytes .../cctz/testdata/zoneinfo/Australia/Canberra | Bin 2223 -> 2214 bytes .../cctz/testdata/zoneinfo/Australia/Currie | Bin 2223 -> 2214 bytes .../cctz/testdata/zoneinfo/Australia/Darwin | Bin 323 -> 318 bytes .../cctz/testdata/zoneinfo/Australia/Eucla | Bin 503 -> 494 bytes .../cctz/testdata/zoneinfo/Australia/Hobart | Bin 2335 -> 2326 bytes .../cctz/testdata/zoneinfo/Australia/LHI | Bin 1889 -> 1880 bytes .../cctz/testdata/zoneinfo/Australia/Lindeman | Bin 522 -> 513 bytes .../testdata/zoneinfo/Australia/Lord_Howe | Bin 1889 -> 1880 bytes .../testdata/zoneinfo/Australia/Melbourne | Bin 2223 -> 2214 bytes .../cctz/testdata/zoneinfo/Australia/NSW | Bin 2223 -> 2214 bytes .../cctz/testdata/zoneinfo/Australia/North | Bin 323 -> 318 bytes .../cctz/testdata/zoneinfo/Australia/Perth | Bin 479 -> 470 bytes .../testdata/zoneinfo/Australia/Queensland | Bin 452 -> 443 bytes .../cctz/testdata/zoneinfo/Australia/South | Bin 2238 -> 2233 bytes .../cctz/testdata/zoneinfo/Australia/Sydney | Bin 2223 -> 2214 bytes .../cctz/testdata/zoneinfo/Australia/Tasmania | Bin 2335 -> 2326 bytes .../cctz/testdata/zoneinfo/Australia/Victoria | Bin 2223 -> 2214 bytes .../cctz/testdata/zoneinfo/Australia/West | Bin 479 -> 470 bytes .../testdata/zoneinfo/Australia/Yancowinna | Bin 2274 -> 2269 bytes .../cctz/testdata/zoneinfo/Brazil/Acre | Bin 662 -> 648 bytes .../cctz/testdata/zoneinfo/Brazil/DeNoronha | Bin 742 -> 728 bytes .../cctz/testdata/zoneinfo/Brazil/East | Bin 2016 -> 2002 bytes .../cctz/testdata/zoneinfo/Brazil/West | Bin 630 -> 616 bytes absl/time/internal/cctz/testdata/zoneinfo/CET | Bin 2102 -> 2102 bytes .../internal/cctz/testdata/zoneinfo/CST6CDT | Bin 2294 -> 2294 bytes .../cctz/testdata/zoneinfo/Canada/Atlantic | Bin 3438 -> 3424 bytes .../cctz/testdata/zoneinfo/Canada/Central | Bin 2891 -> 2882 bytes .../cctz/testdata/zoneinfo/Canada/Eastern | Bin 3503 -> 3494 bytes .../cctz/testdata/zoneinfo/Canada/Mountain | Bin 2402 -> 2388 bytes .../testdata/zoneinfo/Canada/Newfoundland | Bin 3664 -> 3655 bytes .../cctz/testdata/zoneinfo/Canada/Pacific | Bin 2901 -> 2892 bytes .../testdata/zoneinfo/Canada/Saskatchewan | Bin 994 -> 980 bytes .../cctz/testdata/zoneinfo/Canada/Yukon | Bin 2093 -> 2084 bytes .../cctz/testdata/zoneinfo/Chile/Continental | Bin 2538 -> 2529 bytes .../cctz/testdata/zoneinfo/Chile/EasterIsland | Bin 2242 -> 2233 bytes .../time/internal/cctz/testdata/zoneinfo/Cuba | Bin 2437 -> 2428 bytes absl/time/internal/cctz/testdata/zoneinfo/EET | Bin 1876 -> 1876 bytes absl/time/internal/cctz/testdata/zoneinfo/EST | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/EST5EDT | Bin 2294 -> 2294 bytes .../internal/cctz/testdata/zoneinfo/Egypt | Bin 1972 -> 1963 bytes .../time/internal/cctz/testdata/zoneinfo/Eire | Bin 3531 -> 3522 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+0 | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+1 | Bin 148 -> 120 bytes .../cctz/testdata/zoneinfo/Etc/GMT+10 | Bin 149 -> 121 bytes .../cctz/testdata/zoneinfo/Etc/GMT+11 | Bin 149 -> 121 bytes .../cctz/testdata/zoneinfo/Etc/GMT+12 | Bin 149 -> 121 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+2 | Bin 148 -> 120 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+3 | Bin 148 -> 120 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+4 | Bin 148 -> 120 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+5 | Bin 148 -> 120 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+6 | Bin 148 -> 120 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+7 | Bin 148 -> 120 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+8 | Bin 148 -> 120 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT+9 | Bin 148 -> 120 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-0 | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-1 | Bin 149 -> 121 bytes .../cctz/testdata/zoneinfo/Etc/GMT-10 | Bin 150 -> 122 bytes .../cctz/testdata/zoneinfo/Etc/GMT-11 | Bin 150 -> 122 bytes .../cctz/testdata/zoneinfo/Etc/GMT-12 | Bin 150 -> 122 bytes .../cctz/testdata/zoneinfo/Etc/GMT-13 | Bin 150 -> 122 bytes .../cctz/testdata/zoneinfo/Etc/GMT-14 | Bin 150 -> 122 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-2 | Bin 149 -> 121 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-3 | Bin 149 -> 121 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-4 | Bin 149 -> 121 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-5 | Bin 149 -> 121 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-6 | Bin 149 -> 121 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-7 | Bin 149 -> 121 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-8 | Bin 149 -> 121 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT-9 | Bin 149 -> 121 bytes .../internal/cctz/testdata/zoneinfo/Etc/GMT0 | Bin 127 -> 118 bytes .../cctz/testdata/zoneinfo/Etc/Greenwich | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/UCT | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/UTC | Bin 127 -> 118 bytes .../cctz/testdata/zoneinfo/Etc/Universal | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Etc/Zulu | Bin 127 -> 118 bytes .../cctz/testdata/zoneinfo/Europe/Amsterdam | Bin 2949 -> 2940 bytes .../cctz/testdata/zoneinfo/Europe/Andorra | Bin 1751 -> 1742 bytes .../cctz/testdata/zoneinfo/Europe/Astrakhan | Bin 1197 -> 1183 bytes .../cctz/testdata/zoneinfo/Europe/Athens | Bin 2271 -> 2262 bytes .../cctz/testdata/zoneinfo/Europe/Belfast | Bin 3687 -> 3678 bytes .../cctz/testdata/zoneinfo/Europe/Belgrade | Bin 1957 -> 1948 bytes .../cctz/testdata/zoneinfo/Europe/Berlin | Bin 2335 -> 2326 bytes .../cctz/testdata/zoneinfo/Europe/Bratislava | Bin 2338 -> 2329 bytes .../cctz/testdata/zoneinfo/Europe/Brussels | Bin 2970 -> 2961 bytes .../cctz/testdata/zoneinfo/Europe/Bucharest | Bin 2221 -> 2212 bytes .../cctz/testdata/zoneinfo/Europe/Budapest | Bin 2405 -> 2396 bytes .../cctz/testdata/zoneinfo/Europe/Busingen | Bin 1918 -> 1909 bytes .../cctz/testdata/zoneinfo/Europe/Chisinau | Bin 2445 -> 2436 bytes .../cctz/testdata/zoneinfo/Europe/Copenhagen | Bin 2160 -> 2151 bytes .../cctz/testdata/zoneinfo/Europe/Dublin | Bin 3531 -> 3522 bytes .../cctz/testdata/zoneinfo/Europe/Gibraltar | Bin 3061 -> 3052 bytes .../cctz/testdata/zoneinfo/Europe/Guernsey | Bin 3687 -> 3678 bytes .../cctz/testdata/zoneinfo/Europe/Helsinki | Bin 1909 -> 1900 bytes .../cctz/testdata/zoneinfo/Europe/Isle_of_Man | Bin 3687 -> 3678 bytes .../cctz/testdata/zoneinfo/Europe/Istanbul | Bin 2166 -> 2157 bytes .../cctz/testdata/zoneinfo/Europe/Jersey | Bin 3687 -> 3678 bytes .../cctz/testdata/zoneinfo/Europe/Kaliningrad | Bin 1518 -> 1509 bytes .../cctz/testdata/zoneinfo/Europe/Kiev | Bin 2097 -> 2088 bytes .../cctz/testdata/zoneinfo/Europe/Kirov | Bin 1167 -> 1153 bytes .../cctz/testdata/zoneinfo/Europe/Lisbon | Bin 3469 -> 3469 bytes .../cctz/testdata/zoneinfo/Europe/Ljubljana | Bin 1957 -> 1948 bytes .../cctz/testdata/zoneinfo/Europe/London | Bin 3687 -> 3678 bytes .../cctz/testdata/zoneinfo/Europe/Luxembourg | Bin 2974 -> 2960 bytes .../cctz/testdata/zoneinfo/Europe/Madrid | Bin 2637 -> 2628 bytes .../cctz/testdata/zoneinfo/Europe/Malta | Bin 2629 -> 2620 bytes .../cctz/testdata/zoneinfo/Europe/Mariehamn | Bin 1909 -> 1900 bytes .../cctz/testdata/zoneinfo/Europe/Minsk | Bin 1370 -> 1361 bytes .../cctz/testdata/zoneinfo/Europe/Monaco | Bin 2953 -> 2944 bytes .../cctz/testdata/zoneinfo/Europe/Moscow | Bin 1544 -> 1535 bytes .../cctz/testdata/zoneinfo/Europe/Nicosia | Bin 2016 -> 2002 bytes .../cctz/testdata/zoneinfo/Europe/Oslo | Bin 2251 -> 2242 bytes .../cctz/testdata/zoneinfo/Europe/Paris | Bin 2971 -> 2962 bytes .../cctz/testdata/zoneinfo/Europe/Podgorica | Bin 1957 -> 1948 bytes .../cctz/testdata/zoneinfo/Europe/Prague | Bin 2338 -> 2329 bytes .../cctz/testdata/zoneinfo/Europe/Riga | Bin 2235 -> 2226 bytes .../cctz/testdata/zoneinfo/Europe/Rome | Bin 2692 -> 2683 bytes .../cctz/testdata/zoneinfo/Europe/Samara | Bin 1253 -> 1215 bytes .../cctz/testdata/zoneinfo/Europe/San_Marino | Bin 2692 -> 2683 bytes .../cctz/testdata/zoneinfo/Europe/Sarajevo | Bin 1957 -> 1948 bytes .../cctz/testdata/zoneinfo/Europe/Saratov | Bin 1197 -> 1183 bytes .../cctz/testdata/zoneinfo/Europe/Simferopol | Bin 1490 -> 1481 bytes .../cctz/testdata/zoneinfo/Europe/Skopje | Bin 1957 -> 1948 bytes .../cctz/testdata/zoneinfo/Europe/Sofia | Bin 2130 -> 2121 bytes .../cctz/testdata/zoneinfo/Europe/Stockholm | Bin 1918 -> 1909 bytes .../cctz/testdata/zoneinfo/Europe/Tallinn | Bin 2187 -> 2178 bytes .../cctz/testdata/zoneinfo/Europe/Tirane | Bin 2098 -> 2084 bytes .../cctz/testdata/zoneinfo/Europe/Tiraspol | Bin 2445 -> 2436 bytes .../cctz/testdata/zoneinfo/Europe/Ulyanovsk | Bin 1281 -> 1267 bytes .../cctz/testdata/zoneinfo/Europe/Uzhgorod | Bin 2103 -> 2094 bytes .../cctz/testdata/zoneinfo/Europe/Vaduz | Bin 1918 -> 1909 bytes .../cctz/testdata/zoneinfo/Europe/Vatican | Bin 2692 -> 2683 bytes .../cctz/testdata/zoneinfo/Europe/Vienna | Bin 2237 -> 2228 bytes .../cctz/testdata/zoneinfo/Europe/Vilnius | Bin 2199 -> 2190 bytes .../cctz/testdata/zoneinfo/Europe/Volgograd | Bin 1167 -> 1183 bytes .../cctz/testdata/zoneinfo/Europe/Warsaw | Bin 2705 -> 2696 bytes .../cctz/testdata/zoneinfo/Europe/Zagreb | Bin 1957 -> 1948 bytes .../cctz/testdata/zoneinfo/Europe/Zaporozhye | Bin 2115 -> 2106 bytes .../cctz/testdata/zoneinfo/Europe/Zurich | Bin 1918 -> 1909 bytes .../internal/cctz/testdata/zoneinfo/Factory | Bin 148 -> 120 bytes absl/time/internal/cctz/testdata/zoneinfo/GB | Bin 3687 -> 3678 bytes .../internal/cctz/testdata/zoneinfo/GB-Eire | Bin 3687 -> 3678 bytes absl/time/internal/cctz/testdata/zoneinfo/GMT | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/GMT+0 | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/GMT-0 | Bin 127 -> 118 bytes .../time/internal/cctz/testdata/zoneinfo/GMT0 | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Greenwich | Bin 127 -> 118 bytes absl/time/internal/cctz/testdata/zoneinfo/HST | Bin 128 -> 119 bytes .../internal/cctz/testdata/zoneinfo/Hongkong | Bin 1189 -> 1175 bytes .../internal/cctz/testdata/zoneinfo/Iceland | Bin 1188 -> 1174 bytes .../testdata/zoneinfo/Indian/Antananarivo | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Indian/Chagos | Bin 225 -> 211 bytes .../cctz/testdata/zoneinfo/Indian/Christmas | Bin 182 -> 173 bytes .../cctz/testdata/zoneinfo/Indian/Cocos | Bin 191 -> 182 bytes .../cctz/testdata/zoneinfo/Indian/Comoro | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Indian/Kerguelen | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Indian/Mahe | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Indian/Maldives | Bin 220 -> 211 bytes .../cctz/testdata/zoneinfo/Indian/Mauritius | Bin 267 -> 253 bytes .../cctz/testdata/zoneinfo/Indian/Mayotte | Bin 285 -> 271 bytes .../cctz/testdata/zoneinfo/Indian/Reunion | Bin 187 -> 173 bytes .../time/internal/cctz/testdata/zoneinfo/Iran | Bin 1718 -> 1704 bytes .../internal/cctz/testdata/zoneinfo/Israel | Bin 2265 -> 2256 bytes .../internal/cctz/testdata/zoneinfo/Jamaica | Bin 507 -> 498 bytes .../internal/cctz/testdata/zoneinfo/Japan | Bin 318 -> 309 bytes .../internal/cctz/testdata/zoneinfo/Kwajalein | Bin 259 -> 250 bytes .../internal/cctz/testdata/zoneinfo/Libya | Bin 655 -> 641 bytes absl/time/internal/cctz/testdata/zoneinfo/MET | Bin 2102 -> 2102 bytes absl/time/internal/cctz/testdata/zoneinfo/MST | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/MST7MDT | Bin 2294 -> 2294 bytes .../cctz/testdata/zoneinfo/Mexico/BajaNorte | Bin 2356 -> 2342 bytes .../cctz/testdata/zoneinfo/Mexico/BajaSur | Bin 1564 -> 1550 bytes .../cctz/testdata/zoneinfo/Mexico/General | Bin 1618 -> 1604 bytes absl/time/internal/cctz/testdata/zoneinfo/NZ | Bin 2460 -> 2451 bytes .../internal/cctz/testdata/zoneinfo/NZ-CHAT | Bin 2087 -> 2078 bytes .../internal/cctz/testdata/zoneinfo/Navajo | Bin 2453 -> 2444 bytes absl/time/internal/cctz/testdata/zoneinfo/PRC | Bin 414 -> 545 bytes .../internal/cctz/testdata/zoneinfo/PST8PDT | Bin 2294 -> 2294 bytes .../cctz/testdata/zoneinfo/Pacific/Apia | Bin 1134 -> 1125 bytes .../cctz/testdata/zoneinfo/Pacific/Auckland | Bin 2460 -> 2451 bytes .../testdata/zoneinfo/Pacific/Bougainville | Bin 296 -> 286 bytes .../cctz/testdata/zoneinfo/Pacific/Chatham | Bin 2087 -> 2078 bytes .../cctz/testdata/zoneinfo/Pacific/Chuuk | Bin 183 -> 174 bytes .../cctz/testdata/zoneinfo/Pacific/Easter | Bin 2242 -> 2233 bytes .../cctz/testdata/zoneinfo/Pacific/Efate | Bin 492 -> 478 bytes .../cctz/testdata/zoneinfo/Pacific/Enderbury | Bin 259 -> 250 bytes .../cctz/testdata/zoneinfo/Pacific/Fakaofo | Bin 221 -> 212 bytes .../cctz/testdata/zoneinfo/Pacific/Fiji | Bin 1104 -> 1090 bytes .../cctz/testdata/zoneinfo/Pacific/Funafuti | Bin 183 -> 174 bytes .../cctz/testdata/zoneinfo/Pacific/Galapagos | Bin 268 -> 254 bytes .../cctz/testdata/zoneinfo/Pacific/Gambier | Bin 186 -> 172 bytes .../testdata/zoneinfo/Pacific/Guadalcanal | Bin 188 -> 174 bytes .../cctz/testdata/zoneinfo/Pacific/Guam | Bin 225 -> 216 bytes .../cctz/testdata/zoneinfo/Pacific/Honolulu | Bin 276 -> 329 bytes .../cctz/testdata/zoneinfo/Pacific/Johnston | Bin 276 -> 329 bytes .../cctz/testdata/zoneinfo/Pacific/Kiritimati | Bin 263 -> 254 bytes .../cctz/testdata/zoneinfo/Pacific/Kosrae | Bin 251 -> 242 bytes .../cctz/testdata/zoneinfo/Pacific/Kwajalein | Bin 259 -> 250 bytes .../cctz/testdata/zoneinfo/Pacific/Majuro | Bin 221 -> 212 bytes .../cctz/testdata/zoneinfo/Pacific/Marquesas | Bin 195 -> 181 bytes .../cctz/testdata/zoneinfo/Pacific/Midway | Bin 196 -> 187 bytes .../cctz/testdata/zoneinfo/Pacific/Nauru | Bin 282 -> 268 bytes .../cctz/testdata/zoneinfo/Pacific/Niue | Bin 266 -> 257 bytes .../cctz/testdata/zoneinfo/Pacific/Norfolk | Bin 323 -> 314 bytes .../cctz/testdata/zoneinfo/Pacific/Noumea | Bin 328 -> 314 bytes .../cctz/testdata/zoneinfo/Pacific/Pago_Pago | Bin 196 -> 187 bytes .../cctz/testdata/zoneinfo/Pacific/Palau | Bin 182 -> 173 bytes .../cctz/testdata/zoneinfo/Pacific/Pitcairn | Bin 223 -> 214 bytes .../cctz/testdata/zoneinfo/Pacific/Pohnpei | Bin 183 -> 174 bytes .../cctz/testdata/zoneinfo/Pacific/Ponape | Bin 183 -> 174 bytes .../testdata/zoneinfo/Pacific/Port_Moresby | Bin 206 -> 196 bytes .../cctz/testdata/zoneinfo/Pacific/Rarotonga | Bin 602 -> 593 bytes .../cctz/testdata/zoneinfo/Pacific/Saipan | Bin 225 -> 216 bytes .../cctz/testdata/zoneinfo/Pacific/Samoa | Bin 196 -> 187 bytes .../cctz/testdata/zoneinfo/Pacific/Tahiti | Bin 187 -> 173 bytes .../cctz/testdata/zoneinfo/Pacific/Tarawa | Bin 183 -> 174 bytes .../cctz/testdata/zoneinfo/Pacific/Tongatapu | Bin 393 -> 384 bytes .../cctz/testdata/zoneinfo/Pacific/Truk | Bin 183 -> 174 bytes .../cctz/testdata/zoneinfo/Pacific/Wake | Bin 183 -> 174 bytes .../cctz/testdata/zoneinfo/Pacific/Wallis | Bin 183 -> 174 bytes .../cctz/testdata/zoneinfo/Pacific/Yap | Bin 183 -> 174 bytes .../internal/cctz/testdata/zoneinfo/Poland | Bin 2705 -> 2696 bytes .../internal/cctz/testdata/zoneinfo/Portugal | Bin 3469 -> 3469 bytes absl/time/internal/cctz/testdata/zoneinfo/ROC | Bin 790 -> 781 bytes absl/time/internal/cctz/testdata/zoneinfo/ROK | Bin 531 -> 517 bytes .../internal/cctz/testdata/zoneinfo/Singapore | Bin 424 -> 415 bytes .../internal/cctz/testdata/zoneinfo/Turkey | Bin 2166 -> 2157 bytes absl/time/internal/cctz/testdata/zoneinfo/UCT | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/US/Alaska | Bin 2380 -> 2371 bytes .../cctz/testdata/zoneinfo/US/Aleutian | Bin 2365 -> 2356 bytes .../cctz/testdata/zoneinfo/US/Arizona | Bin 353 -> 344 bytes .../cctz/testdata/zoneinfo/US/Central | Bin 3585 -> 3576 bytes .../cctz/testdata/zoneinfo/US/East-Indiana | Bin 1675 -> 1666 bytes .../cctz/testdata/zoneinfo/US/Eastern | Bin 3545 -> 3536 bytes .../internal/cctz/testdata/zoneinfo/US/Hawaii | Bin 276 -> 329 bytes .../cctz/testdata/zoneinfo/US/Indiana-Starke | Bin 2437 -> 2428 bytes .../cctz/testdata/zoneinfo/US/Michigan | Bin 2188 -> 2174 bytes .../cctz/testdata/zoneinfo/US/Mountain | Bin 2453 -> 2444 bytes .../cctz/testdata/zoneinfo/US/Pacific | Bin 2845 -> 2836 bytes .../internal/cctz/testdata/zoneinfo/US/Samoa | Bin 196 -> 187 bytes absl/time/internal/cctz/testdata/zoneinfo/UTC | Bin 127 -> 118 bytes .../internal/cctz/testdata/zoneinfo/Universal | Bin 127 -> 118 bytes .../time/internal/cctz/testdata/zoneinfo/W-SU | Bin 1544 -> 1535 bytes absl/time/internal/cctz/testdata/zoneinfo/WET | Bin 1873 -> 1873 bytes .../time/internal/cctz/testdata/zoneinfo/Zulu | Bin 127 -> 118 bytes .../cctz/testdata/zoneinfo/zone1970.tab | 34 +- .../time/internal/get_current_time_chrono.inc | 31 + absl/time/internal/get_current_time_ios.inc | 82 - absl/time/internal/get_current_time_posix.inc | 4 +- .../internal/get_current_time_windows.inc | 19 - absl/time/internal/test_util.cc | 14 +- absl/time/internal/test_util.h | 28 +- absl/time/time.cc | 338 ++- absl/time/time.h | 843 ++++--- absl/time/time_benchmark.cc | 38 +- absl/time/time_norm_test.cc | 306 --- absl/time/time_test.cc | 393 ++-- absl/time/time_zone_test.cc | 2 +- absl/types/BUILD.bazel | 21 +- absl/types/CMakeLists.txt | 10 +- absl/types/any.h | 8 +- absl/types/any_exception_safety_test.cc | 8 +- absl/types/bad_any_cast.cc | 8 +- absl/types/bad_any_cast.h | 20 +- absl/types/bad_optional_access.cc | 8 +- absl/types/bad_optional_access.h | 20 +- absl/types/bad_variant_access.cc | 8 +- absl/types/bad_variant_access.h | 20 +- absl/types/internal/variant.h | 88 +- absl/types/optional.cc | 4 +- absl/types/optional.h | 25 +- absl/types/optional_exception_safety_test.cc | 30 +- absl/types/optional_test.cc | 33 +- absl/types/span.h | 55 +- absl/types/span_test.cc | 3 +- absl/types/variant.h | 76 +- absl/types/variant_benchmark.cc | 4 +- absl/types/variant_exception_safety_test.cc | 108 +- absl/types/variant_test.cc | 25 +- absl/utility/utility.h | 10 +- 963 files changed, 35672 insertions(+), 4925 deletions(-) create mode 100644 CMake/AbseilConfigureCopts.cmake create mode 100644 absl/base/internal/bits.h create mode 100644 absl/base/internal/bits_test.cc create mode 100644 absl/base/internal/spinlock_benchmark.cc create mode 100644 absl/base/internal/spinlock_linux.inc create mode 100644 absl/compiler_config_setting.bzl create mode 100644 absl/container/fixed_array_exception_safety_test.cc create mode 100644 absl/container/flat_hash_map.h create mode 100644 absl/container/flat_hash_map_test.cc create mode 100644 absl/container/flat_hash_set.h create mode 100644 absl/container/flat_hash_set_test.cc create mode 100644 absl/container/internal/compressed_tuple.h create mode 100644 absl/container/internal/compressed_tuple_test.cc create mode 100644 absl/container/internal/container_memory.h create mode 100644 absl/container/internal/container_memory_test.cc create mode 100644 absl/container/internal/hash_function_defaults.h create mode 100644 absl/container/internal/hash_function_defaults_test.cc create mode 100644 absl/container/internal/hash_generator_testing.cc create mode 100644 absl/container/internal/hash_generator_testing.h create mode 100644 absl/container/internal/hash_policy_testing.h create mode 100644 absl/container/internal/hash_policy_testing_test.cc create mode 100644 absl/container/internal/hash_policy_traits.h create mode 100644 absl/container/internal/hash_policy_traits_test.cc create mode 100644 absl/container/internal/hashtable_debug.h create mode 100644 absl/container/internal/hashtable_debug_hooks.h create mode 100644 absl/container/internal/layout.h create mode 100644 absl/container/internal/layout_test.cc create mode 100644 absl/container/internal/node_hash_policy.h create mode 100644 absl/container/internal/node_hash_policy_test.cc create mode 100644 absl/container/internal/raw_hash_map.h create mode 100644 absl/container/internal/raw_hash_set.cc create mode 100644 absl/container/internal/raw_hash_set.h create mode 100644 absl/container/internal/raw_hash_set_allocator_test.cc create mode 100644 absl/container/internal/raw_hash_set_test.cc create mode 100644 absl/container/internal/tracked.h create mode 100644 absl/container/internal/unordered_map_constructor_test.h create mode 100644 absl/container/internal/unordered_map_lookup_test.h create mode 100644 absl/container/internal/unordered_map_modifiers_test.h create mode 100644 absl/container/internal/unordered_map_test.cc create mode 100644 absl/container/internal/unordered_set_constructor_test.h create mode 100644 absl/container/internal/unordered_set_lookup_test.h create mode 100644 absl/container/internal/unordered_set_modifiers_test.h create mode 100644 absl/container/internal/unordered_set_test.cc create mode 100644 absl/container/node_hash_map.h create mode 100644 absl/container/node_hash_map_test.cc create mode 100644 absl/container/node_hash_set.h create mode 100644 absl/container/node_hash_set_test.cc create mode 100644 absl/hash/BUILD.bazel create mode 100644 absl/hash/CMakeLists.txt create mode 100644 absl/hash/hash.h create mode 100644 absl/hash/hash_test.cc create mode 100644 absl/hash/hash_testing.h create mode 100644 absl/hash/internal/city.cc create mode 100644 absl/hash/internal/city.h create mode 100644 absl/hash/internal/city_test.cc create mode 100644 absl/hash/internal/hash.cc create mode 100644 absl/hash/internal/hash.h create mode 100644 absl/hash/internal/print_hash_of.cc create mode 100644 absl/hash/internal/spy_hash_state.h create mode 100644 absl/strings/ascii_benchmark.cc delete mode 100644 absl/strings/internal/bits.h create mode 100644 absl/strings/internal/pow10_helper.cc create mode 100644 absl/strings/internal/pow10_helper.h create mode 100644 absl/strings/internal/pow10_helper_test.cc create mode 100644 absl/strings/internal/str_format/arg.cc create mode 100644 absl/strings/internal/str_format/arg.h create mode 100644 absl/strings/internal/str_format/arg_test.cc create mode 100644 absl/strings/internal/str_format/bind.cc create mode 100644 absl/strings/internal/str_format/bind.h create mode 100644 absl/strings/internal/str_format/bind_test.cc create mode 100644 absl/strings/internal/str_format/checker.h create mode 100644 absl/strings/internal/str_format/checker_test.cc create mode 100644 absl/strings/internal/str_format/convert_test.cc create mode 100644 absl/strings/internal/str_format/extension.cc create mode 100644 absl/strings/internal/str_format/extension.h create mode 100644 absl/strings/internal/str_format/extension_test.cc create mode 100644 absl/strings/internal/str_format/float_conversion.cc create mode 100644 absl/strings/internal/str_format/float_conversion.h create mode 100644 absl/strings/internal/str_format/output.cc create mode 100644 absl/strings/internal/str_format/output.h create mode 100644 absl/strings/internal/str_format/output_test.cc create mode 100644 absl/strings/internal/str_format/parser.cc create mode 100644 absl/strings/internal/str_format/parser.h create mode 100644 absl/strings/internal/str_format/parser_test.cc create mode 100644 absl/strings/numbers_benchmark.cc create mode 100644 absl/strings/str_format.h create mode 100644 absl/strings/str_format_test.cc create mode 100644 absl/time/civil_time.cc create mode 100644 absl/time/civil_time.h create mode 100644 absl/time/civil_time_benchmark.cc create mode 100644 absl/time/civil_time_test.cc create mode 100644 absl/time/internal/get_current_time_chrono.inc delete mode 100644 absl/time/internal/get_current_time_ios.inc delete mode 100644 absl/time/internal/get_current_time_windows.inc delete mode 100644 absl/time/time_norm_test.cc diff --git a/.gitignore b/.gitignore index 886f4432f39..7175c4f89dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,12 @@ # Ignore all bazel-* symlinks. /bazel-* +# Ignore Bazel verbose explanations +--verbose_explanations +# Ignore CMake usual build directory +build +# Ignore Vim files +*.swp +# Ignore QtCreator Project file +CMakeLists.txt.user +# Ignore VS Code files +.vscode/* diff --git a/CMake/AbseilConfigureCopts.cmake b/CMake/AbseilConfigureCopts.cmake new file mode 100644 index 00000000000..96e0390b9dc --- /dev/null +++ b/CMake/AbseilConfigureCopts.cmake @@ -0,0 +1,145 @@ +# 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 e4eafe49e47..5402bf51051 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -15,6 +15,7 @@ # include(CMakeParseArguments) +include(AbseilConfigureCopts) # The IDE folder for Abseil that will be used if Abseil is included in a CMake # project that sets @@ -48,7 +49,11 @@ function(absl_library) add_library(${_NAME} STATIC ${ABSL_LIB_SOURCES}) - target_compile_options(${_NAME} PRIVATE ${ABSL_COMPILE_CXXFLAGS} ${ABSL_LIB_PRIVATE_COMPILE_FLAGS}) + 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} @@ -57,12 +62,199 @@ function(absl_library) # 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: +# NAME: name of target (see Note) +# HDRS: List of public header files for the library +# SRCS: List of source files for the library +# DEPS: List of other libraries to be linked in to the binary targets +# COPTS: List of private compile options +# DEFINES: List of public defines +# LINKOPTS: List of link options +# PUBLIC: Add this so that this library will be exported under absl:: (see Note). +# 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}. +# This is to reduce namespace pollution. +# +# absl_cc_library( +# NAME +# awesome +# HDRS +# "a.h" +# SRCS +# "a.cc" +# ) +# absl_cc_library( +# NAME +# fantastic_lib +# SRCS +# "b.cc" +# DEPS +# absl_internal_awesome # not "awesome"! +# ) +# +# 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 +# ) +# +# User can then use the library as absl::main_lib (although absl_main_lib is defined too). +# +# TODO: Implement "ALWAYSLINK" +function(absl_cc_library) + cmake_parse_arguments(ABSL_CC_LIB + "DISABLE_INSTALL;PUBLIC;TESTONLY" + "NAME" + "HDRS;SRCS;COPTS;DEFINES;LINKOPTS;DEPS" + ${ARGN} + ) + + if (NOT ABSL_CC_LIB_TESTONLY OR ABSL_RUN_TESTS) + if (ABSL_CC_LIB_PUBLIC) + set(_NAME "absl_${ABSL_CC_LIB_NAME}") + else() + set(_NAME "absl_internal_${ABSL_CC_LIB_NAME}") + endif() + + # Check if this is a header-only library + if ("${ABSL_CC_LIB_SRCS}" STREQUAL "") + set(ABSL_CC_LIB_IS_INTERFACE 1) + else() + set(ABSL_CC_LIB_IS_INTERFACE 0) + endif() + + if(NOT ABSL_CC_LIB_IS_INTERFACE) + 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}) + target_compile_options(${_NAME} + PRIVATE ${ABSL_CC_LIB_COPTS}) + target_link_libraries(${_NAME} + PUBLIC ${ABSL_CC_LIB_DEPS} + PRIVATE ${ABSL_CC_LIB_LINKOPTS} + ) + target_compile_definitions(${_NAME} PUBLIC ${ABSL_CC_LIB_DEFINES}) + + # Add all Abseil targets to a a folder in the IDE for organization. + if(ABSL_CC_LIB_PUBLIC) + set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}) + elseif(ABSL_CC_LIB_TESTONLY) + set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test) + else() + set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/internal) + endif() + + # INTERFACE libraries can't have the CXX_STANDARD property set + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD ${ABSL_CXX_STANDARD}) + set_property(TARGET ${_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) + else() + # Generating header-only library + add_library(${_NAME} INTERFACE) + target_include_directories(${_NAME} + INTERFACE ${ABSL_COMMON_INCLUDE_DIRS}) + target_link_libraries(${_NAME} + INTERFACE ${ABSL_CC_LIB_DEPS} ${ABSL_CC_LIB_LINKOPTS} + ) + target_compile_definitions(${_NAME} INTERFACE ${ABSL_CC_LIB_DEFINES}) + endif() + + add_library(absl::${ABSL_CC_LIB_NAME} ALIAS ${_NAME}) + endif() +endfunction() + +# absl_cc_test() +# +# CMake function to imitate Bazel's cc_test rule. +# +# Parameters: +# NAME: name of target (see Usage below) +# SRCS: List of source files for the binary +# DEPS: List of other libraries to be linked in to the binary targets +# COPTS: List of private compile options +# DEFINES: List of public defines +# LINKOPTS: List of link options +# +# Note: +# By default, absl_cc_test will always create a binary named absl_${NAME}. +# This will also add it to ctest list as absl_${NAME}. +# +# Usage: +# absl_cc_library( +# NAME +# awesome +# HDRS +# "a.h" +# SRCS +# "a.cc" +# PUBLIC +# ) +# +# absl_cc_test( +# NAME +# awesome_test +# SRCS +# "awesome_test.cc" +# DEPS +# absl::awesome +# gmock +# gtest_main +# ) +function(absl_cc_test) + if(NOT ABSL_RUN_TESTS) + return() + endif() + + cmake_parse_arguments(ABSL_CC_TEST + "" + "NAME" + "SRCS;COPTS;DEFINES;LINKOPTS;DEPS" + ${ARGN} + ) + set(_NAME "absl_${ABSL_CC_TEST_NAME}") + add_executable(${_NAME} "") + target_sources(${_NAME} PRIVATE ${ABSL_CC_TEST_SRCS}) + target_include_directories(${_NAME} + PUBLIC ${ABSL_COMMON_INCLUDE_DIRS} + PRIVATE ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} + ) + target_compile_definitions(${_NAME} + PUBLIC ${ABSL_CC_TEST_DEFINES} + ) + target_compile_options(${_NAME} + PRIVATE ${ABSL_CC_TEST_COPTS} + ) + target_link_libraries(${_NAME} + PUBLIC ${ABSL_CC_TEST_DEPS} + PRIVATE ${ABSL_CC_TEST_LINKOPTS} + ) + # Add all Abseil targets to a a folder in the IDE for organization. + set_property(TARGET ${_NAME} PROPERTY FOLDER ${ABSL_IDE_FOLDER}/test) + + 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}) +endfunction() # # header only virtual target creation @@ -103,13 +295,15 @@ function(absl_header_library) # 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 # @@ -123,7 +317,7 @@ endfunction() # # all tests will be register for execution with add_test() # -# test compilation and execution is disable when BUILD_TESTING=OFF +# test compilation and execution is disable when ABSL_RUN_TESTS=OFF # function(absl_test) @@ -135,25 +329,32 @@ function(absl_test) ) - if(BUILD_TESTING) + if(ABSL_RUN_TESTS) - set(_NAME ${ABSL_TEST_TARGET}) + set(_NAME "absl_${ABSL_TEST_TARGET}") string(TOUPPER ${_NAME} _UPPER_NAME) - add_executable(${_NAME}_bin ${ABSL_TEST_SOURCES}) + add_executable(${_NAME} ${ABSL_TEST_SOURCES}) - target_compile_options(${_NAME}_bin PRIVATE ${ABSL_COMPILE_CXXFLAGS} ${ABSL_TEST_PRIVATE_COMPILE_FLAGS}) - target_link_libraries(${_NAME}_bin PUBLIC ${ABSL_TEST_PUBLIC_LIBRARIES} ${ABSL_TEST_COMMON_LIBRARIES}) - target_include_directories(${_NAME}_bin + 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}_bin PROPERTY FOLDER ${ABSL_IDE_FOLDER}) + 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}_bin) - endif(BUILD_TESTING) + add_test(NAME ${_NAME} COMMAND ${_NAME}) + endif(ABSL_RUN_TESTS) endfunction() diff --git a/CMake/DownloadGTest.cmake b/CMake/DownloadGTest.cmake index 9d4132158b8..3c682aef07f 100644 --- a/CMake/DownloadGTest.cmake +++ b/CMake/DownloadGTest.cmake @@ -4,7 +4,7 @@ # Download the latest googletest from Github master configure_file( ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in - googletest-download/CMakeLists.txt + ${CMAKE_BINARY_DIR}/googletest-download/CMakeLists.txt ) # Configure and build the downloaded googletest source diff --git a/CMakeLists.txt b/CMakeLists.txt index 89a3386f7e2..1eafa4078a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,9 +17,15 @@ # 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) + +# Compiler id for Apple Clang is now AppleClang. +if (POLICY CMP0025) + cmake_policy(SET CMP0025 NEW) +endif() + project(absl) -list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake) include(GNUInstallDirs) include(AbseilHelpers) @@ -32,8 +38,16 @@ if (MSVC) # /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 /WX /wd4005 /wd4068 /wd4244 /wd4267 /wd4800) - add_definitions(/DNOMINMAX /DWIN32_LEAN_AND_MEAN=1 /D_CRT_SECURE_NO_WARNINGS /D_SCL_SECURE_NO_WARNINGS) + 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 + ) else() set(ABSL_STD_CXX_FLAG "-std=c++11" CACHE STRING "c++ std flag (default: c++11)") endif() @@ -60,6 +74,12 @@ 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() + set(ABSL_USING_CLANG OFF) +endif() + # find dependencies ## pthread find_package(Threads REQUIRED) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 40351ddcfaa..f4cb4a29ed0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,6 +18,53 @@ You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to do it again. +## Contribution Guidelines + +Potential contributors sometimes ask us if the Abseil project is the appropriate +home for their utility library code or for specific functions implementing +missing portions of the standard. Often, the answer to this question is "no". +We’d like to articulate our thinking on this issue so that our choices can be +understood by everyone and so that contributors can have a better intuition +about whether Abseil might be interested in adopting a new library. + +### Priorities + +Although our mission is to augment the C++ standard library, our goal is not to +provide a full forward-compatible implementation of the latest standard. For us +to consider a library for inclusion in Abseil, it is not enough that a library +is useful. We generally choose to release a library when it meets at least one +of the following criteria: + +* **Widespread usage** - Using our internal codebase to help gauge usage, most + of the libraries we've released have tens of thousands of users. +* **Anticipated widespread usage** - Pre-adoption of some standard-compliant + APIs may not have broad adoption initially but can be expected to pick up + usage when it replaces legacy APIs. `absl::from_chars`, for example, + replaces existing code that converts strings to numbers and will therefore + likely see usage growth. +* **High impact** - APIs that provide a key solution to a specific problem, + such as `absl::FixedArray`, have higher impact than usage numbers may signal + and are released because of their importance. +* **Direct support for a library that falls under one of the above** - When we + want access to a smaller library as an implementation detail for a + higher-priority library we plan to release, we may release it, as we did + with portions of `absl/meta/type_traits.h`. One consequence of this is that + the presence of a library in Abseil does not necessarily mean that other + similar libraries would be a high priority. + +### API Freeze Consequences + +Via the +[Abseil Compatibility Guidelines](https://abseil.io/about/compatibility), we +have promised a large degree of API stability. In particular, we will not make +backward-incompatible changes to released APIs without also shipping a tool or +process that can upgrade our users' code. We are not yet at the point of easily +releasing such tools. Therefore, at this time, shipping a library establishes an +API contract which is borderline unchangeable. (We can add new functionality, +but we cannot easily change existing behavior.) This constraint forces us to +very carefully review all APIs that we ship. + + ## Coding Style To keep the source consistent, readable, diffable and easy to merge, we use a diff --git a/README.md b/README.md index 8eed5751bf5..e9362be2d31 100644 --- a/README.md +++ b/README.md @@ -63,10 +63,14 @@ Abseil contains the following C++ library components:
The `algorithm` library contains additions to the C++ `` library and container-based versions of such algorithms. * [`container`](absl/container/) -
The `container` library contains additional STL-style containers. +
The `container` library contains additional STL-style containers, + including Abseil's unordered "Swiss table" containers. * [`debugging`](absl/debugging/)
The `debugging` library contains code useful for enabling leak - checks. Future updates will add stacktrace and symbolization utilities. + checks, and stacktrace and symbolization utilities. +* [`hash`](absl/hash/) +
The `hash` library contains the hashing framework and default hash + functor implementations for hashable types in Abseil. * [`memory`](absl/memory/)
The `memory` library contains C++11-compatible versions of `std::make_unique()` and related memory management facilities. @@ -90,6 +94,8 @@ Abseil contains the following C++ library components: * [`types`](absl/types/)
The `types` library contains non-container utility types, like a C++11-compatible version of the C++17 `std::optional` type. +* [`utility`](absl/utility/) +
The `utility` library contains utility and helper code. ## License diff --git a/WORKSPACE b/WORKSPACE index a7b1c139290..72ef13980c2 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,21 +1,23 @@ workspace(name = "com_google_absl") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + # Bazel toolchains http_archive( name = "bazel_toolchains", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/287b64e0a211fb7c23b74695f8d5f5205b61f4eb.tar.gz", - "https://github.com/bazelbuild/bazel-toolchains/archive/287b64e0a211fb7c23b74695f8d5f5205b61f4eb.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/bc09b995c137df042bb80a395b73d7ce6f26afbe.tar.gz", + "https://github.com/bazelbuild/bazel-toolchains/archive/bc09b995c137df042bb80a395b73d7ce6f26afbe.tar.gz", ], - strip_prefix = "bazel-toolchains-287b64e0a211fb7c23b74695f8d5f5205b61f4eb", - sha256 = "aca8ac6afd7745027ee4a43032b51a725a61a75a30f02cc58681ee87e4dcdf4b", + strip_prefix = "bazel-toolchains-bc09b995c137df042bb80a395b73d7ce6f26afbe", + sha256 = "4329663fe6c523425ad4d3c989a8ac026b04e1acedeceb56aa4b190fa7f3973c", ) # GoogleTest/GoogleMock framework. Used by most unit-tests. http_archive( name = "com_google_googletest", - urls = ["https://github.com/google/googletest/archive/4e4df226fc197c0dda6e37f5c8c3845ca1e73a49.zip"], - strip_prefix = "googletest-4e4df226fc197c0dda6e37f5c8c3845ca1e73a49", - sha256 = "d4179caf54410968d1fff0b869e7d74803dd30209ee6645ccf1ca65ab6cf5e5a", + urls = ["https://github.com/google/googletest/archive/b4d4438df9479675a632b2f11125e57133822ece.zip"], # 2018-07-16 + strip_prefix = "googletest-b4d4438df9479675a632b2f11125e57133822ece", + sha256 = "5aaa5d566517cae711e2a3505ea9a6438be1b37fcaae0ebcb96ccba9aa56f23a", ) # Google benchmark. @@ -25,11 +27,3 @@ http_archive( strip_prefix = "benchmark-16703ff83c1ae6d53e5155df3bb3ab0bc96083be", sha256 = "59f918c8ccd4d74b6ac43484467b500f1d64b40cc1010daa055375b322a43ba3", ) - -# RE2 regular-expression framework. Used by some unit-tests. -http_archive( - name = "com_googlesource_code_re2", - urls = ["https://github.com/google/re2/archive/6cf8ccd82dbaab2668e9b13596c68183c9ecd13f.zip"], - strip_prefix = "re2-6cf8ccd82dbaab2668e9b13596c68183c9ecd13f", - sha256 = "279a852219dbfc504501775596089d30e9c0b29664ce4128b0ac4c841471a16a", -) diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index 439addbfc82..edd0274c5d8 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -18,11 +18,10 @@ package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 -config_setting( +load(":compiler_config_setting.bzl", "create_llvm_config") + +create_llvm_config( name = "llvm_compiler", - values = { - "compiler": "llvm", - }, visibility = [":__subpackages__"], ) diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt index 689f64e258b..1d09b1935d8 100644 --- a/absl/CMakeLists.txt +++ b/absl/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(base) add_subdirectory(algorithm) add_subdirectory(container) add_subdirectory(debugging) +add_subdirectory(hash) add_subdirectory(memory) add_subdirectory(meta) add_subdirectory(numeric) diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt index fdf45c55ed6..87a165c06a3 100644 --- a/absl/algorithm/CMakeLists.txt +++ b/absl/algorithm/CMakeLists.txt @@ -14,50 +14,50 @@ # limitations under the License. # -list(APPEND ALGORITHM_PUBLIC_HEADERS - "algorithm.h" - "container.h" -) - - -# -## TESTS -# - -# test algorithm_test -list(APPEND ALGORITHM_TEST_SRC - "algorithm_test.cc" - ${ALGORITHM_PUBLIC_HEADERS} - ${ALGORITHM_INTERNAL_HEADERS} -) - -absl_header_library( - TARGET - absl_algorithm - EXPORT_NAME +absl_cc_library( + NAME algorithm + HDRS + "algorithm.h" + COPTS + ${ABSL_DEFAULT_COPTS} + PUBLIC ) -absl_test( - TARGET +absl_cc_test( + NAME algorithm_test - SOURCES - ${ALGORITHM_TEST_SRC} - PUBLIC_LIBRARIES + SRCS + "algorithm_test.cc" + DEPS absl::algorithm + gmock_main ) +absl_cc_library( + NAME + algorithm_container + HDRS + "container.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::algorithm + absl::core_headers + absl::meta + PUBLIC +) - - -# test container_test -set(CONTAINER_TEST_SRC "container_test.cc") - -absl_test( - TARGET +absl_cc_test( + NAME container_test - SOURCES - ${CONTAINER_TEST_SRC} - PUBLIC_LIBRARIES - absl::algorithm + SRCS + "container_test.cc" + DEPS + absl::algorithm_container + absl::base + absl::core_headers + absl::memory + absl::span + gmock_main ) diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h index 3a8f2724210..1eef16cbac9 100644 --- a/absl/algorithm/algorithm.h +++ b/absl/algorithm/algorithm.h @@ -27,7 +27,7 @@ #include namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace algorithm_internal { @@ -146,7 +146,7 @@ ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator>()); } -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_ALGORITHM_ALGORITHM_H_ diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index 8eb10d7a143..b7718206898 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include #include @@ -54,8 +56,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_06_20 { - +inline namespace lts_2018_12_18 { namespace container_algorithm_internal { // NOTE: it is important to defer to ADL lookup for building with C++ modules, @@ -102,6 +103,17 @@ ContainerIter c_begin(C& c) { return begin(c); } template ContainerIter c_end(C& c) { return end(c); } +template +struct IsUnorderedContainer : std::false_type {}; + +template +struct IsUnorderedContainer< + std::unordered_map> : std::true_type {}; + +template +struct IsUnorderedContainer> + : std::true_type {}; + } // namespace container_algorithm_internal // PUBLIC API @@ -315,7 +327,7 @@ container_algorithm_internal::ContainerDifferenceType c_count_if( // c_mismatch() // -// Container-based version of the `std::mismatchf()` function to +// Container-based version of the `std::mismatch()` function to // return the first element where two ordered containers differ. template container_algorithm_internal::ContainerIterPairType @@ -495,7 +507,7 @@ BidirectionalIterator c_copy_backward(const C& src, // Container-based version of the `std::move()` function to move // a container's elements into an iterator. template -OutputIterator c_move(C& src, OutputIterator dest) { +OutputIterator c_move(C&& src, OutputIterator dest) { return std::move(container_algorithm_internal::c_begin(src), container_algorithm_internal::c_end(src), dest); } @@ -635,7 +647,7 @@ container_algorithm_internal::ContainerIter c_generate_n(C& c, Size n, // Note: `c_xx()` container versions for `remove()`, `remove_if()`, // and `unique()` are omitted, because it's not clear whether or not such -// functions should call erase their supplied sequences afterwards. Either +// functions should call erase on their supplied sequences afterwards. Either // behavior would be surprising for a different set of users. // @@ -1155,7 +1167,13 @@ bool c_includes(const C1& c1, const C2& c2, Compare&& comp) { // Container-based version of the `std::set_union()` function // to return an iterator containing the union of two containers; duplicate // values are not copied into the output. -template +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { return std::set_union(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), @@ -1165,7 +1183,13 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output) { // Overload of c_set_union() for performing a merge using a `comp` other than // `operator<`. -template +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, Compare&& comp) { return std::set_union(container_algorithm_internal::c_begin(c1), @@ -1179,7 +1203,13 @@ OutputIterator c_set_union(const C1& c1, const C2& c2, OutputIterator output, // // Container-based version of the `std::set_intersection()` function // to return an iterator containing the intersection of two containers. -template +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, OutputIterator output) { return std::set_intersection(container_algorithm_internal::c_begin(c1), @@ -1190,7 +1220,13 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2, // Overload of c_set_intersection() for performing a merge using a `comp` other // than `operator<`. -template +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> OutputIterator c_set_intersection(const C1& c1, const C2& c2, OutputIterator output, Compare&& comp) { return std::set_intersection(container_algorithm_internal::c_begin(c1), @@ -1205,7 +1241,13 @@ OutputIterator c_set_intersection(const C1& c1, const C2& c2, // Container-based version of the `std::set_difference()` function // to return an iterator containing elements present in the first container but // not in the second. -template +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> OutputIterator c_set_difference(const C1& c1, const C2& c2, OutputIterator output) { return std::set_difference(container_algorithm_internal::c_begin(c1), @@ -1216,7 +1258,13 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2, // Overload of c_set_difference() for performing a merge using a `comp` other // than `operator<`. -template +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> OutputIterator c_set_difference(const C1& c1, const C2& c2, OutputIterator output, Compare&& comp) { return std::set_difference(container_algorithm_internal::c_begin(c1), @@ -1231,7 +1279,13 @@ OutputIterator c_set_difference(const C1& c1, const C2& c2, // Container-based version of the `std::set_symmetric_difference()` // function to return an iterator containing elements present in either one // container or the other, but not both. -template +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, OutputIterator output) { return std::set_symmetric_difference( @@ -1243,7 +1297,13 @@ OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, // Overload of c_set_symmetric_difference() for performing a merge using a // `comp` other than `operator<`. -template +template ::value, + void>::type, + typename = typename std::enable_if< + !container_algorithm_internal::IsUnorderedContainer::value, + void>::type> OutputIterator c_set_symmetric_difference(const C1& c1, const C2& c2, OutputIterator output, Compare&& comp) { @@ -1638,7 +1698,7 @@ OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, output_first, std::forward(op)); } -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_ALGORITHM_CONTAINER_H_ diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc index de66f14680f..1502b17f863 100644 --- a/absl/algorithm/container_test.cc +++ b/absl/algorithm/container_test.cc @@ -636,6 +636,21 @@ TEST(MutatingTest, Move) { Pointee(5))); } +TEST(MutatingTest, MoveWithRvalue) { + auto MakeRValueSrc = [] { + std::vector> src; + src.emplace_back(absl::make_unique(1)); + src.emplace_back(absl::make_unique(2)); + src.emplace_back(absl::make_unique(3)); + return src; + }; + + std::vector> dest = MakeRValueSrc(); + absl::c_move(MakeRValueSrc(), std::back_inserter(dest)); + EXPECT_THAT(dest, ElementsAre(Pointee(1), Pointee(2), Pointee(3), Pointee(1), + Pointee(2), Pointee(3))); +} + TEST(MutatingTest, SwapRanges) { std::vector odds = {2, 4, 6}; std::vector evens = {1, 3, 5}; diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index d117a4fea86..4566c697cfd 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -19,6 +19,7 @@ load( "ABSL_DEFAULT_COPTS", "ABSL_TEST_COPTS", "ABSL_EXCEPTIONS_FLAG", + "ABSL_EXCEPTIONS_FLAG_LINKOPTS", ) package(default_visibility = ["//visibility:public"]) @@ -29,6 +30,7 @@ cc_library( name = "spinlock_wait", srcs = [ "internal/spinlock_akaros.inc", + "internal/spinlock_linux.inc", "internal/spinlock_posix.inc", "internal/spinlock_wait.cc", "internal/spinlock_win32.inc", @@ -73,7 +75,6 @@ cc_library( copts = ABSL_DEFAULT_COPTS, deps = [ ":config", - ":dynamic_annotations", ], ) @@ -106,6 +107,7 @@ cc_library( "internal/identity.h", "internal/inline_variable.h", "internal/invoke.h", + "internal/scheduling_mode.h", ], copts = ABSL_DEFAULT_COPTS, visibility = [ @@ -179,13 +181,13 @@ cc_library( srcs = ["internal/throw_delegate.cc"], hdrs = ["internal/throw_delegate.h"], copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, visibility = [ "//absl:__subpackages__", ], deps = [ ":base", ":config", - ":core_headers", ], ) @@ -193,6 +195,7 @@ cc_test( name = "throw_delegate_test", srcs = ["throw_delegate_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, deps = [ ":throw_delegate", "@com_google_googletest//:gtest_main", @@ -225,6 +228,7 @@ 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, deps = [ ":base", ":config", @@ -232,7 +236,7 @@ cc_library( "//absl/memory", "//absl/meta:type_traits", "//absl/strings", - "//absl/types:optional", + "//absl/utility", "@com_google_googletest//:gtest", ], ) @@ -241,6 +245,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, deps = [ ":exception_safety_testing", "//absl/memory", @@ -299,6 +304,7 @@ cc_test( size = "medium", srcs = ["spinlock_test_common.cc"], copts = ABSL_TEST_COPTS, + tags = ["no_test_wasm"], deps = [ ":base", ":core_headers", @@ -308,6 +314,33 @@ cc_test( ], ) +cc_library( + name = "spinlock_benchmark_common", + testonly = 1, + srcs = ["internal/spinlock_benchmark.cc"], + copts = ABSL_DEFAULT_COPTS, + visibility = [ + "//absl/base:__pkg__", + ], + deps = [ + ":base", + ":base_internal", + "//absl/synchronization", + "@com_github_google_benchmark//:benchmark_main", + ], + alwayslink = 1, +) + +cc_binary( + name = "spinlock_benchmark", + testonly = 1, + copts = ABSL_DEFAULT_COPTS, + visibility = ["//visibility:private"], + deps = [ + ":spinlock_benchmark_common", + ], +) + cc_library( name = "endian", hdrs = [ @@ -337,6 +370,9 @@ cc_test( name = "config_test", srcs = ["config_test.cc"], copts = ABSL_TEST_COPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":config", "//absl/synchronization:thread_pool", @@ -348,6 +384,9 @@ cc_test( name = "call_once_test", srcs = ["call_once_test.cc"], copts = ABSL_TEST_COPTS, + tags = [ + "no_test_wasm", + ], deps = [ ":base", ":core_headers", @@ -362,6 +401,7 @@ cc_test( copts = ABSL_TEST_COPTS, deps = [ ":base", + "//absl/strings", "@com_google_googletest//:gtest_main", ], ) @@ -387,6 +427,7 @@ cc_test( "//absl:windows": [], "//conditions:default": ["-pthread"], }), + tags = ["no_test_ios_x86_64"], deps = [":malloc_internal"], ) @@ -399,6 +440,9 @@ cc_test( "//absl:windows": [], "//conditions:default": ["-pthread"], }), + tags = [ + "no_test_wasm", + ], deps = [ ":base", ":core_headers", @@ -419,3 +463,23 @@ cc_test( "@com_github_google_benchmark//:benchmark_main", ], ) + +cc_library( + name = "bits", + hdrs = ["internal/bits.h"], + visibility = [ + "//absl:__subpackages__", + ], + deps = [":core_headers"], +) + +cc_test( + name = "bits_test", + size = "small", + srcs = ["internal/bits_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":bits", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 303533e26d1..212dd083628 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -14,373 +14,401 @@ # limitations under the License. # -list(APPEND BASE_PUBLIC_HEADERS - "attributes.h" - "call_once.h" - "casts.h" - "config.h" - "dynamic_annotations.h" - "log_severity.h" - "macros.h" - "optimization.h" - "policy_checks.h" - "port.h" - "thread_annotations.h" +absl_cc_library( + NAME + spinlock_wait + HDRS + "internal/scheduling_mode.h" + "internal/spinlock_wait.h" + SRCS + "internal/spinlock_akaros.inc" + "internal/spinlock_linux.inc" + "internal/spinlock_posix.inc" + "internal/spinlock_wait.cc" + "internal/spinlock_win32.inc" + DEPS + absl::core_headers ) +absl_cc_library( + NAME + config + HDRS + "config.h" + "policy_checks.h" + COPTS + ${ABSL_DEFAULT_COPTS} + PUBLIC +) + +absl_cc_library( + NAME + dynamic_annotations + HDRS + "dynamic_annotations.h" + SRCS + "dynamic_annotations.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEFINES + "__CLANG_SUPPORT_DYN_ANNOTATION__" + PUBLIC +) -list(APPEND BASE_INTERNAL_HEADERS - "internal/atomic_hook.h" - "internal/cycleclock.h" - "internal/direct_mmap.h" - "internal/endian.h" - "internal/exception_testing.h" - "internal/exception_safety_testing.h" - "internal/hide_ptr.h" - "internal/identity.h" - "internal/invoke.h" - "internal/inline_variable.h" - "internal/low_level_alloc.h" - "internal/low_level_scheduling.h" - "internal/per_thread_tls.h" - "internal/pretty_function.h" - "internal/raw_logging.h" - "internal/scheduling_mode.h" - "internal/spinlock.h" - "internal/spinlock_wait.h" - "internal/sysinfo.h" - "internal/thread_identity.h" - "internal/throw_delegate.h" - "internal/tsan_mutex_interface.h" - "internal/unaligned_access.h" - "internal/unscaledcycleclock.h" +absl_cc_library( + NAME + core_headers + HDRS + "attributes.h" + "macros.h" + "optimization.h" + "port.h" + "thread_annotations.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + PUBLIC ) +absl_cc_library( + NAME + malloc_internal + HDRS + "internal/direct_mmap.h" + "internal/low_level_alloc.h" + SRCS + "internal/low_level_alloc.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::config + absl::core_headers + absl::dynamic_annotations + absl::spinlock_wait +) -# absl_base main library -list(APPEND BASE_SRC - "internal/cycleclock.cc" - "internal/raw_logging.cc" - "internal/spinlock.cc" - "internal/sysinfo.cc" - "internal/thread_identity.cc" - "internal/unscaledcycleclock.cc" - "internal/low_level_alloc.cc" - ${BASE_PUBLIC_HEADERS} - ${BASE_INTERNAL_HEADERS} +absl_cc_library( + NAME + base_internal + HDRS + "internal/hide_ptr.h" + "internal/identity.h" + "internal/inline_variable.h" + "internal/invoke.h" + COPTS + ${ABSL_DEFAULT_COPTS} ) -absl_library( - TARGET - absl_base - SOURCES - ${BASE_SRC} - PUBLIC_LIBRARIES - absl_dynamic_annotations - absl_spinlock_wait - EXPORT_NAME +absl_cc_library( + NAME base + HDRS + "call_once.h" + "casts.h" + "internal/atomic_hook.h" + "internal/cycleclock.h" + "internal/low_level_scheduling.h" + "internal/per_thread_tls.h" + "internal/raw_logging.h" + "internal/spinlock.h" + "internal/sysinfo.h" + "internal/thread_identity.h" + "internal/tsan_mutex_interface.h" + "internal/unscaledcycleclock.h" + "log_severity.h" + SRCS + "internal/cycleclock.cc" + "internal/raw_logging.cc" + "internal/spinlock.cc" + "internal/sysinfo.cc" + "internal/thread_identity.cc" + "internal/unscaledcycleclock.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base_internal + absl::config + absl::core_headers + absl::dynamic_annotations + absl::spinlock_wait + PUBLIC ) -# throw delegate library -set(THROW_DELEGATE_SRC "internal/throw_delegate.cc") - -absl_library( - TARGET - absl_throw_delegate - SOURCES - ${THROW_DELEGATE_SRC} - PUBLIC_LIBRARIES - ${THROW_DELEGATE_PUBLIC_LIBRARIES} - PRIVATE_COMPILE_FLAGS - ${ABSL_EXCEPTIONS_FLAG} - EXPORT_NAME +absl_cc_library( + NAME throw_delegate + HDRS + "internal/throw_delegate.h" + SRCS + "internal/throw_delegate.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + DEPS + absl::base +) + +absl_cc_library( + NAME + exception_testing + HDRS + "internal/exception_testing.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + gtest + TESTONLY +) + +absl_cc_library( + NAME + pretty_function + HDRS + "internal/pretty_function.h" + COPTS + ${ABSL_DEFAULT_COPTS} ) -if(BUILD_TESTING) - # exception-safety testing library - set(EXCEPTION_SAFETY_TESTING_SRC +absl_cc_library( + NAME + exception_safety_testing + HDRS "internal/exception_safety_testing.h" + SRCS "internal/exception_safety_testing.cc" - ) - set(EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES - ${ABSL_TEST_COMMON_LIBRARIES} + COPTS + ${ABSL_DEFAULT_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + DEPS absl::base + absl::config + absl::pretty_function absl::memory absl::meta absl::strings - absl::optional + absl::utility gtest - ) - -absl_library( - TARGET - absl_base_internal_exception_safety_testing - SOURCES - ${EXCEPTION_SAFETY_TESTING_SRC} - PUBLIC_LIBRARIES - ${EXCEPTION_SAFETY_TESTING_PUBLIC_LIBRARIES} - PRIVATE_COMPILE_FLAGS - ${ABSL_EXCEPTIONS_FLAG} -) -endif() - - -# dynamic_annotations library -set(DYNAMIC_ANNOTATIONS_SRC "dynamic_annotations.cc") - -absl_library( - TARGET - absl_dynamic_annotations - SOURCES - ${DYNAMIC_ANNOTATIONS_SRC} -) - - -# spinlock_wait library -set(SPINLOCK_WAIT_SRC "internal/spinlock_wait.cc") - -absl_library( - TARGET - absl_spinlock_wait - SOURCES - ${SPINLOCK_WAIT_SRC} -) - - -# malloc_internal library -list(APPEND MALLOC_INTERNAL_SRC - "internal/low_level_alloc.cc" + TESTONLY ) -absl_library( - TARGET - absl_malloc_internal - SOURCES - ${MALLOC_INTERNAL_SRC} - PUBLIC_LIBRARIES - absl_dynamic_annotations +absl_cc_test( + NAME + absl_exception_safety_testing_test + SRCS + "exception_safety_testing_test.cc" + COPTS + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::exception_safety_testing + absl::memory + gtest_main ) - - -# -## TESTS -# - -# call once test -set(ATOMIC_HOOK_TEST_SRC "internal/atomic_hook_test.cc") -set(ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES absl::base) - -absl_test( - TARGET +absl_cc_test( + NAME atomic_hook_test - SOURCES - ${ATOMIC_HOOK_TEST_SRC} - PUBLIC_LIBRARIES - ${ATOMIC_HOOK_TEST_PUBLIC_LIBRARIES} -) - - -# call once test -set(CALL_ONCE_TEST_SRC "call_once_test.cc") -set(CALL_ONCE_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization) - -absl_test( - TARGET - call_once_test - SOURCES - ${CALL_ONCE_TEST_SRC} - PUBLIC_LIBRARIES - ${CALL_ONCE_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/atomic_hook_test.cc" + DEPS + absl::base + absl::core_headers + gtest_main ) - -# test bit_cast_test -set(BIT_CAST_TEST_SRC "bit_cast_test.cc") - -absl_test( - TARGET +absl_cc_test( + NAME bit_cast_test - SOURCES - ${BIT_CAST_TEST_SRC} + SRCS + "bit_cast_test.cc" + DEPS + absl::base + absl::core_headers + gtest_main ) - -# test absl_throw_delegate_test -set(THROW_DELEGATE_TEST_SRC "throw_delegate_test.cc") -set(THROW_DELEGATE_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate) - -absl_test( - TARGET +absl_cc_test( + NAME throw_delegate_test - SOURCES - ${THROW_DELEGATE_TEST_SRC} - PUBLIC_LIBRARIES - ${THROW_DELEGATE_TEST_PUBLIC_LIBRARIES} -) - - -# test invoke_test -set(INVOKE_TEST_SRC "invoke_test.cc") -set(INVOKE_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET - invoke_test - SOURCES - ${INVOKE_TEST_SRC} - PUBLIC_LIBRARIES - ${INVOKE_TEST_PUBLIC_LIBRARIES} -) - - -# test inline_variable_test -list(APPEND INLINE_VARIABLE_TEST_SRC - "internal/inline_variable_testing.h" - "inline_variable_test.cc" - "inline_variable_test_a.cc" - "inline_variable_test_b.cc" + SRCS + "throw_delegate_test.cc" + DEPS + absl::base + absl_internal_throw_delegate + gtest_main ) -set(INLINE_VARIABLE_TEST_PUBLIC_LIBRARIES absl::base) - -absl_test( - TARGET +absl_cc_test( + NAME inline_variable_test - SOURCES - ${INLINE_VARIABLE_TEST_SRC} - PUBLIC_LIBRARIES - ${INLINE_VARIABLE_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/inline_variable_testing.h" + "inline_variable_test.cc" + "inline_variable_test_a.cc" + "inline_variable_test_b.cc" + DEPS + absl::base_internal + gtest_main ) +absl_cc_test( + NAME + invoke_test + SRCS + "invoke_test.cc" + DEPS + absl::base_internal + absl::memory + absl::strings + gmock + gtest_main +) -# test spinlock_test_common -set(SPINLOCK_TEST_COMMON_SRC "spinlock_test_common.cc") -set(SPINLOCK_TEST_COMMON_PUBLIC_LIBRARIES absl::base absl::synchronization) - -absl_test( - TARGET +absl_cc_library( + NAME spinlock_test_common - SOURCES - ${SPINLOCK_TEST_COMMON_SRC} - PUBLIC_LIBRARIES - ${SPINLOCK_TEST_COMMON_PUBLIC_LIBRARIES} + SRCS + "spinlock_test_common.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::core_headers + absl::spinlock_wait + absl::synchronization + gtest + TESTONLY ) - -# test spinlock_test -set(SPINLOCK_TEST_SRC "spinlock_test_common.cc") -set(SPINLOCK_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization) - -absl_test( - TARGET +# On bazel BUILD this target use "alwayslink = 1" which is not implemented here +absl_cc_test( + NAME spinlock_test - SOURCES - ${SPINLOCK_TEST_SRC} - PUBLIC_LIBRARIES - ${SPINLOCK_TEST_PUBLIC_LIBRARIES} + SRCS + "spinlock_test_common.cc" + DEPS + absl::base + absl::core_headers + absl::spinlock_wait + absl::synchronization + gtest_main ) +absl_cc_library( + NAME + endian + HDRS + "internal/endian.h" + "internal/unaligned_access.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers + PUBLIC +) -# test endian_test -set(ENDIAN_TEST_SRC "internal/endian_test.cc") - -absl_test( - TARGET +absl_cc_test( + NAME endian_test - SOURCES - ${ENDIAN_TEST_SRC} + SRCS + "internal/endian_test.cc" + DEPS + absl::base + absl::config + absl::endian + gtest_main ) - -# test config_test -set(CONFIG_TEST_SRC "config_test.cc") -set(CONFIG_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization) -absl_test( - TARGET +absl_cc_test( + NAME config_test - SOURCES - ${CONFIG_TEST_SRC} - PUBLIC_LIBRARIES - ${CONFIG_TEST_PUBLIC_LIBRARIES} + SRCS + "config_test.cc" + DEPS + absl::config + absl::synchronization + gtest_main ) +absl_cc_test( + NAME + call_once_test + SRCS + "call_once_test.cc" + DEPS + absl::base + absl::core_headers + absl::synchronization + gtest_main +) -# test raw_logging_test -set(RAW_LOGGING_TEST_SRC "raw_logging_test.cc") -set(RAW_LOGGING_TEST_PUBLIC_LIBRARIES absl::base) - -absl_test( - TARGET +absl_cc_test( + NAME raw_logging_test - SOURCES - ${RAW_LOGGING_TEST_SRC} - PUBLIC_LIBRARIES - ${RAW_LOGGING_TEST_PUBLIC_LIBRARIES} + SRCS + "raw_logging_test.cc" + DEPS + absl::base + absl::strings + gtest_main ) - -# test sysinfo_test -set(SYSINFO_TEST_SRC "internal/sysinfo_test.cc") -set(SYSINFO_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME sysinfo_test - SOURCES - ${SYSINFO_TEST_SRC} - PUBLIC_LIBRARIES - ${SYSINFO_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/sysinfo_test.cc" + DEPS + absl::base + absl::synchronization + gtest_main ) - -# test low_level_alloc_test -set(LOW_LEVEL_ALLOC_TEST_SRC "internal/low_level_alloc_test.cc") -set(LOW_LEVEL_ALLOC_TEST_PUBLIC_LIBRARIES absl::base) - -absl_test( - TARGET +absl_cc_test( + NAME low_level_alloc_test - SOURCES - ${LOW_LEVEL_ALLOC_TEST_SRC} - PUBLIC_LIBRARIES - ${LOW_LEVEL_ALLOC_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/low_level_alloc_test.cc" + DEPS + absl::malloc_internal + Threads::Threads ) - -# test thread_identity_test -set(THREAD_IDENTITY_TEST_SRC "internal/thread_identity_test.cc") -set(THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES absl::base absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME thread_identity_test - SOURCES - ${THREAD_IDENTITY_TEST_SRC} - PUBLIC_LIBRARIES - ${THREAD_IDENTITY_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/thread_identity_test.cc" + DEPS + absl::base + absl::core_headers + absl::synchronization + Threads::Threads + gtest_main ) -#test exceptions_safety_testing_test -set(EXCEPTION_SAFETY_TESTING_TEST_SRC "exception_safety_testing_test.cc") -set(EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES - absl::base - absl_base_internal_exception_safety_testing - absl::memory - absl::meta - absl::strings - absl::optional +absl_cc_library( + NAME + bits + HDRS + "internal/bits.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers ) -absl_test( - TARGET - absl_exception_safety_testing_test - SOURCES - ${EXCEPTION_SAFETY_TESTING_TEST_SRC} - PUBLIC_LIBRARIES - ${EXCEPTION_SAFETY_TESTING_TEST_PUBLIC_LIBRARIES} - PRIVATE_COMPILE_FLAGS - ${ABSL_EXCEPTIONS_FLAG} +absl_cc_test( + NAME + bits_test + SRCS + "internal/bits_test.cc" + DEPS + absl::bits + gtest_main ) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index b1883b6d752..291ad89ee78 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -100,7 +100,7 @@ // ABSL_PRINTF_ATTRIBUTE // ABSL_SCANF_ATTRIBUTE // -// Tells the compiler to perform `printf` format std::string checking if the +// Tells the compiler to perform `printf` format string checking if the // compiler supports it; see the 'format' attribute in // . // @@ -155,7 +155,12 @@ // ABSL_ATTRIBUTE_WEAK // // Tags a function as weak for the purposes of compilation and linking. -#if ABSL_HAVE_ATTRIBUTE(weak) || (defined(__GNUC__) && !defined(__clang__)) +// Weak attributes currently do not work properly in LLVM's Windows backend, +// so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 +// for futher information. +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ + (defined(__GNUC__) && !defined(__clang__))) && \ + !(defined(__llvm__) && defined(_WIN32)) #undef ABSL_ATTRIBUTE_WEAK #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) #define ABSL_HAVE_ATTRIBUTE_WEAK 1 @@ -296,13 +301,13 @@ // ABSL_HAVE_ATTRIBUTE_SECTION // -// Indicates whether labeled sections are supported. Labeled sections are not -// supported on Darwin/iOS. +// Indicates whether labeled sections are supported. Weak symbol support is +// a prerequisite. Labeled sections are not supported on Darwin/iOS. #ifdef ABSL_HAVE_ATTRIBUTE_SECTION #error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set #elif (ABSL_HAVE_ATTRIBUTE(section) || \ (defined(__GNUC__) && !defined(__clang__))) && \ - !defined(__APPLE__) + !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK #define ABSL_HAVE_ATTRIBUTE_SECTION 1 // ABSL_ATTRIBUTE_SECTION @@ -397,17 +402,28 @@ // ABSL_MUST_USE_RESULT // -// Tells the compiler to warn about unused return values for functions declared -// with this macro. The macro must appear as the very first part of a function -// declaration or definition: +// Tells the compiler to warn about unused results. // -// Example: +// When annotating a function, it must appear as the first part of the +// declaration or definition. The compiler will warn if the return value from +// such a function is unused: // // ABSL_MUST_USE_RESULT Sprocket* AllocateSprocket(); +// AllocateSprocket(); // Triggers a warning. +// +// When annotating a class, it is equivalent to annotating every function which +// returns an instance. +// +// class ABSL_MUST_USE_RESULT Sprocket {}; +// Sprocket(); // Triggers a warning. +// +// Sprocket MakeSprocket(); +// MakeSprocket(); // Triggers a warning. +// +// Note that references and pointers are not instances: // -// This placement has the broadest compatibility with GCC, Clang, and MSVC, with -// both defs and decls, and with GCC-style attributes, MSVC declspec, C++11 -// and C++17 attributes. +// Sprocket* SprocketPointer(); +// SprocketPointer(); // Does *not* trigger a warning. // // ABSL_MUST_USE_RESULT allows using cast-to-void to suppress the unused result // warning. For that, warn_unused_result is used only for clang but not for gcc. @@ -494,14 +510,27 @@ #define ABSL_XRAY_LOG_ARGS(N) #endif +// ABSL_ATTRIBUTE_REINITIALIZES +// +// Indicates that a member function reinitializes the entire object to a known +// state, independent of the previous state of the object. +// +// The clang-tidy check bugprone-use-after-move allows member functions marked +// with this attribute to be called on objects that have been moved from; +// without the attribute, this would result in a use-after-move warning. +#if ABSL_HAVE_CPP_ATTRIBUTE(clang::reinitializes) +#define ABSL_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]] +#else +#define ABSL_ATTRIBUTE_REINITIALIZES +#endif + // ----------------------------------------------------------------------------- // Variable Attributes // ----------------------------------------------------------------------------- // ABSL_ATTRIBUTE_UNUSED // -// Prevents the compiler from complaining about or optimizing away variables -// that appear unused. +// Prevents the compiler from complaining about variables that appear unused. #if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) #undef ABSL_ATTRIBUTE_UNUSED #define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) diff --git a/absl/base/bit_cast_test.cc b/absl/base/bit_cast_test.cc index 71bb368fb74..5af036df37c 100644 --- a/absl/base/bit_cast_test.cc +++ b/absl/base/bit_cast_test.cc @@ -22,7 +22,7 @@ #include "absl/base/macros.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace { template @@ -105,5 +105,5 @@ TEST(BitCast, Double) { } } // namespace -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/call_once.h b/absl/base/call_once.h index 37b6608a8f3..aea9197bce6 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h @@ -39,7 +39,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { class once_flag; @@ -151,12 +151,8 @@ void CallOnceImpl(std::atomic* control, old_control != kOnceRunning && old_control != kOnceWaiter && old_control != kOnceDone) { - ABSL_RAW_LOG( - FATAL, - "Unexpected value for control word: %lx. Either the control word " - "has non-static storage duration (where GoogleOnceDynamic might " - "be appropriate), or there's been a memory corruption.", - static_cast(old_control)); // NOLINT + ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx", + static_cast(old_control)); // NOLINT } } #endif // NDEBUG @@ -212,7 +208,7 @@ void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { } } -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // 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 43a71656629..4d98a405c86 100644 --- a/absl/base/call_once_test.cc +++ b/absl/base/call_once_test.cc @@ -22,7 +22,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace { absl::once_flag once; @@ -100,5 +100,5 @@ TEST(CallOnceTest, ExecutionCount) { } } // namespace -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/casts.h b/absl/base/casts.h index cd07d8f68e7..bba623b4ab9 100644 --- a/absl/base/casts.h +++ b/absl/base/casts.h @@ -25,12 +25,36 @@ #define ABSL_BASE_CASTS_H_ #include +#include #include #include "absl/base/internal/identity.h" +#include "absl/base/macros.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { + +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> {}; + +} // namespace internal_casts // implicit_cast() // @@ -82,7 +106,7 @@ inline namespace lts_2018_06_20 { // // Such implicit cast chaining may be useful within template logic. template -inline To implicit_cast(typename absl::internal::identity_t to) { +constexpr To implicit_cast(typename absl::internal::identity_t to) { return to; } @@ -126,7 +150,32 @@ inline To implicit_cast(typename absl::internal::identity_t to) { // and reading its bits back using a different type. A `bit_cast()` avoids this // issue by implementing its casts using `memcpy()`, which avoids introducing // this undefined behavior. -template +// +// NOTE: The requirements here are more strict than the bit_cast of standard +// proposal p0476 due to the need for workarounds and lack of intrinsics. +// Specifically, this implementation also requires `Dest` to be +// default-constructible. +template < + typename Dest, typename Source, + typename std::enable_if::value, + int>::type = 0> +inline Dest bit_cast(const Source& source) { + Dest dest; + memcpy(static_cast(std::addressof(dest)), + static_cast(std::addressof(source)), sizeof(dest)); + return dest; +} + +// NOTE: This overload is only picked if the requirements of bit_cast are not +// met. It is therefore UB, but is provided temporarily as previous versions of +// this function template were unchecked. Do not use this in new code. +template < + typename Dest, typename Source, + typename std::enable_if< + !internal_casts::is_bitcastable::value, int>::type = 0> +ABSL_DEPRECATED( + "absl::bit_cast type requirements were violated. Update the types being " + "used such that they are the same size and are both TriviallyCopyable.") inline Dest bit_cast(const Source& source) { static_assert(sizeof(Dest) == sizeof(Source), "Source and destination types should have equal sizes."); @@ -136,7 +185,7 @@ inline Dest bit_cast(const Source& source) { return dest; } -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_CASTS_H_ diff --git a/absl/base/config.h b/absl/base/config.h index 2f5f15951fb..db4c4539022 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -139,12 +139,18 @@ #ifdef ABSL_HAVE_THREAD_LOCAL #error ABSL_HAVE_THREAD_LOCAL cannot be directly set #elif defined(__APPLE__) -// Notes: Xcode's clang did not support `thread_local` until version -// 8, and even then not for all iOS < 9.0. Also, Xcode 9.3 started disallowing -// `thread_local` for 32-bit iOS simulator targeting iOS 9.x. -// `__has_feature` is only supported by Clang so it has be inside +// Notes: +// * Xcode's clang did not support `thread_local` until version 8, and +// even then not for all iOS < 9.0. +// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator +// targeting iOS 9.x. +// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time +// making __has_feature unreliable there. +// +// Otherwise, `__has_feature` is only supported by Clang so it has be inside // `defined(__APPLE__)` check. -#if __has_feature(cxx_thread_local) +#if __has_feature(cxx_thread_local) && \ + !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) #define ABSL_HAVE_THREAD_LOCAL 1 #endif #else // !defined(__APPLE__) @@ -199,7 +205,7 @@ #define ABSL_HAVE_INTRINSIC_INT128 1 #elif defined(__CUDACC__) // __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a -// std::string explaining that it has been removed starting with CUDA 9. We use +// string explaining that it has been removed starting with CUDA 9. We use // nested #ifs because there is no short-circuiting in the preprocessor. // NOTE: `__CUDACC__` could be undefined while `__CUDACC_VER__` is defined. #if __CUDACC_VER__ >= 70000 @@ -268,7 +274,8 @@ #error ABSL_HAVE_MMAP cannot be directly set #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \ - defined(__wasm__) || defined(__Fuchsia__) + defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \ + defined(__ASYLO__) #define ABSL_HAVE_MMAP 1 #endif @@ -322,6 +329,8 @@ #define ABSL_HAVE_ALARM 1 #elif defined(_MSC_VER) // feature tests for Microsoft's library +#elif defined(__EMSCRIPTEN__) +// emscripten doesn't support signals #elif defined(__native_client__) #else // other standard libraries @@ -356,6 +365,18 @@ #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 +// https://github.com/abseil/abseil-cpp/issues/207 and +// https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes +#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 +#else +#define ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES 0 +#endif + // ABSL_HAVE_STD_ANY // // Checks whether C++17 std::any is available by checking whether exists. @@ -364,7 +385,8 @@ #endif #ifdef __has_include -#if __has_include() && __cplusplus >= 201703L +#if __has_include() && __cplusplus >= 201703L && \ + ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES #define ABSL_HAVE_STD_ANY 1 #endif #endif @@ -377,7 +399,8 @@ #endif #ifdef __has_include -#if __has_include() && __cplusplus >= 201703L +#if __has_include() && __cplusplus >= 201703L && \ + ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES #define ABSL_HAVE_STD_OPTIONAL 1 #endif #endif @@ -390,7 +413,8 @@ #endif #ifdef __has_include -#if __has_include() && __cplusplus >= 201703L +#if __has_include() && __cplusplus >= 201703L && \ + ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES #define ABSL_HAVE_STD_VARIANT 1 #endif #endif @@ -414,14 +438,21 @@ // , is implemented) or higher. Also, `__cplusplus` is // not correctly set by MSVC, so we use `_MSVC_LANG` to check the language // version. -// TODO(zhangxy): fix tests before enabling aliasing for `std::any`, -// `std::string_view`. +// TODO(zhangxy): fix tests before enabling aliasing for `std::any`. #if defined(_MSC_VER) && _MSC_VER >= 1910 && \ ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402) // #define ABSL_HAVE_STD_ANY 1 #define ABSL_HAVE_STD_OPTIONAL 1 #define ABSL_HAVE_STD_VARIANT 1 -// #define ABSL_HAVE_STD_STRING_VIEW 1 +#define ABSL_HAVE_STD_STRING_VIEW 1 +#endif + +// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION +// SEH exception from emplace for variant when constructing the +// struct can throw. This defeats some of variant_test and +// variant_exception_safety_test. +#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG) +#define ABSL_INTERNAL_MSVC_2017_DBG_MODE #endif #endif // ABSL_BASE_CONFIG_H_ diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc index 97c8d6f8318..7518264d2ec 100644 --- a/absl/base/exception_safety_testing_test.cc +++ b/absl/base/exception_safety_testing_test.cc @@ -38,7 +38,7 @@ template void ExpectNoThrow(const F& f) { try { f(); - } catch (TestException e) { + } catch (const TestException& e) { ADD_FAILURE() << "Unexpected exception thrown from " << e.what(); } } @@ -179,7 +179,7 @@ TEST(ThrowingValueTest, ThrowingStreamOps) { } // Tests the operator<< of ThrowingValue by forcing ConstructorTracker to emit -// a nonfatal failure that contains the std::string representation of the Thrower +// a nonfatal failure that contains the string representation of the Thrower TEST(ThrowingValueTest, StreamOpsOutput) { using ::testing::TypeSpec; exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown); @@ -548,21 +548,21 @@ TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) { // Test that providing operation and inveriants still does not allow for the // the invocation of .Test() and .Test(op) because it lacks a factory auto without_fac = - testing::MakeExceptionSafetyTester().WithOperation(op).WithInvariants( + testing::MakeExceptionSafetyTester().WithOperation(op).WithContracts( inv, testing::strong_guarantee); EXPECT_FALSE(HasNullaryTest(without_fac)); EXPECT_FALSE(HasUnaryTest(without_fac)); - // Test that providing invariants and factory allows the invocation of + // Test that providing contracts and factory allows the invocation of // .Test(op) but does not allow for .Test() because it lacks an operation auto without_op = testing::MakeExceptionSafetyTester() - .WithInvariants(inv, testing::strong_guarantee) + .WithContracts(inv, testing::strong_guarantee) .WithFactory(fac); EXPECT_FALSE(HasNullaryTest(without_op)); EXPECT_TRUE(HasUnaryTest(without_op)); // Test that providing operation and factory still does not allow for the - // the invocation of .Test() and .Test(op) because it lacks invariants + // the invocation of .Test() and .Test(op) because it lacks contracts auto without_inv = testing::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac); EXPECT_FALSE(HasNullaryTest(without_inv)); @@ -577,7 +577,7 @@ std::unique_ptr ExampleFunctionFactory() { void ExampleFunctionOperation(ExampleStruct*) {} -testing::AssertionResult ExampleFunctionInvariant(ExampleStruct*) { +testing::AssertionResult ExampleFunctionContract(ExampleStruct*) { return testing::AssertionSuccess(); } @@ -593,16 +593,16 @@ struct { struct { testing::AssertionResult operator()(ExampleStruct* example_struct) const { - return ExampleFunctionInvariant(example_struct); + return ExampleFunctionContract(example_struct); } -} example_struct_invariant; +} example_struct_contract; auto example_lambda_factory = []() { return ExampleFunctionFactory(); }; auto example_lambda_operation = [](ExampleStruct*) {}; -auto example_lambda_invariant = [](ExampleStruct* example_struct) { - return ExampleFunctionInvariant(example_struct); +auto example_lambda_contract = [](ExampleStruct* example_struct) { + return ExampleFunctionContract(example_struct); }; // Testing that function references, pointers, structs with operator() and @@ -612,28 +612,28 @@ TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) { EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithFactory(ExampleFunctionFactory) .WithOperation(ExampleFunctionOperation) - .WithInvariants(ExampleFunctionInvariant) + .WithContracts(ExampleFunctionContract) .Test()); // function pointer EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithFactory(&ExampleFunctionFactory) .WithOperation(&ExampleFunctionOperation) - .WithInvariants(&ExampleFunctionInvariant) + .WithContracts(&ExampleFunctionContract) .Test()); // struct EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithFactory(example_struct_factory) .WithOperation(example_struct_operation) - .WithInvariants(example_struct_invariant) + .WithContracts(example_struct_contract) .Test()); // lambda EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithFactory(example_lambda_factory) .WithOperation(example_lambda_operation) - .WithInvariants(example_lambda_invariant) + .WithContracts(example_lambda_contract) .Test()); } @@ -658,9 +658,9 @@ struct { } invoker; auto tester = - testing::MakeExceptionSafetyTester().WithOperation(invoker).WithInvariants( + testing::MakeExceptionSafetyTester().WithOperation(invoker).WithContracts( CheckNonNegativeInvariants); -auto strong_tester = tester.WithInvariants(testing::strong_guarantee); +auto strong_tester = tester.WithContracts(testing::strong_guarantee); struct FailsBasicGuarantee : public NonNegative { void operator()() { @@ -690,7 +690,7 @@ TEST(ExceptionCheckTest, StrongGuaranteeFailure) { EXPECT_FALSE(strong_tester.WithInitialValue(FollowsBasicGuarantee{}).Test()); } -struct BasicGuaranteeWithExtraInvariants : public NonNegative { +struct BasicGuaranteeWithExtraContracts : public NonNegative { // After operator(), i is incremented. If operator() throws, i is set to 9999 void operator()() { int old_i = i; @@ -701,21 +701,21 @@ struct BasicGuaranteeWithExtraInvariants : public NonNegative { static constexpr int kExceptionSentinel = 9999; }; -constexpr int BasicGuaranteeWithExtraInvariants::kExceptionSentinel; +constexpr int BasicGuaranteeWithExtraContracts::kExceptionSentinel; -TEST(ExceptionCheckTest, BasicGuaranteeWithExtraInvariants) { +TEST(ExceptionCheckTest, BasicGuaranteeWithExtraContracts) { auto tester_with_val = - tester.WithInitialValue(BasicGuaranteeWithExtraInvariants{}); + tester.WithInitialValue(BasicGuaranteeWithExtraContracts{}); EXPECT_TRUE(tester_with_val.Test()); EXPECT_TRUE( tester_with_val - .WithInvariants([](BasicGuaranteeWithExtraInvariants* o) { - if (o->i == BasicGuaranteeWithExtraInvariants::kExceptionSentinel) { + .WithContracts([](BasicGuaranteeWithExtraContracts* o) { + if (o->i == BasicGuaranteeWithExtraContracts::kExceptionSentinel) { return testing::AssertionSuccess(); } return testing::AssertionFailure() << "i should be " - << BasicGuaranteeWithExtraInvariants::kExceptionSentinel + << BasicGuaranteeWithExtraContracts::kExceptionSentinel << ", but is " << o->i; }) .Test()); @@ -740,7 +740,7 @@ struct HasReset : public NonNegative { void reset() { i = 0; } }; -testing::AssertionResult CheckHasResetInvariants(HasReset* h) { +testing::AssertionResult CheckHasResetContracts(HasReset* h) { h->reset(); return testing::AssertionResult(h->i == 0); } @@ -759,17 +759,29 @@ TEST(ExceptionCheckTest, ModifyingChecker) { }; EXPECT_FALSE(tester.WithInitialValue(FollowsBasicGuarantee{}) - .WithInvariants(set_to_1000, is_1000) + .WithContracts(set_to_1000, is_1000) .Test()); EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{}) - .WithInvariants(increment) + .WithContracts(increment) .Test()); EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithInitialValue(HasReset{}) - .WithInvariants(CheckHasResetInvariants) + .WithContracts(CheckHasResetContracts) .Test(invoker)); } +TEST(ExceptionSafetyTesterTest, ResetsCountdown) { + auto test = + testing::MakeExceptionSafetyTester() + .WithInitialValue(ThrowingValue<>()) + .WithContracts([](ThrowingValue<>*) { return AssertionSuccess(); }) + .WithOperation([](ThrowingValue<>*) {}); + ASSERT_TRUE(test.Test()); + // If the countdown isn't reset because there were no exceptions thrown, then + // this will fail with a termination from an unhandled exception + EXPECT_TRUE(test.Test()); +} + struct NonCopyable : public NonNegative { NonCopyable(const NonCopyable&) = delete; NonCopyable() : NonNegative{0} {} @@ -799,7 +811,7 @@ TEST(ExceptionCheckTest, NonEqualityComparable) { return testing::AssertionResult(nec->i == NonEqualityComparable().i); }; auto strong_nec_tester = tester.WithInitialValue(NonEqualityComparable{}) - .WithInvariants(nec_is_strong); + .WithContracts(nec_is_strong); EXPECT_TRUE(strong_nec_tester.Test()); EXPECT_FALSE(strong_nec_tester.Test( @@ -833,14 +845,14 @@ struct { testing::AssertionResult operator()(ExhaustivenessTester*) const { return testing::AssertionSuccess(); } -} CheckExhaustivenessTesterInvariants; +} CheckExhaustivenessTesterContracts; template unsigned char ExhaustivenessTester::successes = 0; TEST(ExceptionCheckTest, Exhaustiveness) { auto exhaust_tester = testing::MakeExceptionSafetyTester() - .WithInvariants(CheckExhaustivenessTesterInvariants) + .WithContracts(CheckExhaustivenessTesterContracts) .WithOperation(invoker); EXPECT_TRUE( @@ -849,7 +861,7 @@ TEST(ExceptionCheckTest, Exhaustiveness) { EXPECT_TRUE( exhaust_tester.WithInitialValue(ExhaustivenessTester>{}) - .WithInvariants(testing::strong_guarantee) + .WithContracts(testing::strong_guarantee) .Test()); EXPECT_EQ(ExhaustivenessTester>::successes, 0xF); } @@ -931,8 +943,8 @@ TEST(ThrowingValueTraitsTest, RelationalOperators) { } TEST(ThrowingAllocatorTraitsTest, Assignablility) { - EXPECT_TRUE(std::is_move_assignable>::value); - EXPECT_TRUE(std::is_copy_assignable>::value); + EXPECT_TRUE(absl::is_move_assignable>::value); + EXPECT_TRUE(absl::is_copy_assignable>::value); EXPECT_TRUE(std::is_nothrow_move_assignable>::value); EXPECT_TRUE(std::is_nothrow_copy_assignable>::value); } diff --git a/absl/base/inline_variable_test.cc b/absl/base/inline_variable_test.cc index b34aebd814a..b968b10f252 100644 --- a/absl/base/inline_variable_test.cc +++ b/absl/base/inline_variable_test.cc @@ -20,7 +20,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace inline_variable_testing_internal { namespace { @@ -60,5 +60,5 @@ TEST(InlineVariableTest, FunPtrType) { } // namespace } // namespace inline_variable_testing_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/inline_variable_test_a.cc b/absl/base/inline_variable_test_a.cc index 0ea363af2a7..a51b1d811f0 100644 --- a/absl/base/inline_variable_test_a.cc +++ b/absl/base/inline_variable_test_a.cc @@ -15,7 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/inline_variable_test_b.cc b/absl/base/inline_variable_test_b.cc index 32704cf1091..5041e20a61f 100644 --- a/absl/base/inline_variable_test_b.cc +++ b/absl/base/inline_variable_test_b.cc @@ -15,7 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h index 7d0ee2fa67b..58ddf2725bd 100644 --- a/absl/base/internal/atomic_hook.h +++ b/absl/base/internal/atomic_hook.h @@ -28,7 +28,7 @@ #endif namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { template @@ -161,7 +161,7 @@ class AtomicHook { #undef ABSL_HAVE_WORKING_ATOMIC_POINTER } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h new file mode 100644 index 00000000000..29657426e70 --- /dev/null +++ b/absl/base/internal/bits.h @@ -0,0 +1,195 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_BITS_H_ +#define ABSL_BASE_INTERNAL_BITS_H_ + +// This file contains bitwise ops which are implementation details of various +// absl libraries. + +#include + +// Clang on Windows has __builtin_clzll; otherwise we need to use the +// windows intrinsic functions. +#if defined(_MSC_VER) +#include +#if defined(_M_X64) +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_BitScanForward64) +#endif +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) +#endif + +#include "absl/base/attributes.h" + +#if defined(_MSC_VER) +// We can achieve something similar to attribute((always_inline)) with MSVC by +// using the __forceinline keyword, however this is not perfect. MSVC is +// much less aggressive about inlining, and even with the __forceinline keyword. +#define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline +#else +// Use default attribute inline. +#define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE +#endif + + +namespace absl { +inline namespace lts_2018_12_18 { +namespace base_internal { + +ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { + int zeroes = 60; + if (n >> 32) zeroes -= 32, n >>= 32; + if (n >> 16) zeroes -= 16, n >>= 16; + if (n >> 8) zeroes -= 8, n >>= 8; + if (n >> 4) zeroes -= 4, n >>= 4; + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { +#if defined(_MSC_VER) && defined(_M_X64) + // MSVC does not have __buitin_clzll. Use _BitScanReverse64. + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse64(&result, n)) { + return 63 - result; + } + return 64; +#elif defined(_MSC_VER) + // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse + unsigned long result = 0; // NOLINT(runtime/int) + if ((n >> 32) && _BitScanReverse(&result, n >> 32)) { + return 31 - result; + } + if (_BitScanReverse(&result, n)) { + return 63 - result; + } + return 64; +#elif defined(__GNUC__) + // Use __builtin_clzll, which uses the following instructions: + // x86: bsr + // ARM64: clz + // PPC: cntlzd + static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) + "__builtin_clzll does not take 64-bit arg"); + + // Handle 0 as a special case because __builtin_clzll(0) is undefined. + if (n == 0) { + return 64; + } + return __builtin_clzll(n); +#else + return CountLeadingZeros64Slow(n); +#endif +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) { + int zeroes = 28; + if (n >> 16) zeroes -= 16, n >>= 16; + if (n >> 8) zeroes -= 8, n >>= 8; + if (n >> 4) zeroes -= 4, n >>= 4; + return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) { +#if defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + if (_BitScanReverse(&result, n)) { + return 31 - result; + } + return 32; +#elif defined(__GNUC__) + // Use __builtin_clz, which uses the following instructions: + // x86: bsr + // ARM64: clz + // PPC: cntlzd + static_assert(sizeof(int) == sizeof(n), + "__builtin_clz does not take 32-bit arg"); + + // Handle 0 as a special case because __builtin_clz(0) is undefined. + if (n == 0) { + return 32; + } + return __builtin_clz(n); +#else + return CountLeadingZeros32Slow(n); +#endif +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) { + int c = 63; + n &= ~n + 1; + if (n & 0x00000000FFFFFFFF) c -= 32; + if (n & 0x0000FFFF0000FFFF) c -= 16; + if (n & 0x00FF00FF00FF00FF) c -= 8; + if (n & 0x0F0F0F0F0F0F0F0F) c -= 4; + if (n & 0x3333333333333333) c -= 2; + if (n & 0x5555555555555555) c -= 1; + return c; +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) { +#if defined(_MSC_VER) && defined(_M_X64) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward64(&result, n); + return result; +#elif defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + if (static_cast(n) == 0) { + _BitScanForward(&result, n >> 32); + return result + 32; + } + _BitScanForward(&result, n); + return result; +#elif defined(__GNUC__) + static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) + "__builtin_ctzll does not take 64-bit arg"); + return __builtin_ctzll(n); +#else + return CountTrailingZerosNonZero64Slow(n); +#endif +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) { + int c = 31; + n &= ~n + 1; + if (n & 0x0000FFFF) c -= 16; + if (n & 0x00FF00FF) c -= 8; + if (n & 0x0F0F0F0F) c -= 4; + if (n & 0x33333333) c -= 2; + if (n & 0x55555555) c -= 1; + return c; +} + +ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { +#if defined(_MSC_VER) + unsigned long result = 0; // NOLINT(runtime/int) + _BitScanForward(&result, n); + return result; +#elif defined(__GNUC__) + static_assert(sizeof(int) == sizeof(n), + "__builtin_ctz does not take 32-bit arg"); + return __builtin_ctz(n); +#else + return CountTrailingZerosNonZero32Slow(n); +#endif +} + +#undef ABSL_BASE_INTERNAL_FORCEINLINE + +} // namespace base_internal +} // inline namespace lts_2018_12_18 +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_BITS_H_ diff --git a/absl/base/internal/bits_test.cc b/absl/base/internal/bits_test.cc new file mode 100644 index 00000000000..e5d991d6725 --- /dev/null +++ b/absl/base/internal/bits_test.cc @@ -0,0 +1,97 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/bits.h" + +#include "gtest/gtest.h" + +namespace { + +int CLZ64(uint64_t n) { + int fast = absl::base_internal::CountLeadingZeros64(n); + int slow = absl::base_internal::CountLeadingZeros64Slow(n); + EXPECT_EQ(fast, slow) << n; + return fast; +} + +TEST(BitsTest, CountLeadingZeros64) { + EXPECT_EQ(64, CLZ64(uint64_t{})); + EXPECT_EQ(0, CLZ64(~uint64_t{})); + + for (int index = 0; index < 64; index++) { + uint64_t x = static_cast(1) << index; + const auto cnt = 63 - index; + ASSERT_EQ(cnt, CLZ64(x)) << index; + ASSERT_EQ(cnt, CLZ64(x + x - 1)) << index; + } +} + +int CLZ32(uint32_t n) { + int fast = absl::base_internal::CountLeadingZeros32(n); + int slow = absl::base_internal::CountLeadingZeros32Slow(n); + EXPECT_EQ(fast, slow) << n; + return fast; +} + +TEST(BitsTest, CountLeadingZeros32) { + EXPECT_EQ(32, CLZ32(uint32_t{})); + EXPECT_EQ(0, CLZ32(~uint32_t{})); + + for (int index = 0; index < 32; index++) { + uint32_t x = static_cast(1) << index; + const auto cnt = 31 - index; + ASSERT_EQ(cnt, CLZ32(x)) << index; + ASSERT_EQ(cnt, CLZ32(x + x - 1)) << index; + ASSERT_EQ(CLZ64(x), CLZ32(x) + 32); + } +} + +int CTZ64(uint64_t n) { + int fast = absl::base_internal::CountTrailingZerosNonZero64(n); + int slow = absl::base_internal::CountTrailingZerosNonZero64Slow(n); + EXPECT_EQ(fast, slow) << n; + return fast; +} + +TEST(BitsTest, CountTrailingZerosNonZero64) { + EXPECT_EQ(0, CTZ64(~uint64_t{})); + + for (int index = 0; index < 64; index++) { + uint64_t x = static_cast(1) << index; + const auto cnt = index; + ASSERT_EQ(cnt, CTZ64(x)) << index; + ASSERT_EQ(cnt, CTZ64(~(x - 1))) << index; + } +} + +int CTZ32(uint32_t n) { + int fast = absl::base_internal::CountTrailingZerosNonZero32(n); + int slow = absl::base_internal::CountTrailingZerosNonZero32Slow(n); + EXPECT_EQ(fast, slow) << n; + return fast; +} + +TEST(BitsTest, CountTrailingZerosNonZero32) { + EXPECT_EQ(0, CTZ32(~uint32_t{})); + + for (int index = 0; index < 32; index++) { + uint32_t x = static_cast(1) << index; + const auto cnt = index; + ASSERT_EQ(cnt, CTZ32(x)) << index; + ASSERT_EQ(cnt, CTZ32(~(x - 1))) << index; + } +} + + +} // namespace diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc index a4f1fc215c4..d99b63d3104 100644 --- a/absl/base/internal/cycleclock.cc +++ b/absl/base/internal/cycleclock.cc @@ -27,7 +27,7 @@ #include "absl/base/internal/unscaledcycleclock.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { #if ABSL_USE_UNSCALED_CYCLECLOCK @@ -79,5 +79,5 @@ double CycleClock::Frequency() { #endif } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/cycleclock.h b/absl/base/internal/cycleclock.h index 5fd8b348fb5..ae5ede3e8fe 100644 --- a/absl/base/internal/cycleclock.h +++ b/absl/base/internal/cycleclock.h @@ -46,7 +46,7 @@ #include namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { // ----------------------------------------------------------------------------- @@ -73,7 +73,7 @@ class CycleClock { }; } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // 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 e98c9960f52..f064e36392a 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h @@ -62,7 +62,7 @@ extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); #endif // __BIONIC__ namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { // Platform specific logic extracted from @@ -76,7 +76,11 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, // On these architectures, implement mmap with mmap2. static int pagesize = 0; if (pagesize == 0) { +#if defined(__wasm__) || defined(__asmjs__) pagesize = getpagesize(); +#else + pagesize = sysconf(_SC_PAGESIZE); +#endif } if (offset < 0 || offset % pagesize != 0) { errno = EINVAL; @@ -93,11 +97,13 @@ inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, #endif #elif defined(__s390x__) // On s390x, mmap() arguments are passed in memory. - uint32_t buf[6] = { - reinterpret_cast(start), static_cast(length), - static_cast(prot), static_cast(flags), - static_cast(fd), static_cast(offset)}; - return reintrepret_cast(syscall(SYS_mmap, buf)); + unsigned long buf[6] = {reinterpret_cast(start), // NOLINT + static_cast(length), // NOLINT + static_cast(prot), // NOLINT + static_cast(flags), // NOLINT + static_cast(fd), // NOLINT + static_cast(offset)}; // NOLINT + return reinterpret_cast(syscall(SYS_mmap, buf)); #elif defined(__x86_64__) // The x32 ABI has 32 bit longs, but the syscall interface is 64 bit. // We need to explicitly cast to an unsigned 64 bit type to avoid implicit @@ -123,7 +129,7 @@ inline int DirectMunmap(void* start, size_t length) { } } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #else // !__linux__ @@ -132,7 +138,7 @@ inline int DirectMunmap(void* start, size_t length) { // actual mmap()/munmap() methods. namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, @@ -145,7 +151,7 @@ inline int DirectMunmap(void* start, size_t length) { } } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // __linux__ diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index 0044711060f..52c09c5ba5f 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h @@ -34,7 +34,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { // 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. @@ -83,14 +83,14 @@ inline uint64_t gbswap_64(uint64_t host_int) { #elif defined(__GLIBC__) return bswap_64(host_int); #else - return (((x & uint64_t{(0xFF}) << 56) | - ((x & uint64_t{(0xFF00}) << 40) | - ((x & uint64_t{(0xFF0000}) << 24) | - ((x & uint64_t{(0xFF000000}) << 8) | - ((x & uint64_t{(0xFF00000000}) >> 8) | - ((x & uint64_t{(0xFF0000000000}) >> 24) | - ((x & uint64_t{(0xFF000000000000}) >> 40) | - ((x & uint64_t{(0xFF00000000000000}) >> 56)); + return (((host_int & uint64_t{0xFF}) << 56) | + ((host_int & uint64_t{0xFF00}) << 40) | + ((host_int & uint64_t{0xFF0000}) << 24) | + ((host_int & uint64_t{0xFF000000}) << 8) | + ((host_int & uint64_t{0xFF00000000}) >> 8) | + ((host_int & uint64_t{0xFF0000000000}) >> 24) | + ((host_int & uint64_t{0xFF000000000000}) >> 40) | + ((host_int & uint64_t{0xFF00000000000000}) >> 56)); #endif // bswap_64 } @@ -98,8 +98,10 @@ inline uint32_t gbswap_32(uint32_t host_int) { #if defined(__GLIBC__) return bswap_32(host_int); #else - return (((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | - ((x & 0xFF000000) >> 24)); + return (((host_int & uint32_t{0xFF}) << 24) | + ((host_int & uint32_t{0xFF00}) << 8) | + ((host_int & uint32_t{0xFF0000}) >> 8) | + ((host_int & uint32_t{0xFF000000}) >> 24)); #endif } @@ -107,7 +109,8 @@ inline uint16_t gbswap_16(uint16_t host_int) { #if defined(__GLIBC__) return bswap_16(host_int); #else - return uint16_t{((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)}; + return (((host_int & uint16_t{0xFF}) << 8) | + ((host_int & uint16_t{0xFF00}) >> 8)); #endif } @@ -265,7 +268,7 @@ inline void Store64(void *p, uint64_t v) { } // namespace big_endian -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // 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 66ccd45aba9..14ac476542d 100644 --- a/absl/base/internal/endian_test.cc +++ b/absl/base/internal/endian_test.cc @@ -24,7 +24,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace { const uint64_t kInitialNumber{0x0123456789abcdef}; @@ -34,32 +34,16 @@ const uint16_t k16Value{0x0123}; const int kNumValuesToTest = 1000000; const int kRandomSeed = 12345; -#ifdef ABSL_IS_BIG_ENDIAN +#if defined(ABSL_IS_BIG_ENDIAN) const uint64_t kInitialInNetworkOrder{kInitialNumber}; const uint64_t k64ValueLE{0xefcdab8967452301}; const uint32_t k32ValueLE{0x67452301}; const uint16_t k16ValueLE{0x2301}; -const uint8_t k8ValueLE{k8Value}; -const uint64_t k64IValueLE{0xefcdab89674523a1}; -const uint32_t k32IValueLE{0x67452391}; -const uint16_t k16IValueLE{0x85ff}; -const uint8_t k8IValueLE{0xff}; -const uint64_t kDoubleValueLE{0x6e861bf0f9210940}; -const uint32_t kFloatValueLE{0xd00f4940}; -const uint8_t kBoolValueLE{0x1}; const uint64_t k64ValueBE{kInitialNumber}; const uint32_t k32ValueBE{k32Value}; const uint16_t k16ValueBE{k16Value}; -const uint8_t k8ValueBE{k8Value}; -const uint64_t k64IValueBE{0xa123456789abcdef}; -const uint32_t k32IValueBE{0x91234567}; -const uint16_t k16IValueBE{0xff85}; -const uint8_t k8IValueBE{0xff}; -const uint64_t kDoubleValueBE{0x400921f9f01b866e}; -const uint32_t kFloatValueBE{0x40490fd0}; -const uint8_t kBoolValueBE{0x1}; -#elif defined ABSL_IS_LITTLE_ENDIAN +#elif defined(ABSL_IS_LITTLE_ENDIAN) const uint64_t kInitialInNetworkOrder{0xefcdab8967452301}; const uint64_t k64ValueLE{kInitialNumber}; const uint32_t k32ValueLE{k32Value}; @@ -277,5 +261,5 @@ TEST(EndianessTest, big_endian) { } } // namespace -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc index f1d081f7e50..8207b7d7b9a 100644 --- a/absl/base/internal/exception_safety_testing.cc +++ b/absl/base/internal/exception_safety_testing.cc @@ -23,6 +23,10 @@ exceptions_internal::NoThrowTag nothrow_ctor; exceptions_internal::StrongGuaranteeTagType strong_guarantee; +exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester() { + return {}; +} + namespace exceptions_internal { int countdown = -1; diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h index 8c2f5093fc4..d4d41a8a73a 100644 --- a/absl/base/internal/exception_safety_testing.h +++ b/absl/base/internal/exception_safety_testing.h @@ -33,7 +33,7 @@ #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" -#include "absl/types/optional.h" +#include "absl/utility/utility.h" namespace testing { @@ -127,10 +127,8 @@ class ConstructorTracker { void* address = it.first; TrackedAddress& tracked_address = it.second; if (tracked_address.is_alive) { - ADD_FAILURE() << "Object at address " << address - << " with countdown of " << countdown_ - << " was not destroyed [" << tracked_address.description - << "]"; + ADD_FAILURE() << ErrorMessage(address, tracked_address.description, + countdown_, "Object was not destroyed."); } } } @@ -141,11 +139,11 @@ class ConstructorTracker { TrackedAddress& tracked_address = current_tracker_instance_->address_map_[address]; if (tracked_address.is_alive) { - ADD_FAILURE() << "Object at address " << address << " with countdown of " - << current_tracker_instance_->countdown_ - << " was re-constructed. Previously: [" - << tracked_address.description << "] Now: [" << description - << "]"; + ADD_FAILURE() << ErrorMessage( + address, tracked_address.description, + current_tracker_instance_->countdown_, + "Object was re-constructed. Current object was constructed by " + + description); } tracked_address = {true, std::move(description)}; } @@ -159,10 +157,9 @@ class ConstructorTracker { TrackedAddress& tracked_address = it->second; if (!tracked_address.is_alive) { - ADD_FAILURE() << "Object at address " << address << " with countdown of " - << current_tracker_instance_->countdown_ - << " was re-destroyed or created prior to construction " - << "tracking [" << tracked_address.description << "]"; + ADD_FAILURE() << ErrorMessage(address, tracked_address.description, + current_tracker_instance_->countdown_, + "Object was re-destroyed."); } tracked_address.is_alive = false; } @@ -172,6 +169,16 @@ 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) { + return absl::Substitute( + "With coundtown at $0:\n" + " $1\n" + " Object originally constructed by $2\n" + " Object address: $3\n", + countdown, error_description, address_description, address); + } + std::unordered_map address_map_; int countdown_; @@ -190,70 +197,6 @@ class TrackedObject { ~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); } }; - -template -absl::optional TestSingleInvariantAtCountdownImpl( - const Factory& factory, const Operation& operation, int count, - const Invariant& invariant) { - auto t_ptr = factory(); - absl::optional current_res; - SetCountdown(count); - try { - operation(t_ptr.get()); - } catch (const exceptions_internal::TestException& e) { - current_res.emplace(invariant(t_ptr.get())); - if (!current_res.value()) { - *current_res << e.what() << " failed invariant check"; - } - } - UnsetCountdown(); - return current_res; -} - -template -absl::optional TestSingleInvariantAtCountdownImpl( - const Factory& factory, const Operation& operation, int count, - StrongGuaranteeTagType) { - using TPtr = typename decltype(factory())::pointer; - auto t_is_strong = [&](TPtr t) { return *t == *factory(); }; - return TestSingleInvariantAtCountdownImpl(factory, operation, count, - t_is_strong); -} - -template -int TestSingleInvariantAtCountdown( - const Factory& factory, const Operation& operation, int count, - const Invariant& invariant, - absl::optional* reduced_res) { - // If reduced_res is empty, it means the current call to - // TestSingleInvariantAtCountdown(...) is the first test being run so we do - // want to run it. Alternatively, if it's not empty (meaning a previous test - // has run) we want to check if it passed. If the previous test did pass, we - // want to contine running tests so we do want to run the current one. If it - // failed, we want to short circuit so as not to overwrite the AssertionResult - // output. If that's the case, we do not run the current test and instead we - // simply return. - if (!reduced_res->has_value() || reduced_res->value()) { - *reduced_res = TestSingleInvariantAtCountdownImpl(factory, operation, count, - invariant); - } - return 0; -} - -template -inline absl::optional TestAllInvariantsAtCountdown( - const Factory& factory, const Operation& operation, int count, - const Invariants&... invariants) { - absl::optional reduced_res; - - // Run each checker, short circuiting after the first failure - int dummy[] = { - 0, (TestSingleInvariantAtCountdown(factory, operation, count, invariants, - &reduced_res))...}; - static_cast(dummy); - return reduced_res; -} - } // namespace exceptions_internal extern exceptions_internal::NoThrowTag nothrow_ctor; @@ -773,7 +716,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { } size_type max_size() const noexcept { - return std::numeric_limits::max() / sizeof(value_type); + return (std::numeric_limits::max)() / sizeof(value_type); } ThrowingAllocator select_on_container_copy_construction() noexcept( @@ -858,7 +801,7 @@ testing::AssertionResult TestNothrowOp(const Operation& operation) { try { operation(); return testing::AssertionSuccess(); - } catch (exceptions_internal::TestException) { + } catch (const exceptions_internal::TestException&) { return testing::AssertionFailure() << "TestException thrown during call to operation() when nothrow " "guarantee was expected."; @@ -871,7 +814,7 @@ testing::AssertionResult TestNothrowOp(const Operation& operation) { namespace exceptions_internal { -// Dummy struct for ExceptionSafetyTester<> partial state. +// Dummy struct for ExceptionSafetyTestBuilder<> partial state. struct UninitializedT {}; template @@ -884,29 +827,106 @@ class DefaultFactory { T t_; }; -template using EnableIfTestable = typename absl::enable_if_t< - LazyInvariantsCount != 0 && + LazyContractsCount != 0 && !std::is_same::value && !std::is_same::value>; template -class ExceptionSafetyTester; + typename Operation = UninitializedT, typename... Contracts> +class ExceptionSafetyTestBuilder; } // namespace exceptions_internal -exceptions_internal::ExceptionSafetyTester<> MakeExceptionSafetyTester(); +/* + * Constructs an empty ExceptionSafetyTestBuilder. All + * ExceptionSafetyTestBuilder objects are immutable and all With[thing] mutation + * methods return new instances of ExceptionSafetyTestBuilder. + * + * In order to test a T for exception safety, a factory for that T, a testable + * operation, and at least one contract callback returning an assertion + * result must be applied using the respective methods. + */ +exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester(); namespace exceptions_internal { +template +struct IsUniquePtr : std::false_type {}; + +template +struct IsUniquePtr> : std::true_type {}; + +template +struct FactoryPtrTypeHelper { + using type = decltype(std::declval()()); + + static_assert(IsUniquePtr::value, "Factories must return a unique_ptr"); +}; + +template +using FactoryPtrType = typename FactoryPtrTypeHelper::type; + +template +using FactoryElementType = typename FactoryPtrType::element_type; + +template +class ExceptionSafetyTest { + using Factory = std::function()>; + using Operation = std::function; + using Contract = std::function; + + public: + template + explicit ExceptionSafetyTest(const Factory& f, const Operation& op, + const Contracts&... contracts) + : factory_(f), operation_(op), contracts_{WrapContract(contracts)...} {} + + AssertionResult Test() const { + for (int count = 0;; ++count) { + exceptions_internal::ConstructorTracker ct(count); + + for (const auto& contract : contracts_) { + auto t_ptr = factory_(); + try { + SetCountdown(count); + operation_(t_ptr.get()); + // Unset for the case that the operation throws no exceptions, which + // would leave the countdown set and break the *next* exception safety + // test after this one. + UnsetCountdown(); + return AssertionSuccess(); + } catch (const exceptions_internal::TestException& e) { + if (!contract(t_ptr.get())) { + return AssertionFailure() << e.what() << " failed contract check"; + } + } + } + } + } + + private: + template + Contract WrapContract(const ContractFn& contract) { + return [contract](T* t_ptr) { return AssertionResult(contract(t_ptr)); }; + } + + Contract WrapContract(StrongGuaranteeTagType) { + return [this](T* t_ptr) { return AssertionResult(*factory_() == *t_ptr); }; + } + + Factory factory_; + Operation operation_; + std::vector contracts_; +}; /* * Builds a tester object that tests if performing a operation on a T follows - * exception safety guarantees. Verification is done via invariant assertion + * exception safety guarantees. Verification is done via contract assertion * callbacks applied to T instances post-throw. * - * Template parameters for ExceptionSafetyTester: + * Template parameters for ExceptionSafetyTestBuilder: * * - Factory: The factory object (passed in via tester.WithFactory(...) or * tester.WithInitialValue(...)) must be invocable with the signature @@ -921,25 +941,25 @@ namespace exceptions_internal { * fresh T instance so it's free to modify and destroy the T instances as it * pleases. * - * - Invariants...: The invariant assertion callback objects (passed in via - * tester.WithInvariants(...)) must be invocable with the signature + * - Contracts...: The contract assertion callback objects (passed in via + * tester.WithContracts(...)) must be invocable with the signature * `testing::AssertionResult operator()(T*) const` where T is the type being - * tested. Invariant assertion callbacks are provided T instances post-throw. - * They must return testing::AssertionSuccess when the type invariants of the - * provided T instance hold. If the type invariants of the T instance do not + * tested. Contract assertion callbacks are provided T instances post-throw. + * They must return testing::AssertionSuccess when the type contracts of the + * provided T instance hold. If the type contracts of the T instance do not * hold, they must return testing::AssertionFailure. Execution order of - * Invariants... is unspecified. They will each individually get a fresh T + * Contracts... is unspecified. They will each individually get a fresh T * instance so they are free to modify and destroy the T instances as they * please. */ -template -class ExceptionSafetyTester { +template +class ExceptionSafetyTestBuilder { public: /* - * Returns a new ExceptionSafetyTester with an included T factory based on the - * provided T instance. The existing factory will not be included in the newly - * created tester instance. The created factory returns a new T instance by - * copy-constructing the provided const T& t. + * Returns a new ExceptionSafetyTestBuilder with an included T factory based + * on the provided T instance. The existing factory will not be included in + * the newly created tester instance. The created factory returns a new T + * instance by copy-constructing the provided const T& t. * * Preconditions for tester.WithInitialValue(const T& t): * @@ -948,63 +968,63 @@ class ExceptionSafetyTester { * tester.WithFactory(...). */ template - ExceptionSafetyTester, Operation, Invariants...> + ExceptionSafetyTestBuilder, Operation, Contracts...> WithInitialValue(const T& t) const { return WithFactory(DefaultFactory(t)); } /* - * Returns a new ExceptionSafetyTester with the provided T factory included. - * The existing factory will not be included in the newly-created tester - * instance. This method is intended for use with types lacking a copy + * Returns a new ExceptionSafetyTestBuilder with the provided T factory + * included. The existing factory will not be included in the newly-created + * tester instance. This method is intended for use with types lacking a copy * constructor. Types that can be copy-constructed should instead use the * method tester.WithInitialValue(...). */ template - ExceptionSafetyTester, Operation, Invariants...> + ExceptionSafetyTestBuilder, Operation, Contracts...> WithFactory(const NewFactory& new_factory) const { - return {new_factory, operation_, invariants_}; + return {new_factory, operation_, contracts_}; } /* - * Returns a new ExceptionSafetyTester with the provided testable operation - * included. The existing operation will not be included in the newly created - * tester. + * Returns a new ExceptionSafetyTestBuilder with the provided testable + * operation included. The existing operation will not be included in the + * newly created tester. */ template - ExceptionSafetyTester, Invariants...> + ExceptionSafetyTestBuilder, Contracts...> WithOperation(const NewOperation& new_operation) const { - return {factory_, new_operation, invariants_}; + return {factory_, new_operation, contracts_}; } /* - * Returns a new ExceptionSafetyTester with the provided MoreInvariants... - * combined with the Invariants... that were already included in the instance - * on which the method was called. Invariants... cannot be removed or replaced - * once added to an ExceptionSafetyTester instance. A fresh object must be - * created in order to get an empty Invariants... list. + * Returns a new ExceptionSafetyTestBuilder with the provided MoreContracts... + * combined with the Contracts... that were already included in the instance + * on which the method was called. Contracts... cannot be removed or replaced + * once added to an ExceptionSafetyTestBuilder instance. A fresh object must + * be created in order to get an empty Contracts... list. * - * In addition to passing in custom invariant assertion callbacks, this method + * In addition to passing in custom contract assertion callbacks, this method * accepts `testing::strong_guarantee` as an argument which checks T instances * post-throw against freshly created T instances via operator== to verify * that any state changes made during the execution of the operation were * properly rolled back. */ - template - ExceptionSafetyTester...> - WithInvariants(const MoreInvariants&... more_invariants) const { - return {factory_, operation_, - std::tuple_cat(invariants_, - std::tuple...>( - more_invariants...))}; + template + ExceptionSafetyTestBuilder...> + WithContracts(const MoreContracts&... more_contracts) const { + return { + factory_, operation_, + std::tuple_cat(contracts_, std::tuple...>( + more_contracts...))}; } /* * Returns a testing::AssertionResult that is the reduced result of the * exception safety algorithm. The algorithm short circuits and returns - * AssertionFailure after the first invariant callback returns an - * AssertionFailure. Otherwise, if all invariant callbacks return an + * AssertionFailure after the first contract callback returns an + * AssertionFailure. Otherwise, if all contract callbacks return an * AssertionSuccess, the reduced result is AssertionSuccess. * * The passed-in testable operation will not be saved in a new tester instance @@ -1013,97 +1033,62 @@ class ExceptionSafetyTester { * * Preconditions for tester.Test(const NewOperation& new_operation): * - * - May only be called after at least one invariant assertion callback and a + * - May only be called after at least one contract assertion callback and a * factory or initial value have been provided. */ template < typename NewOperation, - typename = EnableIfTestable> + typename = EnableIfTestable> testing::AssertionResult Test(const NewOperation& new_operation) const { - return TestImpl(new_operation, absl::index_sequence_for()); + return TestImpl(new_operation, absl::index_sequence_for()); } /* * Returns a testing::AssertionResult that is the reduced result of the * exception safety algorithm. The algorithm short circuits and returns - * AssertionFailure after the first invariant callback returns an - * AssertionFailure. Otherwise, if all invariant callbacks return an + * AssertionFailure after the first contract callback returns an + * AssertionFailure. Otherwise, if all contract callbacks return an * AssertionSuccess, the reduced result is AssertionSuccess. * * Preconditions for tester.Test(): * - * - May only be called after at least one invariant assertion callback, a + * - May only be called after at least one contract assertion callback, a * factory or initial value and a testable operation have been provided. */ - template > + template < + typename LazyOperation = Operation, + typename = EnableIfTestable> testing::AssertionResult Test() const { - return TestImpl(operation_, absl::index_sequence_for()); + return Test(operation_); } private: template - friend class ExceptionSafetyTester; + friend class ExceptionSafetyTestBuilder; - friend ExceptionSafetyTester<> testing::MakeExceptionSafetyTester(); + friend ExceptionSafetyTestBuilder<> testing::MakeExceptionSafetyTester(); - ExceptionSafetyTester() {} + ExceptionSafetyTestBuilder() {} - ExceptionSafetyTester(const Factory& f, const Operation& o, - const std::tuple& i) - : factory_(f), operation_(o), invariants_(i) {} + ExceptionSafetyTestBuilder(const Factory& f, const Operation& o, + const std::tuple& i) + : factory_(f), operation_(o), contracts_(i) {} template - testing::AssertionResult TestImpl(const SelectedOperation& selected_operation, + testing::AssertionResult TestImpl(SelectedOperation selected_operation, absl::index_sequence) const { - // Starting from 0 and counting upwards until one of the exit conditions is - // hit... - for (int count = 0;; ++count) { - exceptions_internal::ConstructorTracker ct(count); - - // Run the full exception safety test algorithm for the current countdown - auto reduced_res = - TestAllInvariantsAtCountdown(factory_, selected_operation, count, - std::get(invariants_)...); - // If there is no value in the optional, no invariants were run because no - // exception was thrown. This means that the test is complete and the loop - // can exit successfully. - if (!reduced_res.has_value()) { - return testing::AssertionSuccess(); - } - // If the optional is not empty and the value is falsy, an invariant check - // failed so the test must exit to propegate the failure. - if (!reduced_res.value()) { - return reduced_res.value(); - } - // If the optional is not empty and the value is not falsy, it means - // exceptions were thrown but the invariants passed so the test must - // continue to run. - } + return ExceptionSafetyTest>( + factory_, selected_operation, std::get(contracts_)...) + .Test(); } Factory factory_; Operation operation_; - std::tuple invariants_; + std::tuple contracts_; }; } // namespace exceptions_internal -/* - * Constructs an empty ExceptionSafetyTester. All ExceptionSafetyTester - * objects are immutable and all With[thing] mutation methods return new - * instances of ExceptionSafetyTester. - * - * In order to test a T for exception safety, a factory for that T, a testable - * operation, and at least one invariant callback returning an assertion - * result must be applied using the respective methods. - */ -inline exceptions_internal::ExceptionSafetyTester<> -MakeExceptionSafetyTester() { - return {}; -} - } // namespace testing #endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ diff --git a/absl/base/internal/exception_testing.h b/absl/base/internal/exception_testing.h index fd89a3f6268..0cf7918e4c6 100644 --- a/absl/base/internal/exception_testing.h +++ b/absl/base/internal/exception_testing.h @@ -35,7 +35,7 @@ EXPECT_DEATH(expr, ".*") #else #define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \ - EXPECT_DEATH(expr, text) + EXPECT_DEATH_IF_SUPPORTED(expr, text) #endif diff --git a/absl/base/internal/hide_ptr.h b/absl/base/internal/hide_ptr.h index a2694508ba1..ce390dc41be 100644 --- a/absl/base/internal/hide_ptr.h +++ b/absl/base/internal/hide_ptr.h @@ -18,7 +18,7 @@ #include namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_ diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h index 2eaab453306..d57c83f4381 100644 --- a/absl/base/internal/identity.h +++ b/absl/base/internal/identity.h @@ -17,7 +17,7 @@ #define ABSL_BASE_INTERNAL_IDENTITY_H_ namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace internal { template @@ -29,7 +29,7 @@ template using identity_t = typename identity::type; } // namespace internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INTERNAL_IDENTITY_H_ diff --git a/absl/base/internal/inline_variable_testing.h b/absl/base/internal/inline_variable_testing.h index 49fa4ade38b..be0b0b96d5a 100644 --- a/absl/base/internal/inline_variable_testing.h +++ b/absl/base/internal/inline_variable_testing.h @@ -18,7 +18,7 @@ #include "absl/base/internal/inline_variable.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_ diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index bc05a0a394c..1372ef50dca 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -43,7 +43,7 @@ // top of this file for the API documentation. namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { // The five classes below each implement one of the clauses from the definition @@ -184,7 +184,7 @@ InvokeT Invoke(F&& f, Args&&... args) { std::forward(args)...); } } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // 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 ca6239ad3cf..10d805cc635 100644 --- a/absl/base/internal/low_level_alloc.cc +++ b/absl/base/internal/low_level_alloc.cc @@ -63,7 +63,7 @@ #endif // __APPLE__ namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { // A first-fit allocator with amortized logarithmic free() time. @@ -209,7 +209,7 @@ struct LowLevelAlloc::Arena { int32_t allocation_count GUARDED_BY(mu); // flags passed to NewArena const uint32_t flags; - // Result of getpagesize() + // Result of sysconf(_SC_PAGESIZE) const size_t pagesize; // Lowest power of two >= max(16, sizeof(AllocList)) const size_t roundup; @@ -325,8 +325,10 @@ size_t GetPageSize() { SYSTEM_INFO system_info; GetSystemInfo(&system_info); return std::max(system_info.dwPageSize, system_info.dwAllocationGranularity); -#else +#elif defined(__wasm__) || defined(__asmjs__) return getpagesize(); +#else + return sysconf(_SC_PAGESIZE); #endif } @@ -402,16 +404,20 @@ bool LowLevelAlloc::DeleteArena(Arena *arena) { ABSL_RAW_CHECK(munmap_result != 0, "LowLevelAlloc::DeleteArena: VitualFree failed"); #else +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) { munmap_result = munmap(region, size); } else { munmap_result = base_internal::DirectMunmap(region, size); } +#else + munmap_result = munmap(region, size); +#endif // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if (munmap_result != 0) { ABSL_RAW_LOG(FATAL, "LowLevelAlloc::DeleteArena: munmap failed: %d", errno); } -#endif +#endif // _WIN32 } section.Leave(); arena->~Arena(); @@ -546,6 +552,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); ABSL_RAW_CHECK(new_pages != nullptr, "VirtualAlloc failed"); #else +#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) { new_pages = base_internal::DirectMmap(nullptr, new_pages_size, PROT_WRITE|PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); @@ -553,10 +560,15 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); } +#else + new_pages = mmap(nullptr, new_pages_size, PROT_WRITE | PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); +#endif // ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if (new_pages == MAP_FAILED) { ABSL_RAW_LOG(FATAL, "mmap error: %d", errno); } -#endif + +#endif // _WIN32 arena->mu.Lock(); s = reinterpret_cast(new_pages); s->header.size = new_pages_size; @@ -600,7 +612,7 @@ void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) { } } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // 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 3f289571c18..87cfc9349bf 100644 --- a/absl/base/internal/low_level_alloc.h +++ b/absl/base/internal/low_level_alloc.h @@ -39,10 +39,13 @@ #define ABSL_LOW_LEVEL_ALLOC_MISSING 1 #endif -// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows. +// Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows or +// asm.js / WebAssembly. +// See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html +// for more information. #ifdef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING #error ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING cannot be directly set -#elif defined(_WIN32) +#elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__) #define ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING 1 #endif @@ -51,7 +54,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { class LowLevelAlloc { @@ -116,6 +119,6 @@ class LowLevelAlloc { }; } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // 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 15ffe298ae3..65bb519d922 100644 --- a/absl/base/internal/low_level_alloc_test.cc +++ b/absl/base/internal/low_level_alloc_test.cc @@ -22,7 +22,7 @@ #include namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { namespace { @@ -149,7 +149,7 @@ static struct BeforeMain { } // namespace } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // 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 2ae464b44f0..7cb6117e754 100644 --- a/absl/base/internal/low_level_scheduling.h +++ b/absl/base/internal/low_level_scheduling.h @@ -28,7 +28,7 @@ extern "C" bool __google_disable_rescheduling(void); extern "C" void __google_enable_rescheduling(bool disable_result); namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { class SchedulingHelper; // To allow use of SchedulingGuard. @@ -101,6 +101,6 @@ inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index cd1bbc0942e..ed8b8d7c993 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -139,7 +139,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, #endif #ifdef ABSL_MIN_LOG_LEVEL - if (static_cast(severity) < ABSL_MIN_LOG_LEVEL && + if (severity < static_cast(ABSL_MIN_LOG_LEVEL) && severity < absl::LogSeverity::kFatal) { enabled = false; } @@ -181,7 +181,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } // namespace namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace raw_logging_internal { void SafeWriteToStderr(const char *s, size_t len) { #if defined(ABSL_HAVE_SYSCALL_WRITE) @@ -207,6 +207,15 @@ void RawLog(absl::LogSeverity severity, const char* file, int line, va_end(ap); } +// Non-formatting version of RawLog(). +// +// TODO(gfalcon): When string_view no longer depends on base, change this +// interface to take its message as a string_view instead. +static void DefaultInternalLog(absl::LogSeverity severity, const char* file, + int line, const std::string& message) { + RawLog(severity, file, line, "%s", message.c_str()); +} + bool RawLoggingFullySupported() { #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED return true; @@ -215,6 +224,13 @@ bool RawLoggingFullySupported() { #endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED } +ABSL_CONST_INIT absl::base_internal::AtomicHook + internal_log_function(DefaultInternalLog); + +void RegisterInternalLogFunction(InternalLogFunction func) { + internal_log_function.Store(func); +} + } // namespace raw_logging_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index 04ff0607a3a..2786a3de231 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -19,7 +19,10 @@ #ifndef ABSL_BASE_INTERNAL_RAW_LOGGING_H_ #define ABSL_BASE_INTERNAL_RAW_LOGGING_H_ +#include + #include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook.h" #include "absl/base/log_severity.h" #include "absl/base/macros.h" #include "absl/base/port.h" @@ -57,6 +60,34 @@ } \ } while (0) +// ABSL_INTERNAL_LOG and ABSL_INTERNAL_CHECK work like the RAW variants above, +// except that if the richer log library is linked into the binary, we dispatch +// to that instead. This is potentially useful for internal logging and +// assertions, where we are using RAW_LOG neither for its async-signal-safety +// nor for its non-allocating nature, but rather because raw logging has very +// few other dependencies. +// +// The API is a subset of the above: each macro only takes two arguments. Use +// StrCat if you need to build a richer message. +#define ABSL_INTERNAL_LOG(severity, message) \ + do { \ + constexpr const char* absl_raw_logging_internal_basename = \ + ::absl::raw_logging_internal::Basename(__FILE__, \ + sizeof(__FILE__) - 1); \ + ::absl::raw_logging_internal::internal_log_function( \ + ABSL_RAW_LOGGING_INTERNAL_##severity, \ + absl_raw_logging_internal_basename, __LINE__, message); \ + } while (0) + +#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); \ + } \ + } while (0) + #define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo #define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning #define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError @@ -65,7 +96,7 @@ ::absl::NormalizeLogSeverity(severity) namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace raw_logging_internal { // Helper function to implement ABSL_RAW_LOG @@ -84,7 +115,7 @@ void SafeWriteToStderr(const char *s, size_t len); // compile-time function to get the "base" filename, that is, the part of // a filename after the last "/" or "\" path separator. The search starts at -// the end of the std::string; the second parameter is the length of the std::string. +// the end of the string; the second parameter is the length of the string. constexpr const char* Basename(const char* fname, int offset) { return offset == 0 || fname[offset - 1] == '/' || fname[offset - 1] == '\\' ? fname + offset @@ -132,8 +163,20 @@ using LogPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, using AbortHook = void (*)(const char* file, int line, const char* buf_start, const char* prefix_end, const char* buf_end); +// Internal logging function for ABSL_INTERNAL_LOG to dispatch to. +// +// TODO(gfalcon): When string_view no longer depends on base, change this +// interface to take its message as a string_view instead. +using InternalLogFunction = void (*)(absl::LogSeverity severity, + const char* file, int line, + const std::string& message); + +extern base_internal::AtomicHook internal_log_function; + +void RegisterInternalLogFunction(InternalLogFunction func); + } // namespace raw_logging_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // 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 dd7df6bb12f..19a7514c1c2 100644 --- a/absl/base/internal/scheduling_mode.h +++ b/absl/base/internal/scheduling_mode.h @@ -19,7 +19,7 @@ #define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc index 9d90b3cb40b..8f8eef82e0d 100644 --- a/absl/base/internal/spinlock.cc +++ b/absl/base/internal/spinlock.cc @@ -54,7 +54,7 @@ // holder to acquire the lock. There may be outstanding waiter(s). namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { ABSL_CONST_INIT static base_internal::AtomicHook 0); - uint32_t spin_loop_wait_cycles = - EncodeWaitCycles(initial_wait_timestamp, CycleClock::Now()); - *wait_cycles = spin_loop_wait_cycles; - - return TryLockInternal(lock_value, spin_loop_wait_cycles); + return lock_value; } void SpinLock::SlowLock() { + uint32_t lock_value = SpinLoop(); + lock_value = TryLockInternal(lock_value, 0); + if ((lock_value & kSpinLockHeld) == 0) { + return; + } // The lock was not obtained initially, so this thread needs to wait for // it. Record the current timestamp in the local variable wait_start_time // so the total wait time can be stored in the lockword once this thread // obtains the lock. int64_t wait_start_time = CycleClock::Now(); - uint32_t wait_cycles; - uint32_t lock_value = SpinLoop(wait_start_time, &wait_cycles); - + uint32_t wait_cycles = 0; int lock_wait_call_count = 0; while ((lock_value & kSpinLockHeld) != 0) { // If the lock is currently held, but not marked as having a sleeper, mark @@ -142,7 +137,7 @@ void SpinLock::SlowLock() { // owner to think it experienced contention. if (lockword_.compare_exchange_strong( lock_value, lock_value | kSpinLockSleeper, - std::memory_order_acquire, std::memory_order_relaxed)) { + std::memory_order_relaxed, std::memory_order_relaxed)) { // Successfully transitioned to kSpinLockSleeper. Pass // kSpinLockSleeper to the SpinLockWait routine to properly indicate // the last lock_value observed. @@ -171,7 +166,9 @@ void SpinLock::SlowLock() { ABSL_TSAN_MUTEX_POST_DIVERT(this, 0); // Spin again after returning from the wait routine to give this thread // some chance of obtaining the lock. - lock_value = SpinLoop(wait_start_time, &wait_cycles); + lock_value = SpinLoop(); + wait_cycles = EncodeWaitCycles(wait_start_time, CycleClock::Now()); + lock_value = TryLockInternal(lock_value, wait_cycles); } } @@ -207,14 +204,20 @@ uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time, (wait_end_time - wait_start_time) >> PROFILE_TIMESTAMP_SHIFT; // Return a representation of the time spent waiting that can be stored in - // the lock word's upper bits. bit_cast is required as Atomic32 is signed. - const uint32_t clamped = static_cast( + // the lock word's upper bits. + uint32_t clamped = static_cast( std::min(scaled_wait_time, kMaxWaitTime) << LOCKWORD_RESERVED_SHIFT); - // bump up value if necessary to avoid returning kSpinLockSleeper. - const uint32_t after_spinlock_sleeper = - kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT); - return clamped == kSpinLockSleeper ? after_spinlock_sleeper : clamped; + if (clamped == 0) { + return kSpinLockSleeper; // Just wake waiters, but don't record contention. + } + // Bump up value if necessary to avoid returning kSpinLockSleeper. + const uint32_t kMinWaitTime = + kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT); + if (clamped == kSpinLockSleeper) { + return kMinWaitTime; + } + return clamped; } uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) { @@ -226,5 +229,5 @@ uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) { } } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h index 7b59fe260ab..d53878b2e0f 100644 --- a/absl/base/internal/spinlock.h +++ b/absl/base/internal/spinlock.h @@ -45,7 +45,7 @@ #include "absl/base/thread_annotations.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { class LOCKABLE SpinLock { @@ -102,8 +102,8 @@ class LOCKABLE SpinLock { inline void Unlock() UNLOCK_FUNCTION() { ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0); uint32_t lock_value = lockword_.load(std::memory_order_relaxed); - lockword_.store(lock_value & kSpinLockCooperative, - std::memory_order_release); + lock_value = lockword_.exchange(lock_value & kSpinLockCooperative, + std::memory_order_release); if ((lock_value & kSpinLockDisabledScheduling) != 0) { base_internal::SchedulingGuard::EnableRescheduling(true); @@ -162,7 +162,7 @@ class LOCKABLE SpinLock { void InitLinkerInitializedAndCooperative(); void SlowLock() ABSL_ATTRIBUTE_COLD; void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD; - uint32_t SpinLoop(int64_t initial_wait_timestamp, uint32_t* wait_cycles); + uint32_t SpinLoop(); inline bool TryLockImpl() { uint32_t lock_value = lockword_.load(std::memory_order_relaxed); @@ -235,7 +235,7 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, } } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INTERNAL_SPINLOCK_H_ diff --git a/absl/base/internal/spinlock_benchmark.cc b/absl/base/internal/spinlock_benchmark.cc new file mode 100644 index 00000000000..907d3e2745b --- /dev/null +++ b/absl/base/internal/spinlock_benchmark.cc @@ -0,0 +1,52 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// See also //absl/synchronization:mutex_benchmark for a comparison of SpinLock +// and Mutex performance under varying levels of contention. + +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/scheduling_mode.h" +#include "absl/base/internal/spinlock.h" +#include "absl/synchronization/internal/create_thread_identity.h" +#include "benchmark/benchmark.h" + +namespace { + +template +static void BM_SpinLock(benchmark::State& state) { + // Ensure a ThreadIdentity is installed. + ABSL_INTERNAL_CHECK( + absl::synchronization_internal::GetOrCreateCurrentThreadIdentity() != + nullptr, + "GetOrCreateCurrentThreadIdentity() failed"); + + static auto* spinlock = new absl::base_internal::SpinLock(scheduling_mode); + for (auto _ : state) { + absl::base_internal::SpinLockHolder holder(spinlock); + } +} + +BENCHMARK_TEMPLATE(BM_SpinLock, + absl::base_internal::SCHEDULE_KERNEL_ONLY) + ->UseRealTime() + ->Threads(1) + ->ThreadPerCpu(); + +BENCHMARK_TEMPLATE(BM_SpinLock, + absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) + ->UseRealTime() + ->Threads(1) + ->ThreadPerCpu(); + +} // namespace diff --git a/absl/base/internal/spinlock_linux.inc b/absl/base/internal/spinlock_linux.inc new file mode 100644 index 00000000000..94c861dc6ca --- /dev/null +++ b/absl/base/internal/spinlock_linux.inc @@ -0,0 +1,72 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 a Linux-specific part of spinlock_wait.cc + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" + +// The SpinLock lockword is `std::atomic`. Here we assert that +// `std::atomic` is bitwise equivalent of the `int` expected +// by SYS_futex. We also assume that reads/writes done to the lockword +// by SYS_futex have rational semantics with regard to the +// std::atomic<> API. C++ provides no guarantees of these assumptions, +// but they are believed to hold in practice. +static_assert(sizeof(std::atomic) == sizeof(int), + "SpinLock lockword has the wrong size for a futex"); + +// Some Android headers are missing these definitions even though they +// support these futex operations. +#ifdef __BIONIC__ +#ifndef SYS_futex +#define SYS_futex __NR_futex +#endif +#ifndef FUTEX_PRIVATE_FLAG +#define FUTEX_PRIVATE_FLAG 128 +#endif +#endif + +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; + } +} + +ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic *w, + bool all) { + syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0); +} + +} // extern "C" diff --git a/absl/base/internal/spinlock_wait.cc b/absl/base/internal/spinlock_wait.cc index 6709d01e922..3f8e43f002e 100644 --- a/absl/base/internal/spinlock_wait.cc +++ b/absl/base/internal/spinlock_wait.cc @@ -13,7 +13,7 @@ // limitations under the License. // The OS-specific header included below must provide two calls: -// base::subtle::SpinLockDelay() and base::subtle::SpinLockWake(). +// AbslInternalSpinLockDelay() and AbslInternalSpinLockWake(). // See spinlock_wait.h for the specs. #include @@ -23,6 +23,8 @@ #if defined(_WIN32) #include "absl/base/internal/spinlock_win32.inc" +#elif defined(__linux__) +#include "absl/base/internal/spinlock_linux.inc" #elif defined(__akaros__) #include "absl/base/internal/spinlock_akaros.inc" #else @@ -30,21 +32,22 @@ #endif namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { // See spinlock_wait.h for spec. uint32_t SpinLockWait(std::atomic *w, int n, const SpinLockWaitTransition trans[], base_internal::SchedulingMode scheduling_mode) { - for (int loop = 0; ; loop++) { + int loop = 0; + for (;;) { uint32_t v = w->load(std::memory_order_acquire); int i; for (i = 0; i != n && v != trans[i].from; i++) { } if (i == n) { - SpinLockDelay(w, v, loop, scheduling_mode); // no matching transition - } else if (trans[i].to == v || // null transition + SpinLockDelay(w, v, ++loop, scheduling_mode); // no matching transition + } else if (trans[i].to == v || // null transition w->compare_exchange_strong(v, trans[i].to, std::memory_order_acquire, std::memory_order_relaxed)) { @@ -77,5 +80,5 @@ int SpinLockSuggestedDelayNS(int loop) { } } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h index 31aaa8c68cc..5eb727f101b 100644 --- a/absl/base/internal/spinlock_wait.h +++ b/absl/base/internal/spinlock_wait.h @@ -24,7 +24,7 @@ #include "absl/base/internal/scheduling_mode.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl // In some build configurations we pass --detect-odr-violations to the diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index 7db2e001515..ce14fc0f61e 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -56,7 +56,7 @@ #include "absl/base/internal/unscaledcycleclock.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/sysinfo.h b/absl/base/internal/sysinfo.h index 18aa2e2931b..79100f6151e 100644 --- a/absl/base/internal/sysinfo.h +++ b/absl/base/internal/sysinfo.h @@ -33,7 +33,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // 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 fdbbdf8841d..c072ebc2c5a 100644 --- a/absl/base/internal/sysinfo_test.cc +++ b/absl/base/internal/sysinfo_test.cc @@ -28,7 +28,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { namespace { @@ -96,5 +96,5 @@ TEST(SysinfoTest, LinuxGetTID) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc index aa1add8bc1d..b35bee34798 100644 --- a/absl/base/internal/thread_identity.cc +++ b/absl/base/internal/thread_identity.cc @@ -28,7 +28,7 @@ #include "absl/base/internal/spinlock.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { #if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11 @@ -69,6 +69,14 @@ void SetCurrentThreadIdentity( // NOTE: Not async-safe. But can be open-coded. absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey, reclaimer); + +#ifdef __EMSCRIPTEN__ + // Emscripten PThread implementation does not support signals. + // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html + // for more information. + pthread_setspecific(thread_identity_pthread_key, + reinterpret_cast(identity)); +#else // We must mask signals around the call to setspecific as with current glibc, // a concurrent getspecific (needed for GetCurrentThreadIdentityIfPresent()) // may zero our value. @@ -82,6 +90,8 @@ void SetCurrentThreadIdentity( pthread_setspecific(thread_identity_pthread_key, reinterpret_cast(identity)); pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr); +#endif // !__EMSCRIPTEN__ + #elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS // NOTE: Not async-safe. But can be open-coded. absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey, @@ -121,5 +131,5 @@ ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index 18a5a75049b..17ac2a7ca7d 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -33,7 +33,7 @@ #include "absl/base/internal/per_thread_tls.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { struct SynchLocksHeld; struct SynchWaitParams; @@ -237,6 +237,6 @@ inline ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc index f39f11d23b1..ec93fc27200 100644 --- a/absl/base/internal/thread_identity_test.cc +++ b/absl/base/internal/thread_identity_test.cc @@ -25,7 +25,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { namespace { @@ -124,5 +124,5 @@ TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/throw_delegate.cc b/absl/base/internal/throw_delegate.cc index 5466e0f3780..0f73c3ebed5 100644 --- a/absl/base/internal/throw_delegate.cc +++ b/absl/base/internal/throw_delegate.cc @@ -22,7 +22,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { namespace { @@ -31,8 +31,8 @@ template #ifdef ABSL_HAVE_EXCEPTIONS throw error; #else - ABSL_RAW_LOG(ERROR, "%s", error.what()); - abort(); + ABSL_RAW_LOG(FATAL, "%s", error.what()); + std::abort(); #endif } } // 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_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/internal/throw_delegate.h b/absl/base/internal/throw_delegate.h index d34ff79cac7..7e5510c092e 100644 --- a/absl/base/internal/throw_delegate.h +++ b/absl/base/internal/throw_delegate.h @@ -20,7 +20,7 @@ #include namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h index f20a869470e..07a64bba784 100644 --- a/absl/base/internal/unaligned_access.h +++ b/absl/base/internal/unaligned_access.h @@ -65,7 +65,8 @@ void __sanitizer_unaligned_store64(void *p, uint64_t v); } // extern "C" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { +namespace base_internal { inline uint16_t UnalignedLoad16(const void *p) { return __sanitizer_unaligned_load16(p); @@ -91,19 +92,71 @@ inline void UnalignedStore64(void *p, uint64_t v) { __sanitizer_unaligned_store64(p, v); } -} // inline namespace lts_2018_06_20 +} // namespace base_internal +} // inline namespace lts_2018_12_18 } // namespace absl -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) (absl::UnalignedLoad16(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) (absl::UnalignedLoad32(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) (absl::UnalignedLoad64(_p)) +#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(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::UnalignedStore16(_p, _val)) + (absl::base_internal::UnalignedStore16(_p, _val)) #define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - (absl::UnalignedStore32(_p, _val)) + (absl::base_internal::UnalignedStore32(_p, _val)) #define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (absl::UnalignedStore64(_p, _val)) + (absl::base_internal::UnalignedStore64(_p, _val)) #elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \ defined(_M_IX86) || defined(__ppc__) || defined(__PPC__) || \ @@ -160,8 +213,8 @@ inline void UnalignedStore64(void *p, uint64_t v) { // so we do that. namespace absl { -inline namespace lts_2018_06_20 { -namespace internal { +inline namespace lts_2018_12_18 { +namespace base_internal { struct Unaligned16Struct { uint16_t value; @@ -173,24 +226,27 @@ struct Unaligned32Struct { uint8_t dummy; // To make the size non-power-of-two. } ABSL_ATTRIBUTE_PACKED; -} // namespace internal -} // inline namespace lts_2018_06_20 +} // 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_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::internal::Unaligned16Struct *>(_p))->value = \ - (_val)) -#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - ((reinterpret_cast< ::absl::internal::Unaligned32Struct *>(_p))->value = \ - (_val)) +#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_06_20 { +inline namespace lts_2018_12_18 { +namespace base_internal { inline uint64_t UnalignedLoad64(const void *p) { uint64_t t; @@ -200,12 +256,14 @@ inline uint64_t UnalignedLoad64(const void *p) { inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } -} // inline namespace lts_2018_06_20 +} // namespace base_internal +} // inline namespace lts_2018_12_18 } // namespace absl -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) (absl::UnalignedLoad64(_p)) +#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ + (absl::base_internal::UnalignedLoad64(_p)) #define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (absl::UnalignedStore64(_p, _val)) + (absl::base_internal::UnalignedStore64(_p, _val)) #else @@ -217,7 +275,8 @@ inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } // unaligned loads and stores. namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { +namespace base_internal { inline uint16_t UnalignedLoad16(const void *p) { uint16_t t; @@ -243,19 +302,23 @@ 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); } -} // inline namespace lts_2018_06_20 +} // namespace base_internal +} // inline namespace lts_2018_12_18 } // namespace absl -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) (absl::UnalignedLoad16(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) (absl::UnalignedLoad32(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) (absl::UnalignedLoad64(_p)) +#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::UnalignedStore16(_p, _val)) + (absl::base_internal::UnalignedStore16(_p, _val)) #define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - (absl::UnalignedStore32(_p, _val)) + (absl::base_internal::UnalignedStore32(_p, _val)) #define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (absl::UnalignedStore64(_p, _val)) + (absl::base_internal::UnalignedStore64(_p, _val)) #endif diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index e0798eb998d..888caf1e7b3 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -27,7 +27,7 @@ #include "absl/base/internal/sysinfo.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { #if defined(__i386__) @@ -97,7 +97,7 @@ double UnscaledCycleClock::Frequency() { #endif } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_USE_UNSCALED_CYCLECLOCK diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index 9da14d0b463..c71674f3497 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -84,7 +84,7 @@ #define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY #endif namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace time_internal { class UnscaledCycleClockWrapperForGetCurrentTime; } // namespace time_internal @@ -114,7 +114,7 @@ class UnscaledCycleClock { }; } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_USE_UNSCALED_CYCLECLOCK diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc index 2c04b591000..4df637ac5bc 100644 --- a/absl/base/invoke_test.cc +++ b/absl/base/invoke_test.cc @@ -25,7 +25,7 @@ #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { namespace { @@ -198,5 +198,5 @@ TEST(InvokeTest, SfinaeFriendly) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h index 8b7a66ba89c..c24fad79008 100644 --- a/absl/base/log_severity.h +++ b/absl/base/log_severity.h @@ -21,7 +21,7 @@ #include "absl/base/attributes.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { // Four severity levels are defined. Logging APIs should terminate the program // when a message is logged at severity `kFatal`; the other levels have no @@ -40,7 +40,7 @@ constexpr std::array LogSeverities() { absl::LogSeverity::kError, absl::LogSeverity::kFatal}}; } -// Returns the all-caps std::string representation (e.g. "INFO") of the specified +// Returns the all-caps string representation (e.g. "INFO") of the specified // severity level if it is one of the normal levels and "UNKNOWN" otherwise. constexpr const char* LogSeverityName(absl::LogSeverity s) { return s == absl::LogSeverity::kInfo @@ -63,7 +63,7 @@ constexpr absl::LogSeverity NormalizeLogSeverity(int s) { return NormalizeLogSeverity(static_cast(s)); } -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ diff --git a/absl/base/macros.h b/absl/base/macros.h index aabe8db4825..14c4b0a3638 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h @@ -43,14 +43,14 @@ (sizeof(::absl::macros_internal::ArraySizeHelper(array))) namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { 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_06_20 +} // inline namespace lts_2018_12_18 } // 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_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { enum LinkerInitialized { kLinkerInitialized = 0, }; } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl // ABSL_FALLTHROUGH_INTENDED @@ -203,4 +203,14 @@ enum LinkerInitialized { : [] { assert(false && #expr); }()) // NOLINT #endif +#ifdef ABSL_HAVE_EXCEPTIONS +#define ABSL_INTERNAL_TRY try +#define ABSL_INTERNAL_CATCH_ANY catch (...) +#define ABSL_INTERNAL_RETHROW do { throw; } while (false) +#else // ABSL_HAVE_EXCEPTIONS +#define ABSL_INTERNAL_TRY if (true) +#define ABSL_INTERNAL_CATCH_ANY else if (false) +#define ABSL_INTERNAL_RETHROW do {} while (false) +#endif // ABSL_HAVE_EXCEPTIONS + #endif // ABSL_BASE_MACROS_H_ diff --git a/absl/base/raw_logging_test.cc b/absl/base/raw_logging_test.cc index dae4b35138c..b21cf651758 100644 --- a/absl/base/raw_logging_test.cc +++ b/absl/base/raw_logging_test.cc @@ -18,12 +18,20 @@ #include "absl/base/internal/raw_logging.h" +#include + #include "gtest/gtest.h" +#include "absl/strings/str_cat.h" namespace { TEST(RawLoggingCompilationTest, Log) { ABSL_RAW_LOG(INFO, "RAW INFO: %d", 1); + ABSL_RAW_LOG(INFO, "RAW INFO: %d %d", 1, 2); + ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d", 1, 2, 3); + ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d", 1, 2, 3, 4); + ABSL_RAW_LOG(INFO, "RAW INFO: %d %d %d %d %d", 1, 2, 3, 4, 5); + ABSL_RAW_LOG(WARNING, "RAW WARNING: %d", 1); ABSL_RAW_LOG(ERROR, "RAW ERROR: %d", 1); } @@ -32,7 +40,7 @@ TEST(RawLoggingCompilationTest, PassingCheck) { } // Not all platforms support output from raw log, so we don't verify any -// particular output for RAW check failures (expecting the empty std::string +// particular output for RAW check failures (expecting the empty string // accomplishes this). This test is primarily a compilation test, but we // are verifying process death when EXPECT_DEATH works for a platform. const char kExpectedDeathOutput[] = ""; @@ -47,4 +55,25 @@ TEST(RawLoggingDeathTest, LogFatal) { kExpectedDeathOutput); } +TEST(InternalLog, CompilationTest) { + ABSL_INTERNAL_LOG(INFO, "Internal Log"); + std::string log_msg = "Internal Log"; + ABSL_INTERNAL_LOG(INFO, log_msg); + + ABSL_INTERNAL_LOG(INFO, log_msg + " 2"); + + float d = 1.1f; + ABSL_INTERNAL_LOG(INFO, absl::StrCat("Internal log ", 3, " + ", d)); +} + +TEST(InternalLogDeathTest, FailingCheck) { + EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_CHECK(1 == 0, "explanation"), + kExpectedDeathOutput); +} + +TEST(InternalLogDeathTest, LogFatal) { + EXPECT_DEATH_IF_SUPPORTED(ABSL_INTERNAL_LOG(FATAL, "my dog has fleas"), + kExpectedDeathOutput); +} + } // namespace diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc index d04ee36694b..95382977d9a 100644 --- a/absl/base/spinlock_test_common.cc +++ b/absl/base/spinlock_test_common.cc @@ -36,7 +36,7 @@ constexpr int32_t kNumThreads = 10; constexpr int32_t kIters = 1000; namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { namespace base_internal { // This is defined outside of anonymous namespace so that it can be @@ -156,7 +156,8 @@ TEST(SpinLock, WaitCyclesEncoding) { // Test corner cases int64_t start_time = time_distribution(generator); - EXPECT_EQ(0, SpinLockTest::EncodeWaitCycles(start_time, start_time)); + EXPECT_EQ(kSpinLockSleeper, + SpinLockTest::EncodeWaitCycles(start_time, start_time)); EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(0)); EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(kLockwordReservedMask)); EXPECT_EQ(kMaxCycles & ~kProfileTimestampMask, @@ -264,5 +265,5 @@ TEST(SpinLockWithThreads, DoesNotDeadlock) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_06_20 +} // inline namespace lts_2018_12_18 } // namespace absl diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h index 8d30b9324b5..2241ace4a47 100644 --- a/absl/base/thread_annotations.h +++ b/absl/base/thread_annotations.h @@ -31,7 +31,6 @@ // 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_THREAD_ANNOTATIONS_H_ #define ABSL_BASE_THREAD_ANNOTATIONS_H_ @@ -109,13 +108,23 @@ // 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 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__)) diff --git a/absl/compiler_config_setting.bzl b/absl/compiler_config_setting.bzl new file mode 100644 index 00000000000..b77c4f563b9 --- /dev/null +++ b/absl/compiler_config_setting.bzl @@ -0,0 +1,39 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +"""Creates config_setting that allows selecting based on 'compiler' value.""" + +def create_llvm_config(name, visibility): + # The "do_not_use_tools_cpp_compiler_present" attribute exists to + # distinguish between older versions of Bazel that do not support + # "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do. + # In the future, the only way to select on the compiler will be through + # flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can + # be removed. + if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"): + native.config_setting( + name = name, + flag_values = { + "@bazel_tools//tools/cpp:compiler": "llvm", + }, + visibility = visibility, + ) + else: + native.config_setting( + name = name, + values = {"compiler": "llvm"}, + visibility = visibility, + ) diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 119d5c88de2..afc869f45aa 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -19,17 +19,38 @@ load( "ABSL_DEFAULT_COPTS", "ABSL_TEST_COPTS", "ABSL_EXCEPTIONS_FLAG", + "ABSL_EXCEPTIONS_FLAG_LINKOPTS", ) package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 +cc_library( + name = "compressed_tuple", + hdrs = ["internal/compressed_tuple.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/utility", + ], +) + +cc_test( + name = "compressed_tuple_test", + srcs = ["internal/compressed_tuple_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":compressed_tuple", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "fixed_array", hdrs = ["fixed_array.h"], copts = ABSL_DEFAULT_COPTS, deps = [ + ":compressed_tuple", "//absl/algorithm", "//absl/base:core_headers", "//absl/base:dynamic_annotations", @@ -42,9 +63,11 @@ cc_test( name = "fixed_array_test", srcs = ["fixed_array_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, deps = [ ":fixed_array", "//absl/base:exception_testing", + "//absl/hash:hash_testing", "//absl/memory", "@com_google_googletest//:gtest_main", ], @@ -57,11 +80,24 @@ cc_test( deps = [ ":fixed_array", "//absl/base:exception_testing", + "//absl/hash:hash_testing", "//absl/memory", "@com_google_googletest//:gtest_main", ], ) +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, + deps = [ + ":fixed_array", + "//absl/base:exception_safety_testing", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "fixed_array_benchmark", srcs = ["fixed_array_benchmark.cc"], @@ -89,12 +125,14 @@ cc_test( name = "inlined_vector_test", srcs = ["inlined_vector_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, deps = [ ":inlined_vector", ":test_instance_tracker", "//absl/base", "//absl/base:core_headers", "//absl/base:exception_testing", + "//absl/hash:hash_testing", "//absl/memory", "//absl/strings", "@com_google_googletest//:gtest_main", @@ -111,6 +149,7 @@ cc_test( "//absl/base", "//absl/base:core_headers", "//absl/base:exception_testing", + "//absl/hash:hash_testing", "//absl/memory", "//absl/strings", "@com_google_googletest//:gtest_main", @@ -150,3 +189,462 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +NOTEST_TAGS_NONMOBILE = [ + "no_test_darwin_x86_64", + "no_test_loonix", +] + +NOTEST_TAGS_MOBILE = [ + "no_test_android_arm", + "no_test_android_arm64", + "no_test_android_x86", + "no_test_ios_x86_64", +] + +NOTEST_TAGS = NOTEST_TAGS_MOBILE + NOTEST_TAGS_NONMOBILE + +cc_library( + name = "flat_hash_map", + hdrs = ["flat_hash_map.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":container_memory", + ":hash_function_defaults", + ":raw_hash_map", + "//absl/algorithm:container", + "//absl/memory", + ], +) + +cc_test( + name = "flat_hash_map_test", + srcs = ["flat_hash_map_test.cc"], + copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"], + tags = NOTEST_TAGS_NONMOBILE, + deps = [ + ":flat_hash_map", + ":hash_generator_testing", + ":unordered_map_constructor_test", + ":unordered_map_lookup_test", + ":unordered_map_modifiers_test", + "//absl/types:any", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "flat_hash_set", + hdrs = ["flat_hash_set.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":container_memory", + ":hash_function_defaults", + ":raw_hash_set", + "//absl/algorithm:container", + "//absl/base:core_headers", + "//absl/memory", + ], +) + +cc_test( + name = "flat_hash_set_test", + srcs = ["flat_hash_set_test.cc"], + copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"], + tags = NOTEST_TAGS_NONMOBILE, + deps = [ + ":flat_hash_set", + ":hash_generator_testing", + ":unordered_set_constructor_test", + ":unordered_set_lookup_test", + ":unordered_set_modifiers_test", + "//absl/memory", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "node_hash_map", + hdrs = ["node_hash_map.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":container_memory", + ":hash_function_defaults", + ":node_hash_policy", + ":raw_hash_map", + "//absl/algorithm:container", + "//absl/memory", + ], +) + +cc_test( + name = "node_hash_map_test", + srcs = ["node_hash_map_test.cc"], + copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"], + tags = NOTEST_TAGS_NONMOBILE, + deps = [ + ":hash_generator_testing", + ":node_hash_map", + ":tracked", + ":unordered_map_constructor_test", + ":unordered_map_lookup_test", + ":unordered_map_modifiers_test", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "node_hash_set", + hdrs = ["node_hash_set.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":hash_function_defaults", + ":node_hash_policy", + ":raw_hash_set", + "//absl/algorithm:container", + "//absl/memory", + ], +) + +cc_test( + name = "node_hash_set_test", + srcs = ["node_hash_set_test.cc"], + copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"], + tags = NOTEST_TAGS_NONMOBILE, + deps = [ + ":hash_generator_testing", + ":node_hash_set", + ":unordered_set_constructor_test", + ":unordered_set_lookup_test", + ":unordered_set_modifiers_test", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "container_memory", + hdrs = ["internal/container_memory.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/memory", + "//absl/utility", + ], +) + +cc_test( + name = "container_memory_test", + srcs = ["internal/container_memory_test.cc"], + copts = ABSL_TEST_COPTS, + tags = NOTEST_TAGS_NONMOBILE, + deps = [ + ":container_memory", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "hash_function_defaults", + hdrs = ["internal/hash_function_defaults.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/base:config", + "//absl/hash", + "//absl/strings", + ], +) + +cc_test( + name = "hash_function_defaults_test", + srcs = ["internal/hash_function_defaults_test.cc"], + copts = ABSL_TEST_COPTS, + tags = NOTEST_TAGS, + deps = [ + ":hash_function_defaults", + "//absl/hash", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "hash_generator_testing", + testonly = 1, + srcs = ["internal/hash_generator_testing.cc"], + hdrs = ["internal/hash_generator_testing.h"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_policy_testing", + "//absl/meta:type_traits", + "//absl/strings", + ], +) + +cc_library( + name = "hash_policy_testing", + testonly = 1, + hdrs = ["internal/hash_policy_testing.h"], + copts = ABSL_TEST_COPTS, + deps = [ + "//absl/hash", + "//absl/strings", + ], +) + +cc_test( + name = "hash_policy_testing_test", + srcs = ["internal/hash_policy_testing_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_policy_testing", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "hash_policy_traits", + hdrs = ["internal/hash_policy_traits.h"], + copts = ABSL_DEFAULT_COPTS, + deps = ["//absl/meta:type_traits"], +) + +cc_test( + name = "hash_policy_traits_test", + srcs = ["internal/hash_policy_traits_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_policy_traits", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "hashtable_debug", + hdrs = ["internal/hashtable_debug.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":hashtable_debug_hooks", + ], +) + +cc_library( + name = "hashtable_debug_hooks", + hdrs = ["internal/hashtable_debug_hooks.h"], + copts = ABSL_DEFAULT_COPTS, +) + +cc_library( + name = "node_hash_policy", + hdrs = ["internal/node_hash_policy.h"], + copts = ABSL_DEFAULT_COPTS, +) + +cc_test( + name = "node_hash_policy_test", + srcs = ["internal/node_hash_policy_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_policy_traits", + ":node_hash_policy", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "raw_hash_map", + hdrs = ["internal/raw_hash_map.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":container_memory", + ":raw_hash_set", + ], +) + +cc_library( + name = "raw_hash_set", + srcs = ["internal/raw_hash_set.cc"], + hdrs = ["internal/raw_hash_set.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + ":compressed_tuple", + ":container_memory", + ":hash_policy_traits", + ":hashtable_debug_hooks", + ":layout", + "//absl/base:bits", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:endian", + "//absl/memory", + "//absl/meta:type_traits", + "//absl/types:optional", + "//absl/utility", + ], +) + +cc_test( + name = "raw_hash_set_test", + srcs = ["internal/raw_hash_set_test.cc"], + copts = ABSL_TEST_COPTS, + linkstatic = 1, + tags = NOTEST_TAGS, + deps = [ + ":container_memory", + ":hash_function_defaults", + ":hash_policy_testing", + ":hashtable_debug", + ":raw_hash_set", + "//absl/base", + "//absl/base:core_headers", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "raw_hash_set_allocator_test", + size = "small", + srcs = ["internal/raw_hash_set_allocator_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":raw_hash_set", + ":tracked", + "//absl/base:core_headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "layout", + hdrs = ["internal/layout.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/types:span", + "//absl/utility", + ], +) + +cc_test( + name = "layout_test", + size = "small", + srcs = ["internal/layout_test.cc"], + copts = ABSL_TEST_COPTS, + tags = NOTEST_TAGS, + visibility = ["//visibility:private"], + deps = [ + ":layout", + "//absl/base", + "//absl/base:core_headers", + "//absl/types:span", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "tracked", + testonly = 1, + hdrs = ["internal/tracked.h"], + copts = ABSL_TEST_COPTS, +) + +cc_library( + name = "unordered_map_constructor_test", + testonly = 1, + hdrs = ["internal/unordered_map_constructor_test.h"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_generator_testing", + ":hash_policy_testing", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "unordered_map_lookup_test", + testonly = 1, + hdrs = ["internal/unordered_map_lookup_test.h"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_generator_testing", + ":hash_policy_testing", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "unordered_map_modifiers_test", + testonly = 1, + hdrs = ["internal/unordered_map_modifiers_test.h"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_generator_testing", + ":hash_policy_testing", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "unordered_set_constructor_test", + testonly = 1, + hdrs = ["internal/unordered_set_constructor_test.h"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_generator_testing", + ":hash_policy_testing", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "unordered_set_lookup_test", + testonly = 1, + hdrs = ["internal/unordered_set_lookup_test.h"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_generator_testing", + ":hash_policy_testing", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "unordered_set_modifiers_test", + testonly = 1, + hdrs = ["internal/unordered_set_modifiers_test.h"], + copts = ABSL_TEST_COPTS, + deps = [ + ":hash_generator_testing", + ":hash_policy_testing", + "@com_google_googletest//:gtest", + ], +) + +cc_test( + name = "unordered_set_test", + srcs = ["internal/unordered_set_test.cc"], + copts = ABSL_TEST_COPTS, + tags = NOTEST_TAGS_NONMOBILE, + deps = [ + ":unordered_set_constructor_test", + ":unordered_set_lookup_test", + ":unordered_set_modifiers_test", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "unordered_map_test", + srcs = ["internal/unordered_map_test.cc"], + copts = ABSL_TEST_COPTS, + tags = NOTEST_TAGS_NONMOBILE, + deps = [ + ":unordered_map_constructor_test", + ":unordered_map_lookup_test", + ":unordered_map_modifiers_test", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index f56ce92d81b..8605facc2e2 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -14,113 +14,674 @@ # limitations under the License. # +# This is deprecated and will be removed in the future. It also doesn't do +# anything anyways. Prefer to use the library associated with the API you are +# using. +absl_cc_library( + NAME + container + SRCS + "internal/raw_hash_set.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + PUBLIC +) -list(APPEND CONTAINER_PUBLIC_HEADERS - "fixed_array.h" - "inlined_vector.h" +absl_cc_library( + NAME + compressed_tuple + HDRS + "internal/compressed_tuple.h" + DEPS + absl::utility + PUBLIC ) +absl_cc_test( + NAME + compressed_tuple_test + SRCS + "internal/compressed_tuple_test.cc" + DEPS + absl::compressed_tuple + gmock_main +) -list(APPEND CONTAINER_INTERNAL_HEADERS - "internal/test_instance_tracker.h" +absl_cc_library( + NAME + fixed_array + HDRS + "fixed_array.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::compressed_tuple + absl::algorithm + absl::core_headers + absl::dynamic_annotations + absl::throw_delegate + absl::memory + PUBLIC ) +absl_cc_test( + NAME + fixed_array_test + SRCS + "fixed_array_test.cc" + COPTS + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::fixed_array + absl::exception_testing + absl::hash_testing + absl::memory + gmock_main +) -absl_header_library( - TARGET - absl_container - EXPORT_NAME - container +absl_cc_test( + NAME + fixed_array_test_noexceptions + SRCS + "fixed_array_test.cc" + DEPS + absl::fixed_array + absl::exception_testing + absl::hash_testing + absl::memory + gmock_main ) +absl_cc_test( + NAME + fixed_array_exception_safety_test + SRCS + "fixed_array_exception_safety_test.cc" + COPTS + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::fixed_array + absl::exception_safety_testing + gmock_main +) -# -## TESTS -# +absl_cc_library( + NAME + inlined_vector + HDRS + "inlined_vector.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::algorithm + absl::core_headers + absl::throw_delegate + absl::memory + PUBLIC +) + +absl_cc_test( + NAME + inlined_vector_test + SRCS + "inlined_vector_test.cc" + COPTS + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::inlined_vector + absl::test_instance_tracker + absl::base + absl::core_headers + absl::exception_testing + absl::hash_testing + absl::memory + absl::strings + gmock_main +) + +absl_cc_test( + NAME + inlined_vector_test_noexceptions + SRCS + "inlined_vector_test.cc" + DEPS + absl::inlined_vector + absl::test_instance_tracker + absl::base + absl::core_headers + absl::exception_testing + absl::hash_testing + absl::memory + absl::strings + gmock_main +) -list(APPEND TEST_INSTANCE_TRACKER_LIB_SRC - "internal/test_instance_tracker.cc" - ${CONTAINER_PUBLIC_HEADERS} - ${CONTAINER_INTERNAL_HEADERS} +absl_cc_library( + NAME + test_instance_tracker + HDRS + "internal/test_instance_tracker.h" + SRCS + "internal/test_instance_tracker.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + TESTONLY ) +absl_cc_test( + NAME + test_instance_tracker_test + SRCS + "internal/test_instance_tracker_test.cc" + DEPS + absl::test_instance_tracker + gmock_main +) -absl_library( - TARGET - test_instance_tracker_lib - SOURCES - ${TEST_INSTANCE_TRACKER_LIB_SRC} - PUBLIC_LIBRARIES - absl::container - DISABLE_INSTALL +absl_cc_library( + NAME + flat_hash_map + HDRS + "flat_hash_map.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::container_memory + absl::hash_function_defaults + absl::raw_hash_map + absl::algorithm_container + absl::memory + PUBLIC ) +absl_cc_test( + NAME + flat_hash_map_test + SRCS + "flat_hash_map_test.cc" + COPTS + "-DUNORDERED_MAP_CXX17" + DEPS + absl::flat_hash_map + absl::hash_generator_testing + absl::unordered_map_constructor_test + absl::unordered_map_lookup_test + absl::unordered_map_modifiers_test + absl::any + gmock_main +) +absl_cc_library( + NAME + flat_hash_set + HDRS + "flat_hash_set.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::container_memory + absl::hash_function_defaults + absl::raw_hash_set + absl::algorithm_container + absl::core_headers + absl::memory + PUBLIC +) -# test fixed_array_test -set(FIXED_ARRAY_TEST_SRC "fixed_array_test.cc") -set(FIXED_ARRAY_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib) +absl_cc_test( + NAME + flat_hash_set_test + SRCS + "flat_hash_set_test.cc" + 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_modifiers_test + absl::memory + absl::strings + gmock_main +) -absl_test( - TARGET - fixed_array_test - SOURCES - ${FIXED_ARRAY_TEST_SRC} - PUBLIC_LIBRARIES - ${FIXED_ARRAY_TEST_PUBLIC_LIBRARIES} - PRIVATE_COMPILE_FLAGS - ${ABSL_EXCEPTIONS_FLAG} +absl_cc_library( + NAME + node_hash_map + HDRS + "node_hash_map.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::container_memory + absl::hash_function_defaults + absl::node_hash_policy + absl::raw_hash_map + absl::algorithm_container + absl::memory + PUBLIC ) +absl_cc_test( + NAME + node_hash_map_test + SRCS + "node_hash_map_test.cc" + COPTS + "-DUNORDERED_MAP_CXX17" + DEPS + absl::hash_generator_testing + absl::node_hash_map + absl::tracked + absl::unordered_map_constructor_test + absl::unordered_map_lookup_test + absl::unordered_map_modifiers_test + gmock_main +) +absl_cc_library( + NAME + node_hash_set + HDRS + "node_hash_set.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::hash_function_defaults + absl::node_hash_policy + absl::raw_hash_set + absl::algorithm_container + absl::memory + PUBLIC +) -absl_test( - TARGET - fixed_array_test_noexceptions - SOURCES - ${FIXED_ARRAY_TEST_SRC} - PUBLIC_LIBRARIES - ${FIXED_ARRAY_TEST_PUBLIC_LIBRARIES} +absl_cc_test( + NAME + node_hash_set_test + SRCS + "node_hash_set_test.cc" + 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_modifiers_test + gmock_main ) +absl_cc_library( + NAME + container_memory + HDRS + "internal/container_memory.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::memory + absl::utility + PUBLIC +) -# test inlined_vector_test -set(INLINED_VECTOR_TEST_SRC "inlined_vector_test.cc") -set(INLINED_VECTOR_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib) +absl_cc_test( + NAME + container_memory_test + SRCS + "internal/container_memory_test.cc" + DEPS + absl::container_memory + absl::strings + gmock_main +) -absl_test( - TARGET - inlined_vector_test - SOURCES - ${INLINED_VECTOR_TEST_SRC} - PUBLIC_LIBRARIES - ${INLINED_VECTOR_TEST_PUBLIC_LIBRARIES} +absl_cc_library( + NAME + hash_function_defaults + HDRS + "internal/hash_function_defaults.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::hash + absl::strings + PUBLIC ) -absl_test( - TARGET - inlined_vector_test_noexceptions - SOURCES - ${INLINED_VECTOR_TEST_SRC} - PUBLIC_LIBRARIES - ${INLINED_VECTOR_TEST_PUBLIC_LIBRARIES} - PRIVATE_COMPILE_FLAGS - ${ABSL_NOEXCEPTION_CXXFLAGS} +absl_cc_test( + NAME + hash_function_defaults_test + SRCS + "internal/hash_function_defaults_test.cc" + DEPS + absl::hash_function_defaults + absl::hash + absl::strings + gmock_main ) +absl_cc_library( + NAME + hash_generator_testing + HDRS + "internal/hash_generator_testing.h" + SRCS + "internal/hash_generator_testing.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hash_policy_testing + absl::meta + absl::strings + TESTONLY +) -# test test_instance_tracker_test -set(TEST_INSTANCE_TRACKER_TEST_SRC "internal/test_instance_tracker_test.cc") -set(TEST_INSTANCE_TRACKER_TEST_PUBLIC_LIBRARIES absl::base absl_throw_delegate test_instance_tracker_lib) +absl_cc_library( + NAME + hash_policy_testing + HDRS + "internal/hash_policy_testing.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hash + absl::strings + TESTONLY +) +absl_cc_test( + NAME + hash_policy_testing_test + SRCS + "internal/hash_policy_testing_test.cc" + DEPS + absl::hash_policy_testing + gmock_main +) -absl_test( - TARGET - test_instance_tracker_test - SOURCES - ${TEST_INSTANCE_TRACKER_TEST_SRC} - PUBLIC_LIBRARIES - ${TEST_INSTANCE_TRACKER_TEST_PUBLIC_LIBRARIES} +absl_cc_library( + NAME + hash_policy_traits + HDRS + "internal/hash_policy_traits.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::meta + PUBLIC ) +absl_cc_test( + NAME + hash_policy_traits_test + SRCS + "internal/hash_policy_traits_test.cc" + DEPS + absl::hash_policy_traits + gmock_main +) + +absl_cc_library( + NAME + hashtable_debug + HDRS + "internal/hashtable_debug.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::hashtable_debug_hooks +) + +absl_cc_library( + NAME + hashtable_debug_hooks + HDRS + "internal/hashtable_debug_hooks.h" + COPTS + ${ABSL_DEFAULT_COPTS} + PUBLIC +) + +absl_cc_library( + NAME + node_hash_policy + HDRS + "internal/node_hash_policy.h" + COPTS + ${ABSL_DEFAULT_COPTS} + PUBLIC +) + +absl_cc_test( + NAME + node_hash_policy_test + SRCS + "internal/node_hash_policy_test.cc" + DEPS + absl::hash_policy_traits + absl::node_hash_policy + gmock_main +) + +absl_cc_library( + NAME + raw_hash_map + HDRS + "internal/raw_hash_map.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::container_memory + absl::raw_hash_set + PUBLIC +) + +absl_cc_library( + NAME + raw_hash_set + HDRS + "internal/raw_hash_set.h" + SRCS + "internal/raw_hash_set.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::compressed_tuple + absl::container_memory + absl::hash_policy_traits + absl::hashtable_debug_hooks + absl::layout + absl::bits + absl::config + absl::core_headers + absl::endian + absl::memory + absl::meta + absl::optional + absl::utility + PUBLIC +) + +absl_cc_test( + NAME + raw_hash_set_test + SRCS + "internal/raw_hash_set_test.cc" + DEPS + absl::container_memory + absl::hash_function_defaults + absl::hash_policy_testing + absl::hashtable_debug + absl::raw_hash_set + absl::base + absl::core_headers + absl::strings + gmock_main +) + +absl_cc_test( + NAME + raw_hash_set_allocator_test + SRCS + "internal/raw_hash_set_allocator_test.cc" + DEPS + absl::raw_hash_set + absl::tracked + absl::core_headers + gmock_main +) + +absl_cc_library( + NAME + layout + HDRS + "internal/layout.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + absl::meta + absl::strings + absl::span + absl::utility + PUBLIC +) +absl_cc_test( + NAME + layout_test + SRCS + "internal/layout_test.cc" + DEPS + absl::layout + absl::base + absl::core_headers + absl::span + gmock_main +) + +absl_cc_library( + NAME + tracked + HDRS + "internal/tracked.h" + COPTS + ${ABSL_TEST_COPTS} + TESTONLY +) + +absl_cc_library( + NAME + unordered_map_constructor_test + HDRS + "internal/unordered_map_constructor_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hash_generator_testing + absl::hash_policy_testing + gmock + TESTONLY +) + +absl_cc_library( + NAME + unordered_map_lookup_test + HDRS + "internal/unordered_map_lookup_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hash_generator_testing + absl::hash_policy_testing + gmock + TESTONLY +) + +absl_cc_library( + NAME + unordered_map_modifiers_test + HDRS + "internal/unordered_map_modifiers_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hash_generator_testing + absl::hash_policy_testing + gmock + TESTONLY +) + +absl_cc_library( + NAME + unordered_set_constructor_test + HDRS + "internal/unordered_set_constructor_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hash_generator_testing + absl::hash_policy_testing + gmock + TESTONLY +) + +absl_cc_library( + NAME + unordered_set_lookup_test + HDRS + "internal/unordered_set_lookup_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hash_generator_testing + absl::hash_policy_testing + gmock + TESTONLY +) + +absl_cc_library( + NAME + unordered_set_modifiers_test + HDRS + "internal/unordered_set_modifiers_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hash_generator_testing + absl::hash_policy_testing + gmock + TESTONLY +) + +absl_cc_test( + NAME + unordered_set_test + SRCS + "internal/unordered_set_test.cc" + DEPS + absl::unordered_set_constructor_test + absl::unordered_set_lookup_test + absl::unordered_set_modifiers_test + gmock_main +) + +absl_cc_test( + NAME + unordered_map_test + SRCS + "internal/unordered_map_test.cc" + DEPS + absl::unordered_map_constructor_test + absl::unordered_map_lookup_test + absl::unordered_map_modifiers_test + gmock_main +) diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index daa4eb229da..7f6a3afd81e 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Abseil Authors. +// 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. @@ -47,10 +47,11 @@ #include "absl/base/macros.h" #include "absl/base/optimization.h" #include "absl/base/port.h" +#include "absl/container/internal/compressed_tuple.h" #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { constexpr static auto kFixedArrayUseDefault = static_cast(-1); @@ -58,13 +59,13 @@ constexpr static auto kFixedArrayUseDefault = static_cast(-1); // FixedArray // ----------------------------------------------------------------------------- // -// A `FixedArray` provides a run-time fixed-size array, allocating small arrays -// inline for efficiency and correctness. +// A `FixedArray` provides a run-time fixed-size array, allocating a small array +// inline for efficiency. // // Most users should not specify an `inline_elements` argument and let -// `FixedArray<>` automatically determine the number of elements +// `FixedArray` automatically determine the number of elements // to store inline based on `sizeof(T)`. If `inline_elements` is specified, the -// `FixedArray<>` implementation will inline arrays of +// `FixedArray` implementation will use inline storage for arrays with a // length <= `inline_elements`. // // Note that a `FixedArray` constructed with a `size_type` argument will @@ -77,65 +78,100 @@ constexpr static auto kFixedArrayUseDefault = static_cast(-1); // heap allocation, it will do so with global `::operator new[]()` and // `::operator delete[]()`, even if T provides class-scope overrides for these // operators. -template +template > class FixedArray { + static_assert(!std::is_array::value || std::extent::value > 0, + "Arrays with unknown bounds cannot be used with FixedArray."); + static constexpr size_t kInlineBytesDefault = 256; + using AllocatorTraits = std::allocator_traits; // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17, // but this seems to be mostly pedantic. - template - using EnableIfForwardIterator = typename std::enable_if< - std::is_convertible< - typename std::iterator_traits::iterator_category, - std::forward_iterator_tag>::value, - int>::type; + template + using EnableIfForwardIterator = absl::enable_if_t::iterator_category, + std::forward_iterator_tag>::value>; + static constexpr bool NoexceptCopyable() { + return std::is_nothrow_copy_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool NoexceptMovable() { + return std::is_nothrow_move_constructible::value && + absl::allocator_is_nothrow::value; + } + static constexpr bool DefaultConstructorIsNonTrivial() { + return !absl::is_trivially_default_constructible::value; + } public: - // For playing nicely with stl: - using value_type = T; - using iterator = T*; - using const_iterator = const T*; + using allocator_type = typename AllocatorTraits::allocator_type; + using value_type = typename allocator_type::value_type; + using pointer = typename allocator_type::pointer; + using const_pointer = typename allocator_type::const_pointer; + using reference = typename allocator_type::reference; + using const_reference = typename allocator_type::const_reference; + using size_type = typename allocator_type::size_type; + using difference_type = typename allocator_type::difference_type; + using iterator = pointer; + using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - using reference = T&; - using const_reference = const T&; - using pointer = T*; - using const_pointer = const T*; - using difference_type = ptrdiff_t; - using size_type = size_t; static constexpr size_type inline_elements = - inlined == kFixedArrayUseDefault - ? kInlineBytesDefault / sizeof(value_type) - : inlined; - - FixedArray(const FixedArray& other) : rep_(other.begin(), other.end()) {} - FixedArray(FixedArray&& other) noexcept( - // clang-format off - absl::allocator_is_nothrow>::value && - // clang-format on - std::is_nothrow_move_constructible::value) - : rep_(std::make_move_iterator(other.begin()), - std::make_move_iterator(other.end())) {} + (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type) + : static_cast(N)); + + FixedArray( + const FixedArray& other, + const allocator_type& a = allocator_type()) noexcept(NoexceptCopyable()) + : FixedArray(other.begin(), other.end(), a) {} + + FixedArray( + FixedArray&& other, + const allocator_type& a = allocator_type()) noexcept(NoexceptMovable()) + : FixedArray(std::make_move_iterator(other.begin()), + std::make_move_iterator(other.end()), a) {} // Creates an array object that can store `n` elements. // Note that trivially constructible elements will be uninitialized. - explicit FixedArray(size_type n) : rep_(n) {} + explicit FixedArray(size_type n, const allocator_type& a = allocator_type()) + : storage_(n, a) { + if (DefaultConstructorIsNonTrivial()) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end()); + } + } // Creates an array initialized with `n` copies of `val`. - FixedArray(size_type n, const value_type& val) : rep_(n, val) {} + FixedArray(size_type n, const value_type& val, + const allocator_type& a = allocator_type()) + : storage_(n, a) { + memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), + storage_.end(), val); + } + + // Creates an array initialized with the size and contents of `init_list`. + FixedArray(std::initializer_list init_list, + const allocator_type& a = allocator_type()) + : FixedArray(init_list.begin(), init_list.end(), a) {} // Creates an array initialized with the elements from the input // range. The array's size will always be `std::distance(first, last)`. - // REQUIRES: Iter must be a forward_iterator or better. - template = 0> - FixedArray(Iter first, Iter last) : rep_(first, last) {} - - // Creates the array from an initializer_list. - FixedArray(std::initializer_list init_list) - : FixedArray(init_list.begin(), init_list.end()) {} + // REQUIRES: Iterator must be a forward_iterator or better. + template * = nullptr> + FixedArray(Iterator first, Iterator last, + const allocator_type& a = allocator_type()) + : storage_(std::distance(first, last), a) { + memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last); + } - ~FixedArray() {} + ~FixedArray() noexcept { + for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) { + AllocatorTraits::destroy(storage_.alloc(), cur); + } + } // Assignments are deleted because they break the invariant that the size of a // `FixedArray` never changes. @@ -145,7 +181,7 @@ class FixedArray { // FixedArray::size() // // Returns the length of the fixed array. - size_type size() const { return rep_.size(); } + size_type size() const { return storage_.size(); } // FixedArray::max_size() // @@ -153,7 +189,7 @@ class FixedArray { // `FixedArray`. This is equivalent to the most possible addressable bytes // over the number of bytes taken by T. constexpr size_type max_size() const { - return std::numeric_limits::max() / sizeof(value_type); + return (std::numeric_limits::max)() / sizeof(value_type); } // FixedArray::empty() @@ -170,12 +206,12 @@ class FixedArray { // // Returns a const T* pointer to elements of the `FixedArray`. This pointer // can be used to access (but not modify) the contained elements. - const_pointer data() const { return AsValue(rep_.begin()); } + const_pointer data() const { return AsValueType(storage_.begin()); } // Overload of FixedArray::data() to return a T* pointer to elements of the // fixed array. This pointer can be used to access and modify the contained // elements. - pointer data() { return AsValue(rep_.begin()); } + pointer data() { return AsValueType(storage_.begin()); } // FixedArray::operator[] // @@ -295,7 +331,7 @@ class FixedArray { // FixedArray::fill() // // Assigns the given `value` to all elements in the fixed array. - void fill(const T& value) { std::fill(begin(), end(), value); } + void fill(const value_type& val) { std::fill(begin(), end(), val); } // Relational operators. Equality operators are elementwise using // `operator==`, while order operators order FixedArrays lexicographically. @@ -324,18 +360,25 @@ class FixedArray { return !(lhs < rhs); } + template + friend H AbslHashValue(H h, const FixedArray& v) { + return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()), + v.size()); + } + private: - // HolderTraits + // StorageElement // - // Wrapper to hold elements of type T for the case where T is an array type. - // If 'T' is an array type, HolderTraits::type is a struct with a 'T v;'. - // Otherwise, HolderTraits::type is simply 'T'. + // For FixedArrays with a C-style-array value_type, StorageElement is a POD + // wrapper struct called StorageElementWrapper that holds the value_type + // instance inside. This is needed for construction and destruction of the + // entire array regardless of how many dimensions it has. For all other cases, + // StorageElement is just an alias of value_type. // - // Maintainer's Note: The simpler solution would be to simply wrap T in a - // struct whether it's an array or not: 'struct Holder { T v; };', but - // that causes some paranoid diagnostics to misfire about uses of data(), - // believing that 'data()' (aka '&rep_.begin().v') is a pointer to a single - // element, rather than the packed array that it really is. + // Maintainer's Note: The simpler solution would be to simply wrap value_type + // in a struct whether it's an array or not. That causes some paranoid + // diagnostics to misfire, believing that 'data()' returns a pointer to a + // single element, rather than the packed array that it really is. // e.g.: // // FixedArray buf(1); @@ -344,157 +387,134 @@ class FixedArray { // error: call to int __builtin___sprintf_chk(etc...) // will always overflow destination buffer [-Werror] // - class HolderTraits { - template - struct SelectImpl { - using type = U; - static pointer AsValue(type* p) { return p; } - }; - - // Partial specialization for elements of array type. - template - struct SelectImpl { - struct Holder { U v[N]; }; - using type = Holder; - static pointer AsValue(type* p) { return &p->v; } - }; - using Impl = SelectImpl; - - public: - using type = typename Impl::type; - - static pointer AsValue(type *p) { return Impl::AsValue(p); } - - // TODO(billydonahue): fix the type aliasing violation - // this assertion hints at. - static_assert(sizeof(type) == sizeof(value_type), - "Holder must be same size as value_type"); + template , + size_t InnerN = std::extent::value> + struct StorageElementWrapper { + InnerT array[InnerN]; }; - using Holder = typename HolderTraits::type; - static pointer AsValue(Holder *p) { return HolderTraits::AsValue(p); } + using StorageElement = + absl::conditional_t::value, + StorageElementWrapper, value_type>; + using StorageElementBuffer = + absl::aligned_storage_t; - // InlineSpace - // - // Allocate some space, not an array of elements of type T, so that we can - // skip calling the T constructors and destructors for space we never use. - // How many elements should we store inline? - // a. If not specified, use a default of kInlineBytesDefault bytes (This is - // currently 256 bytes, which seems small enough to not cause stack overflow - // or unnecessary stack pollution, while still allowing stack allocation for - // reasonably long character arrays). - // b. Never use 0 length arrays (not ISO C++) - // - template - class InlineSpace { - public: - Holder* data() { return reinterpret_cast(space_.data()); } - void AnnotateConstruct(size_t n) const { Annotate(n, true); } - void AnnotateDestruct(size_t n) const { Annotate(n, false); } + static pointer AsValueType(pointer ptr) { return ptr; } + static pointer AsValueType(StorageElementWrapper* ptr) { + return std::addressof(ptr->array); + } - private: -#ifndef ADDRESS_SANITIZER - void Annotate(size_t, bool) const { } -#else - void Annotate(size_t n, bool creating) const { - if (!n) return; - const void* bot = &left_redzone_; - const void* beg = space_.data(); - const void* end = space_.data() + n; - const void* top = &right_redzone_ + 1; - // args: (beg, end, old_mid, new_mid) - if (creating) { - ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, top, end); - ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, beg, bot); - } else { - ANNOTATE_CONTIGUOUS_CONTAINER(beg, top, end, top); - ANNOTATE_CONTIGUOUS_CONTAINER(bot, beg, bot, beg); - } + static_assert(sizeof(StorageElement) == sizeof(value_type), ""); + static_assert(alignof(StorageElement) == alignof(value_type), ""); + + struct NonEmptyInlinedStorage { + StorageElement* data() { + return reinterpret_cast(inlined_storage_.data()); } + +#ifdef ADDRESS_SANITIZER + void* RedzoneBegin() { return &redzone_begin_; } + void* RedzoneEnd() { return &redzone_end_ + 1; } #endif // ADDRESS_SANITIZER - using Buffer = - typename std::aligned_storage::type; + void AnnotateConstruct(size_type); + void AnnotateDestruct(size_type); - ADDRESS_SANITIZER_REDZONE(left_redzone_); - std::array space_; - ADDRESS_SANITIZER_REDZONE(right_redzone_); + ADDRESS_SANITIZER_REDZONE(redzone_begin_); + std::array inlined_storage_; + ADDRESS_SANITIZER_REDZONE(redzone_end_); }; - // specialization when N = 0. - template - class InlineSpace<0, U> { - public: - Holder* data() { return nullptr; } - void AnnotateConstruct(size_t) const {} - void AnnotateDestruct(size_t) const {} + struct EmptyInlinedStorage { + StorageElement* data() { return nullptr; } + void AnnotateConstruct(size_type) {} + void AnnotateDestruct(size_type) {} }; - // Rep + using InlinedStorage = + absl::conditional_t; + + // Storage // - // A const Rep object holds FixedArray's size and data pointer. + // An instance of Storage manages the inline and out-of-line memory for + // instances of FixedArray. This guarantees that even when construction of + // individual elements fails in the FixedArray constructor body, the + // destructor for Storage will still be called and out-of-line memory will be + // properly deallocated. // - class Rep : public InlineSpace { + class Storage : public InlinedStorage { public: - Rep(size_type n, const value_type& val) : n_(n), p_(MakeHolder(n)) { - std::uninitialized_fill_n(p_, n, val); - } + Storage(size_type n, const allocator_type& a) + : size_alloc_(n, a), data_(InitializeData()) {} - explicit Rep(size_type n) : n_(n), p_(MakeHolder(n)) { - // Loop optimizes to nothing for trivially constructible T. - for (Holder* p = p_; p != p_ + n; ++p) - // Note: no parens: default init only. - // Also note '::' to avoid Holder class placement new operator. - ::new (static_cast(p)) Holder; + ~Storage() noexcept { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateDestruct(size()); + } else { + AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size()); + } } - template - Rep(Iter first, Iter last) - : n_(std::distance(first, last)), p_(MakeHolder(n_)) { - std::uninitialized_copy(first, last, AsValue(p_)); + size_type size() const { return size_alloc_.template get<0>(); } + StorageElement* begin() const { return data_; } + StorageElement* end() const { return begin() + size(); } + allocator_type& alloc() { + return size_alloc_.template get<1>(); } - ~Rep() { - // Destruction must be in reverse order. - // Loop optimizes to nothing for trivially destructible T. - for (Holder* p = end(); p != begin();) (--p)->~Holder(); - if (IsAllocated(size())) { - std::allocator().deallocate(p_, n_); - } else { - this->AnnotateDestruct(size()); - } + private: + static bool UsingInlinedStorage(size_type n) { + return n <= inline_elements; } - Holder* begin() const { return p_; } - Holder* end() const { return p_ + n_; } - size_type size() const { return n_; } - private: - Holder* MakeHolder(size_type n) { - if (IsAllocated(n)) { - return std::allocator().allocate(n); + StorageElement* InitializeData() { + if (UsingInlinedStorage(size())) { + InlinedStorage::AnnotateConstruct(size()); + return InlinedStorage::data(); } else { - this->AnnotateConstruct(n); - return this->data(); + return reinterpret_cast( + AllocatorTraits::allocate(alloc(), size())); } } - bool IsAllocated(size_type n) const { return n > inline_elements; } - - const size_type n_; - Holder* const p_; + // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s + container_internal::CompressedTuple size_alloc_; + StorageElement* data_; }; - - // Data members - Rep rep_; + Storage storage_; }; -template -constexpr size_t FixedArray::inline_elements; - -template -constexpr size_t FixedArray::kInlineBytesDefault; - -} // inline namespace lts_2018_06_20 +template +constexpr size_t FixedArray::kInlineBytesDefault; + +template +constexpr typename FixedArray::size_type + FixedArray::inline_elements; + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateConstruct( + typename FixedArray::size_type n) { +#ifdef ADDRESS_SANITIZER + if (!n) return; + ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n); + ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin()); +#endif // ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} + +template +void FixedArray::NonEmptyInlinedStorage::AnnotateDestruct( + typename FixedArray::size_type n) { +#ifdef ADDRESS_SANITIZER + if (!n) return; + ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd()); + ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data()); +#endif // ADDRESS_SANITIZER + static_cast(n); // Mark used when not in asan mode +} +} // inline namespace lts_2018_12_18 } // namespace absl #endif // ABSL_CONTAINER_FIXED_ARRAY_H_ diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc new file mode 100644 index 00000000000..4d0430b3d9f --- /dev/null +++ b/absl/container/fixed_array_exception_safety_test.cc @@ -0,0 +1,119 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/container/fixed_array.h" + +#include "gtest/gtest.h" +#include "absl/base/internal/exception_safety_testing.h" + +namespace absl { +inline namespace lts_2018_12_18 { + +namespace { + +constexpr size_t kInlined = 25; +constexpr size_t kSmallSize = kInlined / 2; +constexpr size_t kLargeSize = kInlined * 2; + +constexpr int kInitialValue = 5; +constexpr int kUpdatedValue = 10; + +using ::testing::TestThrowingCtor; + +using Thrower = testing::ThrowingValue; +using FixedArr = absl::FixedArray; + +using MoveThrower = testing::ThrowingValue; +using MoveFixedArr = absl::FixedArray; + +TEST(FixedArrayExceptionSafety, CopyConstructor) { + auto small = FixedArr(kSmallSize); + TestThrowingCtor(small); + + auto large = FixedArr(kLargeSize); + TestThrowingCtor(large); +} + +TEST(FixedArrayExceptionSafety, MoveConstructor) { + TestThrowingCtor(FixedArr(kSmallSize)); + TestThrowingCtor(FixedArr(kLargeSize)); + + // TypeSpec::kNoThrowMove + TestThrowingCtor(MoveFixedArr(kSmallSize)); + TestThrowingCtor(MoveFixedArr(kLargeSize)); +} + +TEST(FixedArrayExceptionSafety, SizeConstructor) { + TestThrowingCtor(kSmallSize); + TestThrowingCtor(kLargeSize); +} + +TEST(FixedArrayExceptionSafety, SizeValueConstructor) { + TestThrowingCtor(kSmallSize, Thrower()); + TestThrowingCtor(kLargeSize, Thrower()); +} + +TEST(FixedArrayExceptionSafety, IteratorConstructor) { + auto small = FixedArr(kSmallSize); + TestThrowingCtor(small.begin(), small.end()); + + auto large = FixedArr(kLargeSize); + TestThrowingCtor(large.begin(), large.end()); +} + +TEST(FixedArrayExceptionSafety, InitListConstructor) { + constexpr int small_inlined = 3; + using SmallFixedArr = absl::FixedArray; + + TestThrowingCtor(std::initializer_list{}); + // Test inlined allocation + TestThrowingCtor( + std::initializer_list{Thrower{}, Thrower{}}); + // Test out of line allocation + TestThrowingCtor(std::initializer_list{ + Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}}); +} + +testing::AssertionResult ReadMemory(FixedArr* fixed_arr) { + // Marked volatile to prevent optimization. Used for running asan tests. + volatile int sum = 0; + for (const auto& thrower : *fixed_arr) { + sum += thrower.Get(); + } + return testing::AssertionSuccess() << "Values sum to [" << sum << "]"; +} + +TEST(FixedArrayExceptionSafety, Fill) { + auto test_fill = testing::MakeExceptionSafetyTester() + .WithContracts(ReadMemory) + .WithOperation([&](FixedArr* fixed_arr_ptr) { + auto thrower = + Thrower(kUpdatedValue, testing::nothrow_ctor); + fixed_arr_ptr->fill(thrower); + }); + + EXPECT_TRUE( + test_fill.WithInitialValue(FixedArr(kSmallSize, Thrower(kInitialValue))) + .Test()); + EXPECT_TRUE( + test_fill.WithInitialValue(FixedArr(kLargeSize, Thrower(kInitialValue))) + .Test()); +} + +} // namespace + +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc index 2142132d135..205ff41fe11 100644 --- a/absl/container/fixed_array_test.cc +++ b/absl/container/fixed_array_test.cc @@ -15,9 +15,11 @@ #include "absl/container/fixed_array.h" #include +#include #include #include #include +#include #include #include #include @@ -25,6 +27,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/exception_testing.h" +#include "absl/hash/hash_testing.h" #include "absl/memory/memory.h" using ::testing::ElementsAreArray; @@ -607,6 +610,216 @@ TEST(FixedArrayTest, Fill) { empty.fill(fill_val); } +// TODO(johnsoncj): Investigate InlinedStorage default initialization in GCC 4.x +#ifndef __GNUC__ +TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) { + using T = char; + constexpr auto capacity = 10; + using FixedArrType = absl::FixedArray; + using FixedArrBuffType = + absl::aligned_storage_t; + constexpr auto scrubbed_bits = 0x95; + constexpr auto length = capacity / 2; + + FixedArrBuffType buff; + std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrBuffType)); + + FixedArrType* arr = + ::new (static_cast(std::addressof(buff))) FixedArrType(length); + EXPECT_THAT(*arr, testing::Each(scrubbed_bits)); + arr->~FixedArrType(); +} +#endif // __GNUC__ + +// This is a stateful allocator, but the state lives outside of the +// allocator (in whatever test is using the allocator). This is odd +// but helps in tests where the allocator is propagated into nested +// containers - that chain of allocators uses the same state and is +// thus easier to query for aggregate allocation information. +template +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), instance_count_(nullptr) {} + explicit CountingAllocator(int64_t* b) + : bytes_used_(b), instance_count_(nullptr) {} + CountingAllocator(int64_t* b, int64_t* a) + : bytes_used_(b), instance_count_(a) {} + + template + explicit CountingAllocator(const CountingAllocator& x) + : Alloc(x), + bytes_used_(x.bytes_used_), + instance_count_(x.instance_count_) {} + + pointer allocate(size_type n, const void* const hint = nullptr) { + assert(bytes_used_ != nullptr); + *bytes_used_ += n * sizeof(T); + return Alloc::allocate(n, hint); + } + + void deallocate(pointer p, size_type n) { + Alloc::deallocate(p, n); + assert(bytes_used_ != nullptr); + *bytes_used_ -= n * sizeof(T); + } + + template + void construct(pointer p, Args&&... args) { + Alloc::construct(p, absl::forward(args)...); + if (instance_count_) { + *instance_count_ += 1; + } + } + + void destroy(pointer p) { + Alloc::destroy(p); + if (instance_count_) { + *instance_count_ -= 1; + } + } + + template + class rebind { + public: + using other = CountingAllocator; + }; + + int64_t* bytes_used_; + int64_t* instance_count_; +}; + +TEST(AllocatorSupportTest, CountInlineAllocations) { + constexpr size_t inlined_size = 4; + using Alloc = CountingAllocator; + using AllocFxdArr = absl::FixedArray; + + int64_t allocated = 0; + int64_t active_instances = 0; + + { + const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7}; + + Alloc alloc(&allocated, &active_instances); + + AllocFxdArr arr(ia, ia + inlined_size, alloc); + static_cast(arr); + } + + EXPECT_EQ(allocated, 0); + EXPECT_EQ(active_instances, 0); +} + +TEST(AllocatorSupportTest, CountOutoflineAllocations) { + constexpr size_t inlined_size = 4; + using Alloc = CountingAllocator; + using AllocFxdArr = absl::FixedArray; + + int64_t allocated = 0; + int64_t active_instances = 0; + + { + const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7}; + Alloc alloc(&allocated, &active_instances); + + AllocFxdArr arr(ia, ia + ABSL_ARRAYSIZE(ia), alloc); + + EXPECT_EQ(allocated, arr.size() * sizeof(int)); + static_cast(arr); + } + + EXPECT_EQ(active_instances, 0); +} + +TEST(AllocatorSupportTest, CountCopyInlineAllocations) { + constexpr size_t inlined_size = 4; + using Alloc = CountingAllocator; + using AllocFxdArr = absl::FixedArray; + + int64_t allocated1 = 0; + int64_t allocated2 = 0; + int64_t active_instances = 0; + Alloc alloc(&allocated1, &active_instances); + Alloc alloc2(&allocated2, &active_instances); + + { + int initial_value = 1; + + AllocFxdArr arr1(inlined_size / 2, initial_value, alloc); + + EXPECT_EQ(allocated1, 0); + + AllocFxdArr arr2(arr1, alloc2); + + EXPECT_EQ(allocated2, 0); + static_cast(arr1); + static_cast(arr2); + } + + EXPECT_EQ(active_instances, 0); +} + +TEST(AllocatorSupportTest, CountCopyOutoflineAllocations) { + constexpr size_t inlined_size = 4; + using Alloc = CountingAllocator; + using AllocFxdArr = absl::FixedArray; + + int64_t allocated1 = 0; + int64_t allocated2 = 0; + int64_t active_instances = 0; + Alloc alloc(&allocated1, &active_instances); + Alloc alloc2(&allocated2, &active_instances); + + { + int initial_value = 1; + + AllocFxdArr arr1(inlined_size * 2, initial_value, alloc); + + EXPECT_EQ(allocated1, arr1.size() * sizeof(int)); + + AllocFxdArr arr2(arr1, alloc2); + + EXPECT_EQ(allocated2, inlined_size * 2 * sizeof(int)); + static_cast(arr1); + static_cast(arr2); + } + + EXPECT_EQ(active_instances, 0); +} + +TEST(AllocatorSupportTest, SizeValAllocConstructor) { + using testing::AllOf; + using testing::Each; + using testing::SizeIs; + + constexpr size_t inlined_size = 4; + using Alloc = CountingAllocator; + using AllocFxdArr = absl::FixedArray; + + { + auto len = inlined_size / 2; + auto val = 0; + int64_t allocated = 0; + AllocFxdArr arr(len, val, Alloc(&allocated)); + + EXPECT_EQ(allocated, 0); + EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0))); + } + + { + auto len = inlined_size * 2; + auto val = 0; + int64_t allocated = 0; + AllocFxdArr arr(len, val, Alloc(&allocated)); + + EXPECT_EQ(allocated, len * sizeof(int)); + EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0))); + } +} + #ifdef ADDRESS_SANITIZER TEST(FixedArrayTest, AddressSanitizerAnnotations1) { absl::FixedArray a(10); diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h new file mode 100644 index 00000000000..ed453348b97 --- /dev/null +++ b/absl/container/flat_hash_map.h @@ -0,0 +1,582 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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: flat_hash_map.h +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_map` is an unordered associative container of +// unique keys and associated values designed to be a more efficient replacement +// for `std::unordered_map`. Like `unordered_map`, search, insertion, and +// deletion of map elements can be done as an `O(1)` operation. However, +// `flat_hash_map` (and other unordered associative containers known as the +// collection of Abseil "Swiss tables") contain other optimizations that result +// in both memory and computation advantages. +// +// In most cases, your default choice for a hash map should be a map of type +// `flat_hash_map`. + +#ifndef ABSL_CONTAINER_FLAT_HASH_MAP_H_ +#define ABSL_CONTAINER_FLAT_HASH_MAP_H_ + +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +template +struct FlatHashMapPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::flat_hash_map +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_map` is an unordered associative container which +// has been optimized for both speed and memory footprint in most common use +// cases. Its interface is similar to that of `std::unordered_map` with +// the following notable differences: +// +// * Requires keys that are CopyConstructible +// * Requires values that are MoveConstructible +// * Supports heterogeneous lookup, through `find()`, `operator[]()` and +// `insert()`, provided that the map is provided a compatible heterogeneous +// hashing function and equality operator. +// * Invalidates any references and pointers to elements within the table after +// `rehash()`. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash map. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `flat_hash_map` uses the `absl::Hash` hashing framework. +// All fundamental and Abseil types that support the `absl::Hash` framework have +// a compatible equality operator for comparing insertions into `flat_hash_map`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// 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, +// 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`. +// +// Example: +// +// // Create a flat hash map of three strings (that map to strings) +// absl::flat_hash_map ducks = +// {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}}; +// +// // Insert a new element into the flat hash map +// ducks.insert({"d", "donald"}); +// +// // Force a rehash of the flat hash map +// ducks.rehash(0); +// +// // Find the element with the key "b" +// std::string search_key = "b"; +// auto result = ducks.find(search_key); +// if (result != ducks.end()) { +// std::cout << "Result: " << result->second << std::endl; +// } +template , + class Eq = absl::container_internal::hash_default_eq, + class Allocator = std::allocator>> +class flat_hash_map : public absl::container_internal::raw_hash_map< + absl::container_internal::FlatHashMapPolicy, + Hash, Eq, Allocator> { + using Base = typename flat_hash_map::raw_hash_map; + + public: + // Constructors and Assignment Operators + // + // A flat_hash_map supports the same overload set as `std::unordered_map` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::flat_hash_map map1; + // + // * Initializer List constructor + // + // absl::flat_hash_map map2 = + // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; + // + // * Copy constructor + // + // absl::flat_hash_map map3(map2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::flat_hash_map map4; + // map4 = map3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::flat_hash_map map5(std::move(map4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::flat_hash_map map6; + // map6 = std::move(map5); + // + // * Range constructor + // + // std::vector> v = {{1, "a"}, {2, "b"}}; + // absl::flat_hash_map map7(v.begin(), v.end()); + flat_hash_map() {} + using Base::Base; + + // flat_hash_map::begin() + // + // Returns an iterator to the beginning of the `flat_hash_map`. + using Base::begin; + + // flat_hash_map::cbegin() + // + // Returns a const iterator to the beginning of the `flat_hash_map`. + using Base::cbegin; + + // flat_hash_map::cend() + // + // Returns a const iterator to the end of the `flat_hash_map`. + using Base::cend; + + // flat_hash_map::end() + // + // Returns an iterator to the end of the `flat_hash_map`. + using Base::end; + + // flat_hash_map::capacity() + // + // Returns the number of element slots (assigned, deleted, and empty) + // available within the `flat_hash_map`. + // + // NOTE: this member function is particular to `absl::flat_hash_map` and is + // not provided in the `std::unordered_map` API. + using Base::capacity; + + // flat_hash_map::empty() + // + // Returns whether or not the `flat_hash_map` is empty. + using Base::empty; + + // flat_hash_map::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `flat_hash_map` under current memory constraints. This value can be thought + // of the largest value of `std::distance(begin(), end())` for a + // `flat_hash_map`. + using Base::max_size; + + // flat_hash_map::size() + // + // Returns the number of elements currently within the `flat_hash_map`. + using Base::size; + + // flat_hash_map::clear() + // + // Removes all elements from the `flat_hash_map`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // flat_hash_map::erase() + // + // Erases elements within the `flat_hash_map`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // 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. + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists. + using Base::erase; + + // flat_hash_map::insert() + // + // Inserts an element of the specified value into the `flat_hash_map`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair insert(const init_type& value): + // + // Inserts a value into the `flat_hash_map`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(T&& value): + // std::pair insert(init_type&& value): + // + // Inserts a moveable value into the `flat_hash_map`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const init_type& value): + // iterator insert(const_iterator hint, T&& value): + // iterator insert(const_iterator hint, init_type&& value); + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently, for `flat_hash_map` we guarantee the + // first match is inserted. + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently within the initializer list, for + // `flat_hash_map` we guarantee the first match is inserted. + using Base::insert; + + // flat_hash_map::insert_or_assign() + // + // Inserts an element of the specified value into the `flat_hash_map` provided + // that a value with the given key does not already exist, or replaces it with + // the element value if a key for that value already exists, returning an + // iterator pointing to the newly inserted element. If rehashing occurs due + // to the insertion, all existing iterators are invalidated. Overloads are + // listed below. + // + // pair insert_or_assign(const init_type& k, T&& obj): + // pair insert_or_assign(init_type&& k, T&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `flat_hash_map`. + // + // iterator insert_or_assign(const_iterator hint, + // const init_type& k, T&& obj): + // iterator insert_or_assign(const_iterator hint, init_type&& k, T&& obj): + // + // Inserts/Assigns (or moves) the element of the specified key into the + // `flat_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::insert_or_assign; + + // flat_hash_map::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_map`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace; + + // flat_hash_map::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_map`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. Prefer `try_emplace()` unless your key is not + // copyable or moveable. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace_hint; + + // flat_hash_map::try_emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_map`, provided that no element with the given key + // already exists. Unlike `emplace()`, if an element with the given key + // already exists, we guarantee that no element is constructed. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + // Overloads are listed below. + // + // pair try_emplace(const key_type& k, Args&&... args): + // pair try_emplace(key_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `flat_hash_map`. + // + // iterator try_emplace(const_iterator hint, + // const init_type& k, Args&&... args): + // iterator try_emplace(const_iterator hint, init_type&& k, Args&&... args): + // + // Inserts (via copy or move) the element of the specified key into the + // `flat_hash_map` using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. + using Base::try_emplace; + + // flat_hash_map::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the key,value pair of the element at the indicated position and + // returns a node handle owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the key,value pair of the element with a key matching the passed + // key value and returns a node handle owning that extracted data. If the + // `flat_hash_map` does not contain an element with a matching key, this + // function returns an empty node handle. + using Base::extract; + + // flat_hash_map::merge() + // + // Extracts elements from a given `source` flat hash map into this + // `flat_hash_map`. If the destination `flat_hash_map` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // flat_hash_map::swap(flat_hash_map& other) + // + // Exchanges the contents of this `flat_hash_map` with those of the `other` + // flat hash map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `flat_hash_map` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash map's hashing and key equivalence + // functions be Swappable, and are exchaged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // flat_hash_map::rehash(count) + // + // Rehashes the `flat_hash_map`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_map`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // flat_hash_map::reserve(count) + // + // Sets the number of slots in the `flat_hash_map` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // flat_hash_map::at() + // + // Returns a reference to the mapped value of the element with key equivalent + // to the passed key. + using Base::at; + + // flat_hash_map::contains() + // + // Determines whether an element with a key comparing equal to the given `key` + // exists within the `flat_hash_map`, returning `true` if so or `false` + // otherwise. + using Base::contains; + + // flat_hash_map::count(const Key& key) const + // + // Returns the number of elements with a key comparing equal to the given + // `key` within the `flat_hash_map`. note that this function will return + // either `1` or `0` since duplicate keys are not allowed within a + // `flat_hash_map`. + using Base::count; + + // flat_hash_map::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `flat_hash_map`. + using Base::equal_range; + + // flat_hash_map::find() + // + // Finds an element with the passed `key` within the `flat_hash_map`. + using Base::find; + + // flat_hash_map::operator[]() + // + // Returns a reference to the value mapped to the passed key within the + // `flat_hash_map`, performing an `insert()` if the key does not already + // exist. + // + // If an insertion occurs and results in a rehashing of the container, all + // iterators are invalidated. Otherwise iterators are not affected and + // references are not invalidated. Overloads are listed below. + // + // T& operator[](const Key& key): + // + // Inserts an init_type object constructed in-place if the element with the + // given key does not exist. + // + // T& operator[](Key&& key): + // + // Inserts an init_type object constructed in-place provided that an element + // with the given key does not exist. + using Base::operator[]; + + // flat_hash_map::bucket_count() + // + // Returns the number of "buckets" within the `flat_hash_map`. Note that + // because a flat hash map contains all elements within its internal storage, + // this value simply equals the current capacity of the `flat_hash_map`. + using Base::bucket_count; + + // flat_hash_map::load_factor() + // + // Returns the current load factor of the `flat_hash_map` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // flat_hash_map::max_load_factor() + // + // Manages the maximum load factor of the `flat_hash_map`. Overloads are + // listed below. + // + // float flat_hash_map::max_load_factor() + // + // Returns the current maximum load factor of the `flat_hash_map`. + // + // void flat_hash_map::max_load_factor(float ml) + // + // Sets the maximum load factor of the `flat_hash_map` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `flat_hash_map` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // flat_hash_map::get_allocator() + // + // Returns the allocator function associated with this `flat_hash_map`. + using Base::get_allocator; + + // flat_hash_map::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `flat_hash_map`. + using Base::hash_function; + + // flat_hash_map::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +namespace container_internal { + +template +struct FlatHashMapPolicy { + using slot_type = container_internal::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)...); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + slot_type::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); + } + + template + static decltype(absl::container_internal::DecomposePair( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposePair(std::forward(f), + std::forward(args)...); + } + + static size_t space_used(const slot_type*) { return 0; } + + static std::pair& element(slot_type* slot) { return slot->value; } + + static V& value(std::pair* kv) { return kv->second; } + static const V& value(const std::pair* kv) { return kv->second; } +}; + +} // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template +struct IsUnorderedContainer< + absl::flat_hash_map> : std::true_type {}; + +} // namespace container_algorithm_internal + +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..02d2fa81e2a --- /dev/null +++ b/absl/container/flat_hash_map_test.cc @@ -0,0 +1,243 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/flat_hash_map.h" + +#include "absl/container/internal/hash_generator_testing.h" +#include "absl/container/internal/unordered_map_constructor_test.h" +#include "absl/container/internal/unordered_map_lookup_test.h" +#include "absl/container/internal/unordered_map_modifiers_test.h" +#include "absl/types/any.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { +using ::absl::container_internal::hash_internal::Enum; +using ::absl::container_internal::hash_internal::EnumClass; +using ::testing::_; +using ::testing::Pair; +using ::testing::UnorderedElementsAre; + +template +using Map = + flat_hash_map>; + +static_assert(!std::is_standard_layout(), ""); + +using MapTypes = + ::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); + +TEST(FlatHashMap, StandardLayout) { + struct Int { + explicit Int(size_t value) : value(value) {} + Int() : value(0) { ADD_FAILURE(); } + Int(const Int& other) : value(other.value) { ADD_FAILURE(); } + Int(Int&&) = default; + bool operator==(const Int& other) const { return value == other.value; } + size_t value; + }; + static_assert(std::is_standard_layout(), ""); + + struct Hash { + size_t operator()(const Int& obj) const { return obj.value; } + }; + + // Verify that neither the key nor the value get default-constructed or + // copy-constructed. + { + flat_hash_map m; + m.try_emplace(Int(1), Int(2)); + m.try_emplace(Int(3), Int(4)); + m.erase(Int(1)); + m.rehash(2 * m.bucket_count()); + } + { + flat_hash_map m; + m.try_emplace(Int(1), Int(2)); + m.try_emplace(Int(3), Int(4)); + m.erase(Int(1)); + m.clear(); + } +} + +// gcc becomes unhappy if this is inside the method, so pull it out here. +struct balast {}; + +TEST(FlatHashMap, IteratesMsan) { + // Because SwissTable randomizes on pointer addresses, we keep old tables + // around to ensure we don't reuse old memory. + std::vector> garbage; + for (int i = 0; i < 100; ++i) { + absl::flat_hash_map t; + for (int j = 0; j < 100; ++j) { + t[j]; + for (const auto& p : t) EXPECT_THAT(p, Pair(_, _)); + } + garbage.push_back(std::move(t)); + } +} + +// Demonstration of the "Lazy Key" pattern. This uses heterogeneous insert to +// avoid creating expensive key elements when the item is already present in the +// map. +struct LazyInt { + explicit LazyInt(size_t value, int* tracker) + : value(value), tracker(tracker) {} + + explicit operator size_t() const { + ++*tracker; + return value; + } + + size_t value; + int* tracker; +}; + +struct Hash { + using is_transparent = void; + int* tracker; + size_t operator()(size_t obj) const { + ++*tracker; + return obj; + } + size_t operator()(const LazyInt& obj) const { + ++*tracker; + return obj.value; + } +}; + +struct Eq { + using is_transparent = void; + bool operator()(size_t lhs, size_t rhs) const { + return lhs == rhs; + } + bool operator()(size_t lhs, const LazyInt& rhs) const { + return lhs == rhs.value; + } +}; + +TEST(FlatHashMap, LazyKeyPattern) { + // hashes are only guaranteed in opt mode, we use assertions to track internal + // state that can cause extra calls to hash. + int conversions = 0; + int hashes = 0; + flat_hash_map m(0, Hash{&hashes}); + + m[LazyInt(1, &conversions)] = 1; + EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1))); + EXPECT_EQ(conversions, 1); +#ifdef NDEBUG + EXPECT_EQ(hashes, 1); +#endif + + m[LazyInt(1, &conversions)] = 2; + EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2))); + EXPECT_EQ(conversions, 1); +#ifdef NDEBUG + EXPECT_EQ(hashes, 2); +#endif + + m.try_emplace(LazyInt(2, &conversions), 3); + EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3))); + EXPECT_EQ(conversions, 2); +#ifdef NDEBUG + EXPECT_EQ(hashes, 3); +#endif + + m.try_emplace(LazyInt(2, &conversions), 4); + EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3))); + EXPECT_EQ(conversions, 2); +#ifdef NDEBUG + EXPECT_EQ(hashes, 4); +#endif +} + +TEST(FlatHashMap, BitfieldArgument) { + union { + int n : 1; + }; + n = 0; + flat_hash_map m; + m.erase(n); + m.count(n); + m.prefetch(n); + m.find(n); + m.contains(n); + m.equal_range(n); + m.insert_or_assign(n, n); + m.insert_or_assign(m.end(), n, n); + m.try_emplace(n); + m.try_emplace(m.end(), n); + m.at(n); + m[n]; +} + +TEST(FlatHashMap, MergeExtractInsert) { + // We can't test mutable keys, or non-copyable keys with flat_hash_map. + // Test that the nodes have the proper API. + absl::flat_hash_map m = {{1, 7}, {2, 9}}; + auto node = m.extract(1); + EXPECT_TRUE(node); + EXPECT_EQ(node.key(), 1); + EXPECT_EQ(node.mapped(), 7); + EXPECT_THAT(m, UnorderedElementsAre(Pair(2, 9))); + + node.mapped() = 17; + m.insert(std::move(node)); + EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9))); +} +#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__) +TEST(FlatHashMap, Any) { + absl::flat_hash_map m; + m.emplace(1, 7); + auto it = m.find(1); + ASSERT_NE(it, m.end()); + EXPECT_EQ(7, absl::any_cast(it->second)); + + m.emplace(std::piecewise_construct, std::make_tuple(2), std::make_tuple(8)); + it = m.find(2); + ASSERT_NE(it, m.end()); + EXPECT_EQ(8, absl::any_cast(it->second)); + + m.emplace(std::piecewise_construct, std::make_tuple(3), + std::make_tuple(absl::any(9))); + it = m.find(3); + ASSERT_NE(it, m.end()); + EXPECT_EQ(9, absl::any_cast(it->second)); + + struct H { + size_t operator()(const absl::any&) const { return 0; } + }; + struct E { + bool operator()(const absl::any&, const absl::any&) const { return true; } + }; + absl::flat_hash_map m2; + m2.emplace(1, 7); + auto it2 = m2.find(1); + ASSERT_NE(it2, m2.end()); + EXPECT_EQ(7, it2->second); +} +#endif // __ANDROID__ + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h new file mode 100644 index 00000000000..b175b1bf49b --- /dev/null +++ b/absl/container/flat_hash_set.h @@ -0,0 +1,491 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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: flat_hash_set.h +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_set` is an unordered associative container designed to +// be a more efficient replacement for `std::unordered_set`. Like +// `unordered_set`, search, insertion, and deletion of set elements can be done +// as an `O(1)` operation. However, `flat_hash_set` (and other unordered +// associative containers known as the collection of Abseil "Swiss tables") +// contain other optimizations that result in both memory and computation +// advantages. +// +// In most cases, your default choice for a hash set should be a set of type +// `flat_hash_set`. +#ifndef ABSL_CONTAINER_FLAT_HASH_SET_H_ +#define ABSL_CONTAINER_FLAT_HASH_SET_H_ + +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/base/macros.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export +#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export +#include "absl/memory/memory.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +template +struct FlatHashSetPolicy; +} // namespace container_internal + +// ----------------------------------------------------------------------------- +// absl::flat_hash_set +// ----------------------------------------------------------------------------- +// +// An `absl::flat_hash_set` is an unordered associative container which has +// been optimized for both speed and memory footprint in most common use cases. +// Its interface is similar to that of `std::unordered_set` with the +// 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. +// * Invalidates any references and pointers to elements within the table after +// `rehash()`. +// * Contains a `capacity()` member function indicating the number of element +// slots (open, deleted, and empty) within the hash set. +// * Returns `void` from the `erase(iterator)` overload. +// +// By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All +// fundamental and Abseil types that support the `absl::Hash` framework have a +// compatible equality operator for comparing insertions into `flat_hash_map`. +// If your type is not yet supported by the `absl::Hash` framework, see +// absl/hash/hash.h for information on extending Abseil hashing to user-defined +// types. +// +// NOTE: A `flat_hash_set` stores its keys directly inside its implementation +// array to avoid memory indirection. Because a `flat_hash_set` is designed to +// move data when rehashed, set keys will not retain pointer stability. If you +// require pointer stability, consider using +// `absl::flat_hash_set>`. If your type is not moveable and +// you require pointer stability, consider `absl::node_hash_set` instead. +// +// Example: +// +// // Create a flat hash set of three strings +// absl::flat_hash_set ducks = +// {"huey", "dewey", "louie"}; +// +// // Insert a new element into the flat hash set +// ducks.insert("donald"); +// +// // Force a rehash of the flat hash set +// ducks.rehash(0); +// +// // See if "dewey" is present +// if (ducks.contains("dewey")) { +// std::cout << "We found dewey!" << std::endl; +// } +template , + class Eq = absl::container_internal::hash_default_eq, + class Allocator = std::allocator> +class flat_hash_set + : public absl::container_internal::raw_hash_set< + absl::container_internal::FlatHashSetPolicy, Hash, Eq, Allocator> { + using Base = typename flat_hash_set::raw_hash_set; + + public: + // Constructors and Assignment Operators + // + // A flat_hash_set supports the same overload set as `std::unordered_map` + // for construction and assignment: + // + // * Default constructor + // + // // No allocation for the table's elements is made. + // absl::flat_hash_set set1; + // + // * Initializer List constructor + // + // absl::flat_hash_set set2 = + // {{"huey"}, {"dewey"}, {"louie"},}; + // + // * Copy constructor + // + // absl::flat_hash_set set3(set2); + // + // * Copy assignment operator + // + // // Hash functor and Comparator are copied as well + // absl::flat_hash_set set4; + // set4 = set3; + // + // * Move constructor + // + // // Move is guaranteed efficient + // absl::flat_hash_set set5(std::move(set4)); + // + // * Move assignment operator + // + // // May be efficient if allocators are compatible + // absl::flat_hash_set set6; + // set6 = std::move(set5); + // + // * Range constructor + // + // std::vector v = {"a", "b"}; + // absl::flat_hash_set set7(v.begin(), v.end()); + flat_hash_set() {} + using Base::Base; + + // flat_hash_set::begin() + // + // Returns an iterator to the beginning of the `flat_hash_set`. + using Base::begin; + + // flat_hash_set::cbegin() + // + // Returns a const iterator to the beginning of the `flat_hash_set`. + using Base::cbegin; + + // flat_hash_set::cend() + // + // Returns a const iterator to the end of the `flat_hash_set`. + using Base::cend; + + // flat_hash_set::end() + // + // Returns an iterator to the end of the `flat_hash_set`. + using Base::end; + + // flat_hash_set::capacity() + // + // Returns the number of element slots (assigned, deleted, and empty) + // available within the `flat_hash_set`. + // + // NOTE: this member function is particular to `absl::flat_hash_set` and is + // not provided in the `std::unordered_map` API. + using Base::capacity; + + // flat_hash_set::empty() + // + // Returns whether or not the `flat_hash_set` is empty. + using Base::empty; + + // flat_hash_set::max_size() + // + // Returns the largest theoretical possible number of elements within a + // `flat_hash_set` under current memory constraints. This value can be thought + // of the largest value of `std::distance(begin(), end())` for a + // `flat_hash_set`. + using Base::max_size; + + // flat_hash_set::size() + // + // Returns the number of elements currently within the `flat_hash_set`. + using Base::size; + + // flat_hash_set::clear() + // + // Removes all elements from the `flat_hash_set`. Invalidates any references, + // pointers, or iterators referring to contained elements. + // + // NOTE: this operation may shrink the underlying buffer. To avoid shrinking + // the underlying buffer call `erase(begin(), end())`. + using Base::clear; + + // flat_hash_set::erase() + // + // Erases elements within the `flat_hash_set`. Erasing does not trigger a + // rehash. Overloads are listed below. + // + // void erase(const_iterator pos): + // + // 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. + // + // iterator erase(const_iterator first, const_iterator last): + // + // Erases the elements in the open interval [`first`, `last`), returning an + // iterator pointing to `last`. + // + // size_type erase(const key_type& key): + // + // Erases the element with the matching key, if it exists. + using Base::erase; + + // flat_hash_set::insert() + // + // Inserts an element of the specified value into the `flat_hash_set`, + // returning an iterator pointing to the newly inserted element, provided that + // an element with the given key does not already exist. If rehashing occurs + // due to the insertion, all iterators are invalidated. Overloads are listed + // below. + // + // std::pair insert(const T& value): + // + // Inserts a value into the `flat_hash_set`. Returns a pair consisting of an + // iterator to the inserted element (or to the element that prevented the + // insertion) and a bool denoting whether the insertion took place. + // + // std::pair insert(T&& value): + // + // Inserts a moveable value into the `flat_hash_set`. Returns a pair + // consisting of an iterator to the inserted element (or to the element that + // prevented the insertion) and a bool denoting whether the insertion took + // place. + // + // iterator insert(const_iterator hint, const T& value): + // iterator insert(const_iterator hint, T&& value): + // + // Inserts a value, using the position of `hint` as a non-binding suggestion + // for where to begin the insertion search. Returns an iterator to the + // inserted element, or to the existing element that prevented the + // insertion. + // + // void insert(InputIterator first, InputIterator last): + // + // Inserts a range of values [`first`, `last`). + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently, for `flat_hash_set` we guarantee the + // first match is inserted. + // + // void insert(std::initializer_list ilist): + // + // Inserts the elements within the initializer list `ilist`. + // + // NOTE: Although the STL does not specify which element may be inserted if + // multiple keys compare equivalently within the initializer list, for + // `flat_hash_set` we guarantee the first match is inserted. + using Base::insert; + + // flat_hash_set::emplace() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_set`, provided that no element with the given key + // already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace; + + // flat_hash_set::emplace_hint() + // + // Inserts an element of the specified value by constructing it in-place + // within the `flat_hash_set`, using the position of `hint` as a non-binding + // suggestion for where to begin the insertion search, and only inserts + // provided that no element with the given key already exists. + // + // The element may be constructed even if there already is an element with the + // key in the container, in which case the newly constructed element will be + // destroyed immediately. + // + // If rehashing occurs due to the insertion, all iterators are invalidated. + using Base::emplace_hint; + + // flat_hash_set::extract() + // + // Extracts the indicated element, erasing it in the process, and returns it + // as a C++17-compatible node handle. Overloads are listed below. + // + // node_type extract(const_iterator position): + // + // Extracts the element at the indicated position and returns a node handle + // owning that extracted data. + // + // node_type extract(const key_type& x): + // + // Extracts the element with the key matching the passed key value and + // returns a node handle owning that extracted data. If the `flat_hash_set` + // does not contain an element with a matching key, this function returns an + // empty node handle. + using Base::extract; + + // flat_hash_set::merge() + // + // Extracts elements from a given `source` flat hash map into this + // `flat_hash_set`. If the destination `flat_hash_set` already contains an + // element with an equivalent key, that element is not extracted. + using Base::merge; + + // flat_hash_set::swap(flat_hash_set& other) + // + // Exchanges the contents of this `flat_hash_set` with those of the `other` + // flat hash map, avoiding invocation of any move, copy, or swap operations on + // individual elements. + // + // All iterators and references on the `flat_hash_set` remain valid, excepting + // for the past-the-end iterator, which is invalidated. + // + // `swap()` requires that the flat hash set's hashing and key equivalence + // functions be Swappable, and are exchaged using unqualified calls to + // non-member `swap()`. If the map's allocator has + // `std::allocator_traits::propagate_on_container_swap::value` + // set to `true`, the allocators are also exchanged using an unqualified call + // to non-member `swap()`; otherwise, the allocators are not swapped. + using Base::swap; + + // flat_hash_set::rehash(count) + // + // Rehashes the `flat_hash_set`, setting the number of slots to be at least + // the passed value. If the new number of slots increases the load factor more + // than the current maximum load factor + // (`count` < `size()` / `max_load_factor()`), then the new number of slots + // will be at least `size()` / `max_load_factor()`. + // + // To force a rehash, pass rehash(0). + // + // NOTE: unlike behavior in `std::unordered_set`, references are also + // invalidated upon a `rehash()`. + using Base::rehash; + + // flat_hash_set::reserve(count) + // + // Sets the number of slots in the `flat_hash_set` to the number needed to + // accommodate at least `count` total elements without exceeding the current + // maximum load factor, and may rehash the container if needed. + using Base::reserve; + + // flat_hash_set::contains() + // + // Determines whether an element comparing equal to the given `key` exists + // within the `flat_hash_set`, returning `true` if so or `false` otherwise. + using Base::contains; + + // flat_hash_set::count(const Key& key) const + // + // Returns the number of elements comparing equal to the given `key` within + // the `flat_hash_set`. note that this function will return either `1` or `0` + // since duplicate elements are not allowed within a `flat_hash_set`. + using Base::count; + + // flat_hash_set::equal_range() + // + // Returns a closed range [first, last], defined by a `std::pair` of two + // iterators, containing all elements with the passed key in the + // `flat_hash_set`. + using Base::equal_range; + + // flat_hash_set::find() + // + // Finds an element with the passed `key` within the `flat_hash_set`. + using Base::find; + + // flat_hash_set::bucket_count() + // + // Returns the number of "buckets" within the `flat_hash_set`. Note that + // because a flat hash map contains all elements within its internal storage, + // this value simply equals the current capacity of the `flat_hash_set`. + using Base::bucket_count; + + // flat_hash_set::load_factor() + // + // Returns the current load factor of the `flat_hash_set` (the average number + // of slots occupied with a value within the hash map). + using Base::load_factor; + + // flat_hash_set::max_load_factor() + // + // Manages the maximum load factor of the `flat_hash_set`. Overloads are + // listed below. + // + // float flat_hash_set::max_load_factor() + // + // Returns the current maximum load factor of the `flat_hash_set`. + // + // void flat_hash_set::max_load_factor(float ml) + // + // Sets the maximum load factor of the `flat_hash_set` to the passed value. + // + // NOTE: This overload is provided only for API compatibility with the STL; + // `flat_hash_set` will ignore any set load factor and manage its rehashing + // internally as an implementation detail. + using Base::max_load_factor; + + // flat_hash_set::get_allocator() + // + // Returns the allocator function associated with this `flat_hash_set`. + using Base::get_allocator; + + // flat_hash_set::hash_function() + // + // Returns the hashing function used to hash the keys within this + // `flat_hash_set`. + using Base::hash_function; + + // flat_hash_set::key_eq() + // + // Returns the function used for comparing keys equality. + using Base::key_eq; +}; + +namespace container_internal { + +template +struct FlatHashSetPolicy { + using slot_type = T; + using key_type = T; + using init_type = T; + using constant_iterators = std::true_type; + + template + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + absl::allocator_traits::construct(*alloc, slot, + std::forward(args)...); + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + absl::allocator_traits::destroy(*alloc, slot); + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + construct(alloc, new_slot, std::move(*old_slot)); + destroy(alloc, old_slot); + } + + static T& element(slot_type* slot) { return *slot; } + + template + static decltype(absl::container_internal::DecomposeValue( + std::declval(), std::declval()...)) + apply(F&& f, Args&&... args) { + return absl::container_internal::DecomposeValue( + std::forward(f), std::forward(args)...); + } + + static size_t space_used(const T*) { return 0; } +}; +} // namespace container_internal + +namespace container_algorithm_internal { + +// Specialization of trait in absl/algorithm/container.h +template +struct IsUnorderedContainer> + : std::true_type {}; + +} // namespace container_algorithm_internal + +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..cabc2b59138 --- /dev/null +++ b/absl/container/flat_hash_set_test.cc @@ -0,0 +1,128 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/flat_hash_set.h" + +#include + +#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_modifiers_test.h" +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +using ::absl::container_internal::hash_internal::Enum; +using ::absl::container_internal::hash_internal::EnumClass; +using ::testing::Pointee; +using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; + +template +using Set = + absl::flat_hash_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); + +TEST(FlatHashSet, EmplaceString) { + std::vector v = {"a", "b"}; + absl::flat_hash_set hs(v.begin(), v.end()); + EXPECT_THAT(hs, UnorderedElementsAreArray(v)); +} + +TEST(FlatHashSet, BitfieldArgument) { + union { + int n : 1; + }; + n = 0; + absl::flat_hash_set s = {n}; + s.insert(n); + s.insert(s.end(), n); + s.insert({n}); + s.erase(n); + s.count(n); + s.prefetch(n); + s.find(n); + s.contains(n); + s.equal_range(n); +} + +TEST(FlatHashSet, MergeExtractInsert) { + struct Hash { + size_t operator()(const std::unique_ptr& p) const { return *p; } + }; + struct Eq { + bool operator()(const std::unique_ptr& a, + const std::unique_ptr& b) const { + return *a == *b; + } + }; + absl::flat_hash_set, Hash, Eq> set1, set2; + set1.insert(absl::make_unique(7)); + set1.insert(absl::make_unique(17)); + + set2.insert(absl::make_unique(7)); + set2.insert(absl::make_unique(19)); + + EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17))); + EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(19))); + + set1.merge(set2); + + EXPECT_THAT(set1, UnorderedElementsAre(Pointee(7), Pointee(17), Pointee(19))); + EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7))); + + auto node = set1.extract(absl::make_unique(7)); + EXPECT_TRUE(node); + EXPECT_THAT(node.value(), Pointee(7)); + EXPECT_THAT(set1, UnorderedElementsAre(Pointee(17), Pointee(19))); + + auto insert_result = set2.insert(std::move(node)); + EXPECT_FALSE(node); + EXPECT_FALSE(insert_result.inserted); + EXPECT_TRUE(insert_result.node); + EXPECT_THAT(insert_result.node.value(), Pointee(7)); + EXPECT_EQ(**insert_result.position, 7); + EXPECT_NE(insert_result.position->get(), insert_result.node.value().get()); + EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7))); + + node = set1.extract(absl::make_unique(17)); + EXPECT_TRUE(node); + EXPECT_THAT(node.value(), Pointee(17)); + EXPECT_THAT(set1, UnorderedElementsAre(Pointee(19))); + + node.value() = absl::make_unique(23); + + insert_result = set2.insert(std::move(node)); + EXPECT_FALSE(node); + EXPECT_TRUE(insert_result.inserted); + EXPECT_FALSE(insert_result.node); + EXPECT_EQ(**insert_result.position, 23); + EXPECT_THAT(set2, UnorderedElementsAre(Pointee(7), Pointee(23))); +} + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 183ade5405f..37714baf5c2 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Abseil Authors. +// 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. @@ -20,17 +20,17 @@ // vector" which behaves in an equivalent fashion to a `std::vector`, except // that storage for small sequences of the vector are provided inline without // requiring any heap allocation. - -// An `absl::InlinedVector` specifies the size N at which to inline as one -// of its template parameters. Vectors of length <= N are provided inline. -// Typically N is very small (e.g., 4) so that sequences that are expected to be -// short do not require allocations. - -// An `absl::InlinedVector` does not usually require a specific allocator; if +// +// An `absl::InlinedVector` specifies the default capacity `N` as one of +// its template parameters. Instances where `size() <= N` hold contained +// elements in inline space. Typically `N` is very small so that sequences that +// are expected to be short do not require allocations. +// +// An `absl::InlinedVector` does not usually require a specific allocator. If // the inlined vector grows beyond its initial constraints, it will need to -// allocate (as any normal `std::vector` would) and it will generally use the -// default allocator in that case; optionally, a custom allocator may be -// specified using an `absl::InlinedVector` construction. +// allocate (as any normal `std::vector` would). This is usually performed with +// the default allocator (defined as `std::allocator`). Optionally, a custom +// allocator type may be specified as `A` in `absl::InlinedVector`. #ifndef ABSL_CONTAINER_INLINED_VECTOR_H_ #define ABSL_CONTAINER_INLINED_VECTOR_H_ @@ -53,7 +53,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_06_20 { +inline namespace lts_2018_12_18 { // ----------------------------------------------------------------------------- // InlinedVector @@ -62,12 +62,30 @@ inline namespace lts_2018_06_20 { // An `absl::InlinedVector` is designed to be a drop-in replacement for // `std::vector` for use cases where the vector's size is sufficiently small // that it can be inlined. If the inlined vector does grow beyond its estimated -// size, it will trigger an initial allocation on the heap, and will behave as a -// `std:vector`. The API of the `absl::InlinedVector` within this file is +// capacity, it will trigger an initial allocation on the heap, and will behave +// as a `std:vector`. The API of the `absl::InlinedVector` within this file is // designed to cover the same API footprint as covered by `std::vector`. -template > +template > class InlinedVector { - using AllocatorTraits = std::allocator_traits; + static_assert(N > 0, "InlinedVector requires inline capacity greater than 0"); + constexpr static typename A::size_type inlined_capacity() { + return static_cast(N); + } + + template + using DisableIfIntegral = + absl::enable_if_t::value>; + + template + using EnableIfInputIterator = absl::enable_if_t::iterator_category, + std::input_iterator_tag>::value>; + + template + using IteratorCategory = + typename std::iterator_traits::iterator_category; + + using rvalue_reference = typename A::value_type&&; public: using allocator_type = A; @@ -83,51 +101,64 @@ class InlinedVector { using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::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 specified allocator. explicit InlinedVector(const allocator_type& alloc) noexcept : allocator_and_tag_(alloc) {} - // Create a vector with n copies of value_type(). - explicit InlinedVector(size_type n) : allocator_and_tag_(allocator_type()) { + // 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); } - // Create a vector with n copies of elem - InlinedVector(size_type n, const value_type& elem, + // 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, elem); + InitAssign(n, v); } - // Create and initialize with the elements [first .. last). - // The unused enable_if argument restricts this constructor so that it is - // elided when value_type is an integral type. This prevents ambiguous - // interpretation between a call to this constructor with two integral - // arguments and a call to the preceding (n, elem) constructor. - template - InlinedVector( - InputIterator first, InputIterator last, - const allocator_type& alloc = allocator_type(), - typename std::enable_if::value>::type* = - nullptr) + // Creates an inlined vector of copies of the values in `init_list`. + InlinedVector(std::initializer_list init_list, + const allocator_type& alloc = allocator_type()) : allocator_and_tag_(alloc) { - AppendRange(first, last); + AppendRange(init_list.begin(), init_list.end(), + IteratorCategory{}); } - InlinedVector(std::initializer_list init, + // Creates an inlined vector with elements constructed from the provided + // Iterator range [`first`, `last`). + // + // NOTE: The `enable_if` prevents ambiguous interpretation between a call to + // this constructor with two integral arguments and a call to the above + // `InlinedVector(size_type, const_reference)` constructor. + template * = nullptr> + InlinedVector(InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()) : allocator_and_tag_(alloc) { - AppendRange(init.begin(), init.end()); + AppendRange(first, last, IteratorCategory{}); } - InlinedVector(const InlinedVector& v); - InlinedVector(const InlinedVector& v, const allocator_type& alloc); + // Creates a copy of `other` using `other`'s allocator. + InlinedVector(const InlinedVector& other); - // This move constructor does not allocate and only moves the underlying + // Creates a copy of `other` but with a specified allocator. + InlinedVector(const InlinedVector& other, const allocator_type& alloc); + + // Creates an inlined vector by moving in the contents of `other`. + // + // NOTE: This move constructor does not allocate and only moves the underlying // objects, so its `noexcept` specification depends on whether moving the - // underlying objects can throw or not. We assume + // underlying objects can throw or not. We assume: // a) move constructors should only throw due to allocation failure and // b) if `value_type`'s move constructor allocates, it uses the same // allocation function as the `InlinedVector`'s allocator, so the move @@ -137,408 +168,422 @@ class InlinedVector { absl::allocator_is_nothrow::value || std::is_nothrow_move_constructible::value); - // This move constructor allocates and also 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 above, the `noexcept` specification is dominated by whether the - // allocation can throw regardless of whether `value_type`'s move constructor - // is specified as `noexcept`. + // Creates an inlined vector by moving in the contents of `other`. + // + // 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); ~InlinedVector() { clear(); } - InlinedVector& operator=(const InlinedVector& v) { - if (this == &v) { - return *this; - } - // Optimized to avoid reallocation. - // Prefer reassignment to copy construction for elements. - if (size() < v.size()) { // grow - reserve(v.size()); - std::copy(v.begin(), v.begin() + size(), begin()); - std::copy(v.begin() + size(), v.end(), std::back_inserter(*this)); - } else { // maybe shrink - erase(begin() + v.size(), end()); - std::copy(v.begin(), v.end(), begin()); - } - return *this; - } - - InlinedVector& operator=(InlinedVector&& v) { - if (this == &v) { - return *this; - } - if (v.allocated()) { - clear(); - tag().set_allocated_size(v.size()); - init_allocation(v.allocation()); - v.tag() = Tag(); - } else { - if (allocated()) clear(); - // Both are inlined now. - if (size() < v.size()) { - auto mid = std::make_move_iterator(v.begin() + size()); - std::copy(std::make_move_iterator(v.begin()), mid, begin()); - UninitializedCopy(mid, std::make_move_iterator(v.end()), end()); - } else { - auto new_end = std::copy(std::make_move_iterator(v.begin()), - std::make_move_iterator(v.end()), begin()); - Destroy(new_end, end()); - } - tag().set_inline_size(v.size()); - } - return *this; - } - - InlinedVector& operator=(std::initializer_list init) { - AssignRange(init.begin(), init.end()); - return *this; - } + // --------------------------------------------------------------------------- + // InlinedVector Member Accessors + // --------------------------------------------------------------------------- - // InlinedVector::assign() + // `InlinedVector::empty()` // - // Replaces the contents of the inlined vector with copies of those in the - // iterator range [first, last). - template - void assign( - InputIterator first, InputIterator last, - typename std::enable_if::value>::type* = - nullptr) { - AssignRange(first, last); - } - - // Overload of `InlinedVector::assign()` to take values from elements of an - // initializer list - void assign(std::initializer_list init) { - AssignRange(init.begin(), init.end()); - } - - // Overload of `InlinedVector::assign()` to replace the first `n` elements of - // the inlined vector with `elem` values. - void assign(size_type n, const value_type& elem) { - if (n <= size()) { // Possibly shrink - std::fill_n(begin(), n, elem); - erase(begin() + n, end()); - return; - } - // Grow - reserve(n); - std::fill_n(begin(), size(), elem); - if (allocated()) { - UninitializedFill(allocated_space() + size(), allocated_space() + n, - elem); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space() + size(), inlined_space() + n, elem); - tag().set_inline_size(n); - } - } + // Checks if the inlined vector has no elements. + bool empty() const noexcept { return !size(); } - // InlinedVector::size() + // `InlinedVector::size()` // // Returns the number of elements in the inlined vector. size_type size() const noexcept { return tag().size(); } - // InlinedVector::empty() - // - // Checks if the inlined vector has no elements. - bool empty() const noexcept { return (size() == 0); } - - // InlinedVector::capacity() - // - // Returns the number of elements that can be stored in an inlined vector - // without requiring a reallocation of underlying memory. Note that for - // most inlined vectors, `capacity()` should equal its initial size `N`; for - // inlined vectors which exceed this capacity, they will no longer be inlined, - // and `capacity()` will equal its capacity on the allocated heap. - size_type capacity() const noexcept { - return allocated() ? allocation().capacity() : N; - } - - // InlinedVector::max_size() + // `InlinedVector::max_size()` // // Returns the maximum number of elements the 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 our size type. - return std::numeric_limits::max() / 2; + // vector is allocated. As a result, the maximum size of the container that + // we can express is half of the max for `size_type`. + return (std::numeric_limits::max)() / 2; } - // InlinedVector::data() + // `InlinedVector::capacity()` // - // Returns a const T* pointer to elements of the inlined vector. This pointer - // can be used to access (but not modify) the contained elements. - // Only results within the range `[0,size())` are defined. - const_pointer data() const noexcept { - return allocated() ? allocated_space() : inlined_space(); + // Returns the number of elements that can be stored in the inlined vector + // without requiring a reallocation of underlying memory. + // + // 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. + size_type capacity() const noexcept { + return allocated() ? allocation().capacity() : inlined_capacity(); } - // Overload of InlinedVector::data() to return a T* pointer to elements of the - // inlined vector. This pointer can be used to access and modify the contained - // elements. + // `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. pointer data() noexcept { return allocated() ? allocated_space() : inlined_space(); } - // InlinedVector::clear() - // - // Removes all elements from the inlined vector. - 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(); + // 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. + const_pointer data() const noexcept { + return allocated() ? allocated_space() : inlined_space(); } - // InlinedVector::at() + // `InlinedVector::operator[]()` // - // Returns the ith element of an inlined vector. - const value_type& at(size_type i) const { - if (ABSL_PREDICT_FALSE(i >= size())) { - base_internal::ThrowStdOutOfRange( - "InlinedVector::at failed bounds check"); - } + // Returns a `reference` to the `i`th element of the inlined vector using the + // array operator. + reference operator[](size_type i) { + assert(i < size()); return data()[i]; } - // InlinedVector::operator[] - // - // Returns the ith element of an inlined vector using the array operator. - const value_type& operator[](size_type i) const { + // Overload of `InlinedVector::operator[]()` to return 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]; } - // Overload of InlinedVector::at() to return the ith element of an inlined - // vector. - value_type& at(size_type i) { - if (i >= size()) { + // `InlinedVector::at()` + // + // Returns a `reference` to the `i`th element of the inlined vector. + reference at(size_type i) { + if (ABSL_PREDICT_FALSE(i >= size())) { base_internal::ThrowStdOutOfRange( - "InlinedVector::at failed bounds check"); + "InlinedVector::at() failed bounds check"); } return data()[i]; } - // Overload of InlinedVector::operator[] to return the ith element of an - // inlined vector. - value_type& operator[](size_type i) { - assert(i < size()); + // Overload of `InlinedVector::at()` to return a `const_reference` to the + // `i`th element of the inlined vector. + const_reference at(size_type i) const { + if (ABSL_PREDICT_FALSE(i >= size())) { + base_internal::ThrowStdOutOfRange( + "InlinedVector::at() failed bounds check"); + } return data()[i]; } - // InlinedVector::back() - // - // Returns a reference to the last element of an inlined vector. - value_type& back() { - assert(!empty()); - return at(size() - 1); - } - - // Overload of InlinedVector::back() returns a reference to the last element - // of an inlined vector of const values. - const value_type& back() const { - assert(!empty()); - return at(size() - 1); - } - - // InlinedVector::front() + // `InlinedVector::front()` // - // Returns a reference to the first element of an inlined vector. - value_type& front() { + // Returns a `reference` to the first element of the inlined vector. + reference front() { assert(!empty()); return at(0); } - // Overload of InlinedVector::front() returns a reference to the first element - // of an inlined vector of const values. - const value_type& front() const { + // Overload of `InlinedVector::front()` returns a `const_reference` to the + // first element of the inlined vector. + const_reference front() const { assert(!empty()); return at(0); } - // InlinedVector::emplace_back() + // `InlinedVector::back()` // - // Constructs and appends an object to the inlined vector. - // - // Returns a reference to the inserted element. - template - value_type& 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()); - - value_type* 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)...); + // Returns a `reference` to the last element of the inlined vector. + reference back() { + assert(!empty()); + return at(size() - 1); } - // InlinedVector::push_back() - // - // Appends a const element to the inlined vector. - void push_back(const value_type& t) { emplace_back(t); } - - // Overload of InlinedVector::push_back() to append a move-only element to the - // inlined vector. - void push_back(value_type&& t) { emplace_back(std::move(t)); } - - // InlinedVector::pop_back() - // - // Removes the last element (which is destroyed) in the inlined vector. - void pop_back() { + // Overload of `InlinedVector::back()` to return a `const_reference` to the + // last element of the inlined vector. + const_reference back() const { 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); - } + return at(size() - 1); } - // InlinedVector::resize() + // `InlinedVector::begin()` // - // 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); - - // Overload of InlinedVector::resize() to resize the inlined vector to contain - // `n` elements. If `n` is larger than the current size, enough copies of - // `elem` are appended to increase its size to `n`. - void resize(size_type n, const value_type& elem); - - // InlinedVector::begin() - // - // Returns an iterator to the beginning of the inlined vector. + // Returns an `iterator` to the beginning of the inlined vector. iterator begin() noexcept { return data(); } - // Overload of InlinedVector::begin() for returning a const iterator to the - // beginning of the inlined vector. + // Overload of `InlinedVector::begin()` to return a `const_iterator` to + // the beginning of the inlined vector. const_iterator begin() const noexcept { return data(); } - // InlinedVector::cbegin() - // - // Returns a const iterator to the beginning of the inlined vector. - const_iterator cbegin() const noexcept { return begin(); } - - // InlinedVector::end() + // `InlinedVector::end()` // - // Returns an iterator to the end of the inlined vector. + // Returns an `iterator` to the end of the inlined vector. iterator end() noexcept { return data() + size(); } - // Overload of InlinedVector::end() for returning a const iterator to the end - // of the inlined vector. + // Overload of `InlinedVector::end()` to return a `const_iterator` to the + // end of the inlined vector. const_iterator end() const noexcept { return data() + size(); } - // InlinedVector::cend() + // `InlinedVector::cbegin()` + // + // Returns a `const_iterator` to the beginning of the inlined vector. + const_iterator cbegin() const noexcept { return begin(); } + + // `InlinedVector::cend()` // - // Returns a const iterator to the end of the inlined vector. + // Returns a `const_iterator` to the end of the inlined vector. const_iterator cend() const noexcept { return end(); } - // InlinedVector::rbegin() + // `InlinedVector::rbegin()` // - // Returns a reverse iterator from the end of the inlined vector. + // Returns a `reverse_iterator` from the end of the inlined vector. reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - // Overload of InlinedVector::rbegin() for returning a const reverse iterator - // from the end of the inlined vector. + // Overload of `InlinedVector::rbegin()` to return a + // `const_reverse_iterator` from the end of the inlined vector. const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - // InlinedVector::crbegin() + // `InlinedVector::rend()` // - // Returns a const reverse iterator from the end of the inlined vector. - const_reverse_iterator crbegin() const noexcept { return rbegin(); } - - // InlinedVector::rend() - // - // Returns a reverse iterator from the beginning of the inlined vector. + // Returns a `reverse_iterator` from the beginning of the inlined vector. reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - // Overload of InlinedVector::rend() for returning a const reverse iterator + // Overload of `InlinedVector::rend()` to return a `const_reverse_iterator` // from the beginning of the inlined vector. const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - // InlinedVector::crend() + // `InlinedVector::crbegin()` // - // Returns a reverse iterator from the beginning of the inlined vector. + // Returns a `const_reverse_iterator` from the end of the inlined vector. + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + + // `InlinedVector::crend()` + // + // Returns a `const_reverse_iterator` from the beginning of the inlined + // vector. const_reverse_iterator crend() const noexcept { return rend(); } - // InlinedVector::emplace() + // `InlinedVector::get_allocator()` // - // Constructs and inserts an object to the inlined vector at the given - // `position`, returning an iterator pointing to the newly emplaced element. - template - iterator emplace(const_iterator position, Args&&... args); + // Returns a copy of the allocator of the inlined vector. + allocator_type get_allocator() const { return allocator(); } + + // --------------------------------------------------------------------------- + // InlinedVector Member Mutators + // --------------------------------------------------------------------------- + + // `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{}); + return *this; + } + + // Overload of `InlinedVector::operator=()` to replace the contents of the + // inlined vector with the contents 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()); + } + return *this; + } - // InlinedVector::insert() + // Overload of `InlinedVector::operator=()` to replace the contents of the + // inlined vector with the contents of `other`. // - // Inserts an element of the specified value at `position`, returning an - // iterator pointing to the newly inserted element. - iterator insert(const_iterator position, const value_type& v) { + // NOTE: As a result of calling this overload, `other` may be empty or it's + // contents may be left in a moved-from 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()); + } else { + auto new_end = std::copy(std::make_move_iterator(other.begin()), + std::make_move_iterator(other.end()), begin()); + Destroy(new_end, end()); + } + tag().set_inline_size(other.size()); + } + return *this; + } + + // `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); + } + } + + // 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 values constructed from the range [`first`, `last`). + template * = nullptr> + void assign(InputIterator first, InputIterator last) { + AssignRange(first, last, IteratorCategory{}); + } + + // `InlinedVector::resize()` + // + // 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); + + // 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); + + // `InlinedVector::insert()` + // + // Copies `v` into `position`, returning an `iterator` pointing to the newly + // inserted element. + iterator insert(const_iterator position, const_reference v) { return emplace(position, v); } - // Overload of InlinedVector::insert() for inserting an element of the - // specified rvalue, returning an iterator pointing to the newly inserted - // element. - iterator insert(const_iterator position, value_type&& 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() for inserting `n` elements of the - // specified value at `position`, returning an iterator pointing to the first + // 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 value_type& v) { + iterator insert(const_iterator position, size_type n, const_reference v) { return InsertWithCount(position, n, v); } - // Overload of `InlinedVector::insert()` to disambiguate the two - // three-argument overloads of `insert()`, returning an iterator pointing to - // the first of the newly inserted elements. + // 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()` for inserting elements constructed + // from the range [`first`, `last`). Returns 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()`. template ::iterator_category, - std::input_iterator_tag>::value>::type> + typename = EnableIfInputIterator> iterator insert(const_iterator position, InputIterator first, InputIterator last) { - using IterType = - typename std::iterator_traits::iterator_category; - return InsertWithRange(position, first, last, IterType()); + return InsertWithRange(position, first, last, + IteratorCategory()); } - // Overload of InlinedVector::insert() for inserting a list of elements at - // `position`, returning an iterator pointing to the first of the newly - // inserted elements. - iterator insert(const_iterator position, - std::initializer_list init) { - return insert(position, init.begin(), init.end()); + // `InlinedVector::emplace()` + // + // Constructs and inserts an object in the inlined vector at the given + // `position`, returning an `iterator` pointing to the newly emplaced element. + template + iterator emplace(const_iterator position, Args&&... args); + + // `InlinedVector::emplace_back()` + // + // Constructs and appends a new element to the end of the inlined vector, + // returning a `reference` to the 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)...); + } + + // `InlinedVector::push_back()` + // + // Appends a copy of `v` to the end of the inlined vector. + void push_back(const_reference v) { static_cast(emplace_back(v)); } + + // Overload of `InlinedVector::push_back()` for moving `v` into a newly + // appended element. + 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). + 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); + } } - // InlinedVector::erase() + // `InlinedVector::erase()` // // Erases the element at `position` of the inlined vector, returning an - // iterator pointing to the following element or the container's end if the - // last element was erased. + // `iterator` pointing to the first element following the erased element. + // + // NOTE: May return the end iterator, which is not dereferencable. iterator erase(const_iterator position) { assert(position >= begin()); assert(position < end()); @@ -549,23 +594,36 @@ class InlinedVector { return pos; } - // Overload of InlinedVector::erase() for erasing all elements in the - // iterator range [first, last) in the inlined vector, returning an iterator - // pointing to the first element following the range erased, or the - // container's end if range included the container's last element. - iterator erase(const_iterator first, const_iterator last); + // 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); + + // `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. + 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(); + } - // 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 that if `n` does not exceed the inlined vector's initial size `N`, - // `reserve()` will have no effect; if it does exceed its initial size, - // `reserve()` will trigger an initial allocation and move the inlined vector - // onto the heap. If the vector already exists on the heap and the requested - // size exceeds it, a reallocation will be performed. + // 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 @@ -573,26 +631,25 @@ class InlinedVector { } } - // InlinedVector::shrink_to_fit() + // `InlinedVector::shrink_to_fit()` // - // Reduces memory usage by freeing unused memory. - // After this call `capacity()` will be equal to `max(N, size())`. + // Reduces memory usage by freeing unused memory. After this call, calls to + // `capacity()` will be equal to `(std::max)(inlined_capacity(), size())`. // - // If `size() <= N` and the elements are currently stored on the heap, they - // will be moved to the inlined storage and the heap memory deallocated. - // If `size() > N` and `size() < capacity()` the elements will be moved to - // a reallocated storage on heap. + // 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 + // will be deallocated. + // + // If `size() > inlined_capacity()` and `size() < capacity()` the elements + // will be moved to a smaller heap allocation. void shrink_to_fit() { const auto s = size(); - if (!allocated() || s == capacity()) { - // There's nothing to deallocate. - return; - } + if (ABSL_PREDICT_FALSE(!allocated() || s == capacity())) return; - if (s <= N) { + 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. + // 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())); @@ -600,8 +657,8 @@ class InlinedVector { } // 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. + // 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), @@ -609,118 +666,126 @@ class InlinedVector { ResetAllocation(new_allocation, s); } - // InlinedVector::swap() + // `InlinedVector::swap()` // // Swaps the contents of this inlined vector with the contents of `other`. void swap(InlinedVector& other); - // InlinedVector::get_allocator() - // - // Returns the allocator of this inlined vector. - allocator_type get_allocator() const { return allocator(); } + 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: - static_assert(N > 0, "inlined vector with nonpositive size"); - - // It holds whether the vector is allocated or not in the lowest bit. - // The size is held in the high bits: - // size_ = (size << 1) | is_allocated; + // 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_ >> 1; } - void add_size(size_type n) { size_ += n << 1; } - void set_inline_size(size_type n) { size_ = n << 1; } - void set_allocated_size(size_type n) { size_ = (n << 1) | 1; } - bool allocated() const { return size_ & 1; } + 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 of it for free. + // 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, Tag t = Tag()) - : allocator_type(a), tag_(t) { - } + 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, // NOLINT(runtime/references) - size_type capacity) - : capacity_(capacity), - buffer_(AllocatorTraits::allocate(a, capacity_)) {} + Allocation(allocator_type& a, size_type capacity) + : capacity_(capacity), buffer_(Create(a, capacity)) {} - void Dealloc(allocator_type& a) { // NOLINT(runtime/references) - AllocatorTraits::deallocate(a, buffer(), capacity()); + void Dealloc(allocator_type& a) { + std::allocator_traits::deallocate(a, buffer_, capacity_); } size_type capacity() const { return capacity_; } - const value_type* buffer() const { return buffer_; } - value_type* buffer() { return buffer_; } + + 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); + } + size_type capacity_; - value_type* buffer_; + 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); } - value_type* inlined_space() { - return reinterpret_cast(&rep_.inlined_storage.inlined); - } - const value_type* inlined_space() const { - return reinterpret_cast(&rep_.inlined_storage.inlined); + // TODO(absl-team): investigate whether the reinterpret_cast is appropriate. + pointer inlined_space() { + return reinterpret_cast( + std::addressof(rep_.inlined_storage.inlined[0])); } - value_type* allocated_space() { - return allocation().buffer(); - } - const value_type* allocated_space() const { - return allocation().buffer(); + 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(); - } + + 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. - // The size is not changed, and any newly added memory is not initialized. + // 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() n places to the right. + // 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 iterators pointing to the start of the previously-initialized + // 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). + // 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, @@ -740,13 +805,13 @@ class InlinedVector { } template - value_type& GrowAndEmplaceBack(Args&&... args) { + reference GrowAndEmplaceBack(Args&&... args) { assert(size() == capacity()); const size_type s = size(); Allocation new_allocation(allocator(), 2 * capacity()); - value_type& new_element = + reference new_element = Construct(new_allocation.buffer() + s, std::forward(args)...); UninitializedCopy(std::make_move_iterator(data()), std::make_move_iterator(data() + s), @@ -758,98 +823,91 @@ class InlinedVector { } void InitAssign(size_type n); - void InitAssign(size_type n, const value_type& t); + + void InitAssign(size_type n, const_reference v); template - value_type& Construct(pointer p, Args&&... args) { - AllocatorTraits::construct(allocator(), p, std::forward(args)...); + reference Construct(pointer p, Args&&... args) { + std::allocator_traits::construct( + allocator(), p, std::forward(args)...); return *p; } - template - void UninitializedCopy(Iter src, Iter src_last, value_type* dst) { + template + void UninitializedCopy(Iterator src, Iterator src_last, pointer dst) { for (; src != src_last; ++dst, ++src) Construct(dst, *src); } template - void UninitializedFill(value_type* dst, value_type* dst_last, - const Args&... args) { + void UninitializedFill(pointer dst, pointer dst_last, const Args&... args) { for (; dst != dst_last; ++dst) Construct(dst, args...); } - // Destroy [ptr, ptr_last) in place. - void Destroy(value_type* ptr, value_type* ptr_last); + // Destroy [`from`, `to`) in place. + void Destroy(pointer from, pointer to); - template - void AppendRange(Iter first, Iter last, std::input_iterator_tag) { - std::copy(first, last, std::back_inserter(*this)); - } + template + void AppendRange(Iterator first, Iterator last, std::forward_iterator_tag); - // Faster path for forward iterators. - template - void AppendRange(Iter first, Iter last, std::forward_iterator_tag); + template + void AppendRange(Iterator first, Iterator last, std::input_iterator_tag); - template - void AppendRange(Iter first, Iter last) { - using IterTag = typename std::iterator_traits::iterator_category; - AppendRange(first, last, IterTag()); - } + template + void AssignRange(Iterator first, Iterator last, std::forward_iterator_tag); - template - void AssignRange(Iter first, Iter last, std::input_iterator_tag); - - // Faster path for forward iterators. - template - void AssignRange(Iter first, Iter last, std::forward_iterator_tag); - - template - void AssignRange(Iter first, Iter last) { - using IterTag = typename std::iterator_traits::iterator_category; - AssignRange(first, last, IterTag()); - } + template + void AssignRange(Iterator first, Iterator last, std::input_iterator_tag); iterator InsertWithCount(const_iterator position, size_type n, - const value_type& v); + const_reference v); - template - iterator InsertWithRange(const_iterator position, InputIter first, - InputIter last, std::input_iterator_tag); - template - iterator InsertWithRange(const_iterator position, ForwardIter first, - ForwardIter last, std::forward_iterator_tag); + template + iterator InsertWithRange(const_iterator position, ForwardIterator first, + ForwardIterator last, std::forward_iterator_tag); - AllocatorAndTag allocator_and_tag_; + template + iterator InsertWithRange(const_iterator position, InputIterator first, + InputIterator last, std::input_iterator_tag); - // Either the inlined or allocated representation + // Stores either the inlined or allocated representation union Rep { - // Use struct to perform indirection that solves a bizarre compilation - // error on Visual Studio (all known versions). - struct { - typename std::aligned_storage::type inlined[N]; - } inlined_storage; - struct { - typename std::aligned_storage::type allocation; - } allocation_storage; - } 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_; }; // ----------------------------------------------------------------------------- // InlinedVector Non-Member Functions // ----------------------------------------------------------------------------- -// swap() +// `swap()` // // Swaps the contents of two inlined vectors. This convenience function -// simply calls InlinedVector::swap(other_inlined_vector). +// simply calls `InlinedVector::swap()`. template void swap(InlinedVector& a, InlinedVector& b) noexcept(noexcept(a.swap(b))) { a.swap(b); } -// operator==() +// `operator==()` // // Tests the equivalency of the contents of two inlined vectors. template @@ -858,7 +916,7 @@ bool operator==(const InlinedVector& a, return absl::equal(a.begin(), a.end(), b.begin(), b.end()); } -// operator!=() +// `operator!=()` // // Tests the inequality of the contents of two inlined vectors. template @@ -867,7 +925,7 @@ bool operator!=(const InlinedVector& a, 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. @@ -877,7 +935,7 @@ bool operator<(const InlinedVector& a, return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); } -// operator>() +// `operator>()` // // Tests whether the contents of one inlined vector are greater than the // contents of another through a lexicographical comparison operation. @@ -887,7 +945,7 @@ bool operator>(const InlinedVector& a, 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. @@ -897,7 +955,7 @@ bool operator<=(const InlinedVector& a, 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. @@ -909,97 +967,99 @@ bool operator>=(const InlinedVector& a, // ----------------------------------------------------------------------------- // Implementation of InlinedVector -// ----------------------------------------------------------------------------- // -// Do not depend on any implementation details below this line. +// Do not depend on any below implementation details! +// ----------------------------------------------------------------------------- template -InlinedVector::InlinedVector(const InlinedVector& v) - : allocator_and_tag_(v.allocator()) { - reserve(v.size()); +InlinedVector::InlinedVector(const InlinedVector& other) + : allocator_and_tag_(other.allocator()) { + reserve(other.size()); if (allocated()) { - UninitializedCopy(v.begin(), v.end(), allocated_space()); - tag().set_allocated_size(v.size()); + UninitializedCopy(other.begin(), other.end(), allocated_space()); + tag().set_allocated_size(other.size()); } else { - UninitializedCopy(v.begin(), v.end(), inlined_space()); - tag().set_inline_size(v.size()); + UninitializedCopy(other.begin(), other.end(), inlined_space()); + tag().set_inline_size(other.size()); } } template -InlinedVector::InlinedVector(const InlinedVector& v, +InlinedVector::InlinedVector(const InlinedVector& other, const allocator_type& alloc) : allocator_and_tag_(alloc) { - reserve(v.size()); + reserve(other.size()); if (allocated()) { - UninitializedCopy(v.begin(), v.end(), allocated_space()); - tag().set_allocated_size(v.size()); + UninitializedCopy(other.begin(), other.end(), allocated_space()); + tag().set_allocated_size(other.size()); } else { - UninitializedCopy(v.begin(), v.end(), inlined_space()); - tag().set_inline_size(v.size()); + UninitializedCopy(other.begin(), other.end(), inlined_space()); + tag().set_inline_size(other.size()); } } template -InlinedVector::InlinedVector(InlinedVector&& v) noexcept( +InlinedVector::InlinedVector(InlinedVector&& other) noexcept( absl::allocator_is_nothrow::value || std::is_nothrow_move_constructible::value) - : allocator_and_tag_(v.allocator_and_tag_) { - if (v.allocated()) { + : 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(v.allocation()); - v.tag() = Tag(); + init_allocation(other.allocation()); + other.tag() = Tag(); } else { - UninitializedCopy(std::make_move_iterator(v.inlined_space()), - std::make_move_iterator(v.inlined_space() + v.size()), - inlined_space()); + UninitializedCopy( + std::make_move_iterator(other.inlined_space()), + std::make_move_iterator(other.inlined_space() + other.size()), + inlined_space()); } } template -InlinedVector::InlinedVector( - InlinedVector&& v, - const allocator_type& - alloc) noexcept(absl::allocator_is_nothrow::value) +InlinedVector::InlinedVector(InlinedVector&& other, + const allocator_type& alloc) noexcept( // + absl::allocator_is_nothrow::value) : allocator_and_tag_(alloc) { - if (v.allocated()) { - if (alloc == v.allocator()) { + if (other.allocated()) { + if (alloc == other.allocator()) { // We can just steal the allocation from the source. - tag() = v.tag(); - init_allocation(v.allocation()); - v.tag() = Tag(); + tag() = other.tag(); + init_allocation(other.allocation()); + other.tag() = Tag(); } else { // We need to use our own allocator - reserve(v.size()); - UninitializedCopy(std::make_move_iterator(v.begin()), - std::make_move_iterator(v.end()), allocated_space()); - tag().set_allocated_size(v.size()); + 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(v.inlined_space()), - std::make_move_iterator(v.inlined_space() + v.size()), - inlined_space()); - tag().set_inline_size(v.size()); + 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 value_type& t) { - if (n > static_cast(N)) { +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, t); + UninitializedFill(allocated_space(), allocated_space() + n, v); tag().set_allocated_size(n); } else { - UninitializedFill(inlined_space(), inlined_space() + n, t); + UninitializedFill(inlined_space(), inlined_space() + n, v); tag().set_inline_size(n); } } template void InlinedVector::InitAssign(size_type n) { - if (n > static_cast(N)) { + if (n > inlined_capacity()) { Allocation new_allocation(allocator(), n); init_allocation(new_allocation); UninitializedFill(allocated_space(), allocated_space() + n); @@ -1031,7 +1091,7 @@ void InlinedVector::resize(size_type n) { } template -void InlinedVector::resize(size_type n, const value_type& elem) { +void InlinedVector::resize(size_type n, const_reference v) { size_type s = size(); if (n < s) { erase(begin() + n, end()); @@ -1040,23 +1100,23 @@ void InlinedVector::resize(size_type n, const value_type& elem) { reserve(n); assert(capacity() >= n); - // Fill new space with copies of 'elem'. + // Fill new space with copies of 'v'. if (allocated()) { - UninitializedFill(allocated_space() + s, allocated_space() + n, elem); + UninitializedFill(allocated_space() + s, allocated_space() + n, v); tag().set_allocated_size(n); } else { - UninitializedFill(inlined_space() + s, inlined_space() + n, elem); + UninitializedFill(inlined_space() + s, inlined_space() + n, v); tag().set_inline_size(n); } } template template -typename InlinedVector::iterator InlinedVector::emplace( - const_iterator position, Args&&... args) { +auto InlinedVector::emplace(const_iterator position, Args&&... args) + -> iterator { assert(position >= begin()); assert(position <= end()); - if (position == end()) { + if (ABSL_PREDICT_FALSE(position == end())) { emplace_back(std::forward(args)...); return end() - 1; } @@ -1076,14 +1136,14 @@ typename InlinedVector::iterator InlinedVector::emplace( } template -typename InlinedVector::iterator InlinedVector::erase( - const_iterator first, const_iterator last) { - assert(begin() <= first); - assert(first <= last); - assert(last <= end()); +auto InlinedVector::erase(const_iterator from, const_iterator to) + -> iterator { + assert(begin() <= from); + assert(from <= to); + assert(to <= end()); - iterator range_start = const_cast(first); - iterator range_end = const_cast(last); + 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); @@ -1104,10 +1164,9 @@ typename InlinedVector::iterator InlinedVector::erase( template void InlinedVector::swap(InlinedVector& other) { - using std::swap; // Augment ADL with std::swap. - if (&other == this) { - return; - } + 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()); @@ -1126,12 +1185,12 @@ void InlinedVector::swap(InlinedVector& other) { 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, + // `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: A[b_size,a_size) -> B[b_size,a_size) + // 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); @@ -1143,6 +1202,7 @@ void InlinedVector::swap(InlinedVector& other) { 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 @@ -1157,13 +1217,13 @@ void InlinedVector::swap(InlinedVector& other) { 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. - (void)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 + // 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. + // 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, @@ -1185,7 +1245,7 @@ void InlinedVector::EnlargeBy(size_type delta) { const size_type s = size(); assert(s <= capacity()); - size_type target = std::max(static_cast(N), s + delta); + size_type target = std::max(inlined_capacity(), s + delta); // Compute new capacity by repeatedly doubling current capacity // TODO(psrc): Check and avoid overflow? @@ -1217,7 +1277,7 @@ auto InlinedVector::ShiftRight(const_iterator position, size_type n) while (new_capacity < required_size) { new_capacity <<= 1; } - // Move everyone into the new allocation, leaving a gap of n for the + // 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(); @@ -1235,8 +1295,8 @@ auto InlinedVector::ShiftRight(const_iterator position, size_type n) 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 move. + // 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; @@ -1262,28 +1322,26 @@ auto InlinedVector::ShiftRight(const_iterator position, size_type n) } template -void InlinedVector::Destroy(value_type* ptr, value_type* ptr_last) { - for (value_type* p = ptr; p != ptr_last; ++p) { - AllocatorTraits::destroy(allocator(), p); +void InlinedVector::Destroy(pointer from, pointer to) { + for (pointer cur = from; cur != to; ++cur) { + std::allocator_traits::destroy(allocator(), cur); } - - // 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. #ifndef NDEBUG - if (ptr != ptr_last) { - memset(reinterpret_cast(ptr), 0xab, - sizeof(*ptr) * (ptr_last - ptr)); + // 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(Iter first, Iter last, +template +void InlinedVector::AppendRange(Iterator first, Iterator last, std::forward_iterator_tag) { - using Length = typename std::iterator_traits::difference_type; - Length length = std::distance(first, last); + auto length = std::distance(first, last); reserve(size() + length); if (allocated()) { UninitializedCopy(first, last, allocated_space() + size()); @@ -1295,24 +1353,17 @@ void InlinedVector::AppendRange(Iter first, Iter last, } template -template -void InlinedVector::AssignRange(Iter first, Iter last, +template +void InlinedVector::AppendRange(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 -template -void InlinedVector::AssignRange(Iter first, Iter last, +template +void InlinedVector::AssignRange(Iterator first, Iterator last, std::forward_iterator_tag) { - using Length = typename std::iterator_traits::difference_type; - Length length = std::distance(first, last); + 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()); @@ -1330,12 +1381,26 @@ void InlinedVector::AssignRange(Iter first, Iter last, } } +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 value_type& v) + size_type n, const_reference v) -> iterator { assert(position >= begin() && position <= end()); - if (n == 0) return const_cast(position); + if (ABSL_PREDICT_FALSE(n == 0)) return const_cast(position); value_type copy = v; std::pair it_pair = ShiftRight(position, n); @@ -1346,41 +1411,39 @@ auto InlinedVector::InsertWithCount(const_iterator position, } template -template +template auto InlinedVector::InsertWithRange(const_iterator position, - InputIter first, InputIter 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; -} - -// Overload of InlinedVector::InsertWithRange() -template -template -auto InlinedVector::InsertWithRange(const_iterator position, - ForwardIter first, - ForwardIter last, + ForwardIterator first, + ForwardIterator last, std::forward_iterator_tag) -> iterator { assert(position >= begin() && position <= end()); - if (first == last) { - return const_cast(position); - } - using Length = typename std::iterator_traits::difference_type; - Length n = std::distance(first, last); + 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; - ForwardIter open_spot = std::next(first, used_spots); + 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; } -} // inline namespace lts_2018_06_20 +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; +} + +} // inline namespace lts_2018_12_18 } // 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 24f2174928a..a3ad0f8ae4f 100644 --- a/absl/container/inlined_vector_benchmark.cc +++ b/absl/container/inlined_vector_benchmark.cc @@ -66,7 +66,7 @@ BENCHMARK(BM_StdVectorFill)->Range(0, 1024); // The purpose of the next two benchmarks is to verify that // absl::InlinedVector is efficient when moving is more efficent than // copying. To do so, we use strings that are larger than the short -// std::string optimization. +// string optimization. bool StringRepresentedInline(std::string s) { const char* chars = s.data(); std::string s1 = std::move(s); diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index 26a7d5bca86..3a1ea8ac1de 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc @@ -31,6 +31,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/container/internal/test_instance_tracker.h" +#include "absl/hash/hash_testing.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" @@ -905,6 +906,8 @@ TYPED_TEST_P(InstanceTest, Swap) { InstanceTracker tracker; InstanceVec a, b; const size_t inlined_capacity = a.capacity(); + 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)); EXPECT_EQ(tracker.instances(), l1 + l2); @@ -918,15 +921,15 @@ TYPED_TEST_P(InstanceTest, Swap) { EXPECT_EQ(tracker.swaps(), 0); // Allocations are swapped. EXPECT_EQ(tracker.moves(), 0); } else if (a.size() <= inlined_capacity && b.size() <= inlined_capacity) { - EXPECT_EQ(tracker.swaps(), std::min(l1, l2)); - // TODO(bsamwel): This should use moves when the type is movable. - EXPECT_EQ(tracker.copies(), std::max(l1, l2) - std::min(l1, l2)); + EXPECT_EQ(tracker.swaps(), min_len); + EXPECT_EQ((tracker.moves() ? tracker.moves() : tracker.copies()), + max_len - min_len); } else { // One is allocated and the other isn't. The allocation is transferred // without copying elements, and the inlined instances are copied/moved. EXPECT_EQ(tracker.swaps(), 0); - // TODO(bsamwel): This should use moves when the type is movable. - EXPECT_EQ(tracker.copies(), std::min(l1, l2)); + EXPECT_EQ((tracker.moves() ? tracker.moves() : tracker.copies()), + min_len); } EXPECT_EQ(l1, b.size()); @@ -1725,42 +1728,87 @@ TEST(AllocatorSupportTest, ScopedAllocatorWorks) { 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; + int64_t allocated = 0; AllocVec vec(MyAlloc{CountingAllocator{&allocated}}); EXPECT_EQ(allocated, 0); // This default constructs a vector, but the allocator should pass itself - // into the vector. + // into the vector, so check allocation compared to that. // The absl::InlinedVector does not allocate any memory. - // The vector does not allocate any memory. + // The vector may allocate any memory. + auto expected = default_std_vec_allocated; vec.resize(1); - EXPECT_EQ(allocated, 0); + EXPECT_EQ(allocated, expected); // We make vector allocate memory. // It must go through the allocator even though we didn't construct the - // vector directly. + // 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, sizeof(int) * 1); + EXPECT_EQ(allocated, expected); // Another allocating vector. + expected += one_element_std_vec_copy_allocated; vec.push_back(vec[0]); - EXPECT_EQ(allocated, sizeof(int) * 2); + 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, sizeof(int) * 2 + sizeof(StdVector) * 8); + 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, sizeof(int) * 3 + sizeof(StdVector) * 8); + EXPECT_EQ(allocated, expected); - // And extending these should still work. + // 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, sizeof(int) * 4 + sizeof(StdVector) * 8); + EXPECT_EQ(allocated, expected); vec.clear(); EXPECT_EQ(allocated, 0); } +TEST(AllocatorSupportTest, SizeAllocConstructor) { + constexpr int inlined_size = 4; + using Alloc = CountingAllocator; + using AllocVec = absl::InlinedVector; + + { + auto len = inlined_size / 2; + int64_t allocated = 0; + auto v = AllocVec(len, Alloc(&allocated)); + + // Inline storage used; allocator should not be invoked + EXPECT_THAT(allocated, 0); + EXPECT_THAT(v, AllOf(SizeIs(len), Each(0))); + } + + { + auto len = inlined_size * 2; + int64_t allocated = 0; + auto v = AllocVec(len, Alloc(&allocated)); + + // Out of line storage used; allocation of 8 elements expected + EXPECT_THAT(allocated, len * sizeof(int)); + EXPECT_THAT(v, AllOf(SizeIs(len), Each(0))); + } +} + } // anonymous namespace diff --git a/absl/container/internal/compressed_tuple.h b/absl/container/internal/compressed_tuple.h new file mode 100644 index 00000000000..29fe7c12ca3 --- /dev/null +++ b/absl/container/internal/compressed_tuple.h @@ -0,0 +1,177 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Helper class to perform the Empty Base Optimization. +// Ts can contain classes and non-classes, empty or not. For the ones that +// are empty classes, we perform the optimization. If all types in Ts are empty +// classes, then CompressedTuple is itself an empty class. +// +// To access the members, use member get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// http://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 "absl/utility/utility.h" + +#ifdef _MSC_VER +// 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 +#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC +#endif // _MSC_VER + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { + +template +class CompressedTuple; + +namespace internal_compressed_tuple { + +template +struct Elem; +template +struct Elem, I> + : std::tuple_element> {}; +template +using ElemT = typename Elem::type; + +// Use the __is_final intrinsic if available. Where it's not available, classes +// declared with the 'final' specifier cannot be used as CompressedTuple +// elements. +// TODO(sbenza): Replace this with std::is_final in C++14. +template +constexpr bool IsFinal() { +#if defined(__clang__) || defined(__GNUC__) + return __is_final(T); +#else + return false; +#endif +} + +template +constexpr bool ShouldUseBase() { + return std::is_class::value && std::is_empty::value && !IsFinal(); +} + +// 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 >()> +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 +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage + : ElemT { + using T = internal_compressed_tuple::ElemT; + constexpr Storage() = default; + explicit constexpr Storage(T&& v) : T(absl::forward(v)) {} + constexpr const T& get() const { return *this; } + T& get() { return *this; } +}; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl; + +template +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC + CompressedTupleImpl, absl::index_sequence> + // 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>... { + constexpr CompressedTupleImpl() = default; + explicit constexpr CompressedTupleImpl(Ts&&... args) + : Storage, I>(absl::forward(args))... {} +}; + +} // 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. +// +// To access the members, use member .get() function. +// +// Eg: +// absl::container_internal::CompressedTuple value(7, t1, t2, +// t3); +// assert(value.get<0>() == 7); +// T1& t1 = value.get<1>(); +// const T2& t2 = value.get<2>(); +// ... +// +// http://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> { + private: + template + using ElemT = internal_compressed_tuple::ElemT; + + public: + constexpr CompressedTuple() = default; + explicit constexpr CompressedTuple(Ts... base) + : CompressedTuple::CompressedTupleImpl(absl::forward(base)...) {} + + template + ElemT& get() { + return internal_compressed_tuple::Storage::get(); + } + + template + constexpr const ElemT& get() const { + return internal_compressed_tuple::Storage::get(); + } +}; + +// Explicit specialization for a zero-element tuple +// (needed to avoid ambiguous overloads for the default constructor). +template <> +class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {}; + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl + +#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC + +#endif // ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ diff --git a/absl/container/internal/compressed_tuple_test.cc b/absl/container/internal/compressed_tuple_test.cc new file mode 100644 index 00000000000..2b5ed4a4abc --- /dev/null +++ b/absl/container/internal/compressed_tuple_test.cc @@ -0,0 +1,168 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/compressed_tuple.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +template +struct Empty {}; + +template +struct NotEmpty { + T value; +}; + +template +struct TwoValues { + T value1; + U value2; +}; + +TEST(CompressedTupleTest, Sizeof) { + EXPECT_EQ(sizeof(int), sizeof(CompressedTuple)); + EXPECT_EQ(sizeof(int), sizeof(CompressedTuple>)); + EXPECT_EQ(sizeof(int), sizeof(CompressedTuple, Empty<1>>)); + EXPECT_EQ(sizeof(int), + sizeof(CompressedTuple, Empty<1>, Empty<2>>)); + + EXPECT_EQ(sizeof(TwoValues), + sizeof(CompressedTuple>)); + EXPECT_EQ(sizeof(TwoValues), + sizeof(CompressedTuple, NotEmpty>)); + EXPECT_EQ(sizeof(TwoValues), + sizeof(CompressedTuple, NotEmpty, Empty<1>>)); +} + +TEST(CompressedTupleTest, Access) { + struct S { + std::string x; + }; + CompressedTuple, S> x(7, {}, S{"ABC"}); + EXPECT_EQ(sizeof(x), sizeof(TwoValues)); + EXPECT_EQ(7, x.get<0>()); + EXPECT_EQ("ABC", x.get<2>().x); +} + +TEST(CompressedTupleTest, NonClasses) { + CompressedTuple x(7, "ABC"); + EXPECT_EQ(7, x.get<0>()); + EXPECT_STREQ("ABC", x.get<1>()); +} + +TEST(CompressedTupleTest, MixClassAndNonClass) { + CompressedTuple, NotEmpty> x(7, "ABC", {}, + {1.25}); + struct Mock { + int v; + const char* p; + double d; + }; + EXPECT_EQ(sizeof(x), sizeof(Mock)); + EXPECT_EQ(7, x.get<0>()); + EXPECT_STREQ("ABC", x.get<1>()); + EXPECT_EQ(1.25, x.get<3>().value); +} + +TEST(CompressedTupleTest, Nested) { + CompressedTuple, + CompressedTuple>> + x(1, CompressedTuple(2), + CompressedTuple>(3, CompressedTuple(4))); + EXPECT_EQ(1, x.get<0>()); + EXPECT_EQ(2, x.get<1>().get<0>()); + EXPECT_EQ(3, x.get<2>().get<0>()); + EXPECT_EQ(4, x.get<2>().get<1>().get<0>()); + + CompressedTuple, Empty<0>, + CompressedTuple, CompressedTuple>>> + y; + std::set*> empties{&y.get<0>(), &y.get<1>(), &y.get<2>().get<0>(), + &y.get<2>().get<1>().get<0>()}; +#ifdef _MSC_VER + // MSVC has a bug where many instances of the same base class are layed out in + // the same address when using __declspec(empty_bases). + // This will be fixed in a future version of MSVC. + int expected = 1; +#else + int expected = 4; +#endif + EXPECT_EQ(expected, sizeof(y)); + EXPECT_EQ(expected, empties.size()); + EXPECT_EQ(sizeof(y), sizeof(Empty<0>) * empties.size()); + + EXPECT_EQ(4 * sizeof(char), + sizeof(CompressedTuple, + CompressedTuple>)); + EXPECT_TRUE( + (std::is_empty>, + CompressedTuple>>>::value)); +} + +TEST(CompressedTupleTest, Reference) { + int i = 7; + std::string s = "Very long std::string that goes in the heap"; + CompressedTuple x(i, i, s, s); + + // Sanity check. We should have not moved from `s` + EXPECT_EQ(s, "Very long std::string that goes in the heap"); + + EXPECT_EQ(x.get<0>(), x.get<1>()); + EXPECT_NE(&x.get<0>(), &x.get<1>()); + EXPECT_EQ(&x.get<1>(), &i); + + EXPECT_EQ(x.get<2>(), x.get<3>()); + EXPECT_NE(&x.get<2>(), &x.get<3>()); + EXPECT_EQ(&x.get<3>(), &s); +} + +TEST(CompressedTupleTest, NoElements) { + CompressedTuple<> x; + static_cast(x); // Silence -Wunused-variable. + EXPECT_TRUE(std::is_empty>::value); +} + +TEST(CompressedTupleTest, Constexpr) { + constexpr CompressedTuple> 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>(); + EXPECT_EQ(x0, 7); + EXPECT_EQ(x1, 1.25); + EXPECT_EQ(x2, 5); +} + +#if defined(__clang__) || defined(__GNUC__) +TEST(CompressedTupleTest, EmptyFinalClass) { + struct S final { + int f() const { return 5; } + }; + CompressedTuple x; + EXPECT_EQ(x.get<0>().f(), 5); +} +#endif + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h new file mode 100644 index 00000000000..ddccbe05eb1 --- /dev/null +++ b/absl/container/internal/container_memory.h @@ -0,0 +1,407 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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_MEMORY_H_ +#define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ + +#ifdef ADDRESS_SANITIZER +#include +#endif + +#ifdef MEMORY_SANITIZER +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/utility/utility.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { + +// Allocates at least n bytes aligned to the specified alignment. +// Alignment must be a power of 2. It must be positive. +// +// Note that many allocators don't honor alignment requirements above certain +// threshold (usually either alignof(std::max_align_t) or alignof(void*)). +// Allocate() doesn't apply alignment corrections. If the underlying allocator +// returns insufficiently alignment pointer, that's what you are going to get. +template +void* Allocate(Alloc* alloc, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + struct alignas(Alignment) M {}; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + A mem_alloc(*alloc); + void* p = AT::allocate(mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); + assert(reinterpret_cast(p) % Alignment == 0 && + "allocator does not respect alignment"); + return p; +} + +// The pointer must have been previously obtained by calling +// Allocate(alloc, n). +template +void Deallocate(Alloc* alloc, void* p, size_t n) { + static_assert(Alignment > 0, ""); + assert(n && "n must be positive"); + struct alignas(Alignment) M {}; + using A = typename absl::allocator_traits::template rebind_alloc; + using AT = typename absl::allocator_traits::template rebind_traits; + A mem_alloc(*alloc); + AT::deallocate(mem_alloc, static_cast(p), + (n + sizeof(M) - 1) / sizeof(M)); +} + +namespace memory_internal { + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +template +void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t, + absl::index_sequence) { + absl::allocator_traits::construct( + *alloc, ptr, std::get(std::forward(t))...); +} + +template +struct WithConstructedImplF { + template + decltype(std::declval()(std::declval())) operator()( + Args&&... args) const { + return std::forward(f)(T(std::forward(args)...)); + } + F&& f; +}; + +template +decltype(std::declval()(std::declval())) WithConstructedImpl( + Tuple&& t, absl::index_sequence, F&& f) { + return WithConstructedImplF{std::forward(f)}( + std::get(std::forward(t))...); +} + +template +auto TupleRefImpl(T&& t, absl::index_sequence) + -> decltype(std::forward_as_tuple(std::get(std::forward(t))...)) { + return std::forward_as_tuple(std::get(std::forward(t))...); +} + +// Returns a tuple of references to the elements of the input tuple. T must be a +// tuple. +template +auto TupleRef(T&& t) -> decltype( + TupleRefImpl(std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>())) { + return TupleRefImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +template +decltype(std::declval()(std::declval(), std::piecewise_construct, + std::declval>(), std::declval())) +DecomposePairImpl(F&& f, std::pair, V> p) { + const auto& key = std::get<0>(p.first); + return std::forward(f)(key, std::piecewise_construct, std::move(p.first), + std::move(p.second)); +} + +} // namespace memory_internal + +// Constructs T into uninitialized storage pointed by `ptr` using the args +// specified in the tuple. +template +void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) { + memory_internal::ConstructFromTupleImpl( + alloc, ptr, std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>()); +} + +// Constructs T using the args specified in the tuple and calls F with the +// constructed value. +template +decltype(std::declval()(std::declval())) WithConstructed( + Tuple&& t, F&& f) { + return memory_internal::WithConstructedImpl( + std::forward(t), + absl::make_index_sequence< + std::tuple_size::type>::value>(), + std::forward(f)); +} + +// Given arguments of an std::pair's consructor, PairArgs() returns a pair of +// tuples with references to the passed arguments. The tuples contain +// constructor arguments for the first and the second elements of the pair. +// +// The following two snippets are equivalent. +// +// 1. std::pair p(args...); +// +// 2. auto a = PairArgs(args...); +// std::pair p(std::piecewise_construct, +// std::move(p.first), std::move(p.second)); +inline std::pair, std::tuple<>> PairArgs() { return {}; } +template +std::pair, std::tuple> PairArgs(F&& f, S&& s) { + return {std::piecewise_construct, std::forward_as_tuple(std::forward(f)), + std::forward_as_tuple(std::forward(s))}; +} +template +std::pair, std::tuple> PairArgs( + const std::pair& p) { + return PairArgs(p.first, p.second); +} +template +std::pair, std::tuple> PairArgs(std::pair&& p) { + return PairArgs(std::forward(p.first), std::forward(p.second)); +} +template +auto PairArgs(std::piecewise_construct_t, F&& f, S&& s) + -> decltype(std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s)))) { + return std::make_pair(memory_internal::TupleRef(std::forward(f)), + memory_internal::TupleRef(std::forward(s))); +} + +// A helper function for implementing apply() in map policies. +template +auto DecomposePair(F&& f, Args&&... args) + -> decltype(memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...))) { + return memory_internal::DecomposePairImpl( + std::forward(f), PairArgs(std::forward(args)...)); +} + +// A helper function for implementing apply() in set policies. +template +decltype(std::declval()(std::declval(), std::declval())) +DecomposeValue(F&& f, Arg&& arg) { + const auto& key = arg; + return std::forward(f)(key, std::forward(arg)); +} + +// Helper functions for asan and msan. +inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) { +#ifdef ADDRESS_SANITIZER + ASAN_POISON_MEMORY_REGION(m, s); +#endif +#ifdef MEMORY_SANITIZER + __msan_poison(m, s); +#endif + (void)m; + (void)s; +} + +inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) { +#ifdef ADDRESS_SANITIZER + ASAN_UNPOISON_MEMORY_REGION(m, s); +#endif +#ifdef MEMORY_SANITIZER + __msan_unpoison(m, s); +#endif + (void)m; + (void)s; +} + +template +inline void SanitizerPoisonObject(const T* object) { + SanitizerPoisonMemoryRegion(object, sizeof(T)); +} + +template +inline void SanitizerUnpoisonObject(const T* object) { + SanitizerUnpoisonMemoryRegion(object, sizeof(T)); +} + +namespace memory_internal { + +// If Pair is a standard-layout type, OffsetOf::kFirst and +// OffsetOf::kSecond are equivalent to offsetof(Pair, first) and +// offsetof(Pair, second) respectively. Otherwise they are -1. +// +// The purpose of OffsetOf is to avoid calling offsetof() on non-standard-layout +// type, which is non-portable. +template +struct OffsetOf { + static constexpr size_t kFirst = -1; + static constexpr size_t kSecond = -1; +}; + +template +struct OffsetOf::type> { + static constexpr size_t kFirst = offsetof(Pair, first); + static constexpr size_t kSecond = offsetof(Pair, second); +}; + +template +struct IsLayoutCompatible { + private: + struct Pair { + K first; + V second; + }; + + // Is P layout-compatible with Pair? + template + static constexpr bool LayoutCompatible() { + return std::is_standard_layout

() && sizeof(P) == sizeof(Pair) && + alignof(P) == alignof(Pair) && + memory_internal::OffsetOf

::kFirst == + memory_internal::OffsetOf::kFirst && + memory_internal::OffsetOf

::kSecond == + memory_internal::OffsetOf::kSecond; + } + + public: + // Whether pair and pair are layout-compatible. If they are, + // then it is safe to store them in a union and read from either. + static constexpr bool value = std::is_standard_layout() && + std::is_standard_layout() && + memory_internal::OffsetOf::kFirst == 0 && + LayoutCompatible>() && + LayoutCompatible>(); +}; + +} // namespace memory_internal + +// If kMutableKeys is false, only the value member is accessed. +// +// 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. +template +union slot_type { + private: + static void emplace(slot_type* slot) { + // The construction of union doesn't do anything at runtime but it allows us + // to access its members without violating aliasing rules. + new (slot) 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>; + + public: + slot_type() {} + ~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 + static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct(*alloc, &slot->mutable_value, + std::forward(args)...); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::forward(args)...); + } + } + + // Construct this slot by moving from another slot. + template + static void construct(Allocator* alloc, slot_type* slot, slot_type* other) { + emplace(slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &slot->mutable_value, std::move(other->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &slot->value, + std::move(other->value)); + } + } + + template + static void destroy(Allocator* alloc, slot_type* slot) { + if (kMutableKeys::value) { + absl::allocator_traits::destroy(*alloc, &slot->mutable_value); + } else { + absl::allocator_traits::destroy(*alloc, &slot->value); + } + } + + template + static void transfer(Allocator* alloc, slot_type* new_slot, + slot_type* old_slot) { + emplace(new_slot); + if (kMutableKeys::value) { + absl::allocator_traits::construct( + *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); + } else { + absl::allocator_traits::construct(*alloc, &new_slot->value, + std::move(old_slot->value)); + } + destroy(alloc, old_slot); + } + + template + static void swap(Allocator* alloc, slot_type* a, slot_type* b) { + if (kMutableKeys::value) { + using std::swap; + swap(a->mutable_value, b->mutable_value); + } else { + value_type tmp = std::move(a->value); + absl::allocator_traits::destroy(*alloc, &a->value); + absl::allocator_traits::construct(*alloc, &a->value, + std::move(b->value)); + absl::allocator_traits::destroy(*alloc, &b->value); + absl::allocator_traits::construct(*alloc, &b->value, + std::move(tmp)); + } + } + + template + static void move(Allocator* alloc, slot_type* src, slot_type* dest) { + if (kMutableKeys::value) { + dest->mutable_value = std::move(src->mutable_value); + } else { + absl::allocator_traits::destroy(*alloc, &dest->value); + absl::allocator_traits::construct(*alloc, &dest->value, + std::move(src->value)); + } + } + + template + static void move(Allocator* alloc, slot_type* first, slot_type* last, + slot_type* result) { + for (slot_type *src = first, *dest = result; src != last; ++src, ++dest) + move(alloc, src, dest); + } +}; + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..da87ca209d4 --- /dev/null +++ b/absl/container/internal/container_memory_test.cc @@ -0,0 +1,190 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/container_memory.h" + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +using ::testing::Pair; + +TEST(Memory, AlignmentLargerThanBase) { + std::allocator alloc; + void* mem = Allocate<2>(&alloc, 3); + EXPECT_EQ(0, reinterpret_cast(mem) % 2); + memcpy(mem, "abc", 3); + Deallocate<2>(&alloc, mem, 3); +} + +TEST(Memory, AlignmentSmallerThanBase) { + std::allocator alloc; + void* mem = Allocate<2>(&alloc, 3); + EXPECT_EQ(0, reinterpret_cast(mem) % 2); + memcpy(mem, "abc", 3); + Deallocate<2>(&alloc, mem, 3); +} + +class Fixture : public ::testing::Test { + using Alloc = std::allocator; + + public: + Fixture() { ptr_ = std::allocator_traits::allocate(*alloc(), 1); } + ~Fixture() override { + std::allocator_traits::destroy(*alloc(), ptr_); + std::allocator_traits::deallocate(*alloc(), ptr_, 1); + } + std::string* ptr() { return ptr_; } + Alloc* alloc() { return &alloc_; } + + private: + Alloc alloc_; + std::string* ptr_; +}; + +TEST_F(Fixture, ConstructNoArgs) { + ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple()); + EXPECT_EQ(*ptr(), ""); +} + +TEST_F(Fixture, ConstructOneArg) { + ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple("abcde")); + EXPECT_EQ(*ptr(), "abcde"); +} + +TEST_F(Fixture, ConstructTwoArg) { + ConstructFromTuple(alloc(), ptr(), std::forward_as_tuple(5, 'a')); + EXPECT_EQ(*ptr(), "aaaaa"); +} + +TEST(PairArgs, NoArgs) { + EXPECT_THAT(PairArgs(), + Pair(std::forward_as_tuple(), std::forward_as_tuple())); +} + +TEST(PairArgs, TwoArgs) { + EXPECT_EQ( + std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')), + PairArgs(1, 'A')); +} + +TEST(PairArgs, Pair) { + EXPECT_EQ( + std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')), + PairArgs(std::make_pair(1, 'A'))); +} + +TEST(PairArgs, Piecewise) { + EXPECT_EQ( + std::make_pair(std::forward_as_tuple(1), std::forward_as_tuple('A')), + PairArgs(std::piecewise_construct, std::forward_as_tuple(1), + std::forward_as_tuple('A'))); +} + +TEST(WithConstructed, Simple) { + EXPECT_EQ(1, WithConstructed( + std::make_tuple(std::string("a")), + [](absl::string_view str) { return str.size(); })); +} + +template +decltype(DecomposeValue(std::declval(), std::declval())) +DecomposeValueImpl(int, F&& f, Arg&& arg) { + return DecomposeValue(std::forward(f), std::forward(arg)); +} + +template +const char* DecomposeValueImpl(char, F&& f, Arg&& arg) { + return "not decomposable"; +} + +template +decltype(DecomposeValueImpl(0, std::declval(), std::declval())) +TryDecomposeValue(F&& f, Arg&& arg) { + return DecomposeValueImpl(0, std::forward(f), std::forward(arg)); +} + +TEST(DecomposeValue, Decomposable) { + auto f = [](const int& x, int&& y) { + EXPECT_EQ(&x, &y); + EXPECT_EQ(42, x); + return 'A'; + }; + EXPECT_EQ('A', TryDecomposeValue(f, 42)); +} + +TEST(DecomposeValue, NotDecomposable) { + auto f = [](void*) { + ADD_FAILURE() << "Must not be called"; + return 'A'; + }; + EXPECT_STREQ("not decomposable", TryDecomposeValue(f, 42)); +} + +template +decltype(DecomposePair(std::declval(), std::declval()...)) +DecomposePairImpl(int, F&& f, Args&&... args) { + return DecomposePair(std::forward(f), std::forward(args)...); +} + +template +const char* DecomposePairImpl(char, F&& f, Args&&... args) { + return "not decomposable"; +} + +template +decltype(DecomposePairImpl(0, std::declval(), std::declval()...)) +TryDecomposePair(F&& f, Args&&... args) { + return DecomposePairImpl(0, std::forward(f), std::forward(args)...); +} + +TEST(DecomposePair, Decomposable) { + auto f = [](const int& x, std::piecewise_construct_t, std::tuple k, + std::tuple&& v) { + EXPECT_EQ(&x, &std::get<0>(k)); + EXPECT_EQ(42, x); + EXPECT_EQ(0.5, std::get<0>(v)); + return 'A'; + }; + EXPECT_EQ('A', TryDecomposePair(f, 42, 0.5)); + EXPECT_EQ('A', TryDecomposePair(f, std::make_pair(42, 0.5))); + EXPECT_EQ('A', TryDecomposePair(f, std::piecewise_construct, + std::make_tuple(42), std::make_tuple(0.5))); +} + +TEST(DecomposePair, NotDecomposable) { + auto f = [](...) { + ADD_FAILURE() << "Must not be called"; + return 'A'; + }; + EXPECT_STREQ("not decomposable", + TryDecomposePair(f)); + EXPECT_STREQ("not decomposable", + TryDecomposePair(f, std::piecewise_construct, std::make_tuple(), + std::make_tuple(0.5))); +} + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/hash_function_defaults.h b/absl/container/internal/hash_function_defaults.h new file mode 100644 index 00000000000..72c75fa03e3 --- /dev/null +++ b/absl/container/internal/hash_function_defaults.h @@ -0,0 +1,145 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Define the default Hash and Eq functions for SwissTable containers. +// +// std::hash and std::equal_to are not appropriate hash and equal +// functions for SwissTable containers. There are two reasons for this. +// +// SwissTable containers are power of 2 sized containers: +// +// This means they use the lower bits of the hash value to find the slot for +// each entry. The typical hash function for integral types is the identity. +// This is a very weak hash function for SwissTable and any power of 2 sized +// hashtable implementation which will lead to excessive collisions. For +// SwissTable we use murmur3 style mixing to reduce collisions to a minimum. +// +// SwissTable containers support heterogeneous lookup: +// +// In order to make heterogeneous lookup work, hash and equal functions must be +// polymorphic. At the same time they have to satisfy the same requirements the +// C++ standard imposes on hash functions and equality operators. That is: +// +// if hash_default_eq(a, b) returns true for any a and b of type T, then +// hash_default_hash(a) must equal hash_default_hash(b) +// +// For SwissTable containers this requirement is relaxed to allow a and b of +// any and possibly different types. Note that like the standard the hash and +// 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. +// +#ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/hash/hash.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { + +// The hash of an object of type T is computed by using absl::Hash. +template +struct HashEq { + using Hash = absl::Hash; + using Eq = std::equal_to; +}; + +struct StringHash { + using is_transparent = void; + + size_t operator()(absl::string_view v) const { + return absl::Hash{}(v); + } +}; + +// Supports heterogeneous lookup for string-like elements. +struct StringHashEq { + using Hash = StringHash; + struct Eq { + using is_transparent = void; + bool operator()(absl::string_view lhs, absl::string_view rhs) const { + return lhs == rhs; + } + }; +}; +template <> +struct HashEq : StringHashEq {}; +template <> +struct HashEq : StringHashEq {}; + +// Supports heterogeneous lookup for pointers and smart pointers. +template +struct HashEq { + struct Hash { + using is_transparent = void; + template + size_t operator()(const U& ptr) const { + return absl::Hash{}(HashEq::ToPtr(ptr)); + } + }; + struct Eq { + using is_transparent = void; + template + bool operator()(const A& a, const B& b) const { + return HashEq::ToPtr(a) == HashEq::ToPtr(b); + } + }; + + private: + static const T* ToPtr(const T* ptr) { return ptr; } + template + static const T* ToPtr(const std::unique_ptr& ptr) { + return ptr.get(); + } + template + static const T* ToPtr(const std::shared_ptr& ptr) { + return ptr.get(); + } +}; + +template +struct HashEq> : HashEq {}; +template +struct HashEq> : HashEq {}; + +// This header's visibility is restricted. If you need to access the default +// hasher please use the container's ::hasher alias instead. +// +// Example: typename Hash = typename absl::flat_hash_map::hasher +template +using hash_default_hash = typename container_internal::HashEq::Hash; + +// This header's visibility is restricted. If you need to access the default +// key equal please use the container's ::key_equal alias instead. +// +// Example: typename Eq = typename absl::flat_hash_map::key_equal +template +using hash_default_eq = typename container_internal::HashEq::Eq; + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..4610843a3d6 --- /dev/null +++ b/absl/container/internal/hash_function_defaults_test.cc @@ -0,0 +1,303 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/hash_function_defaults.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +using ::testing::Types; + +TEST(Eq, Int32) { + hash_default_eq eq; + EXPECT_TRUE(eq(1, 1u)); + EXPECT_TRUE(eq(1, char{1})); + EXPECT_TRUE(eq(1, true)); + EXPECT_TRUE(eq(1, double{1.1})); + EXPECT_FALSE(eq(1, char{2})); + EXPECT_FALSE(eq(1, 2u)); + EXPECT_FALSE(eq(1, false)); + EXPECT_FALSE(eq(1, 2.)); +} + +TEST(Hash, Int32) { + hash_default_hash hash; + auto h = hash(1); + EXPECT_EQ(h, hash(1u)); + EXPECT_EQ(h, hash(char{1})); + EXPECT_EQ(h, hash(true)); + EXPECT_EQ(h, hash(double{1.1})); + EXPECT_NE(h, hash(2u)); + EXPECT_NE(h, hash(char{2})); + EXPECT_NE(h, hash(false)); + EXPECT_NE(h, hash(2.)); +} + +enum class MyEnum { A, B, C, D }; + +TEST(Eq, Enum) { + hash_default_eq eq; + EXPECT_TRUE(eq(MyEnum::A, MyEnum::A)); + EXPECT_FALSE(eq(MyEnum::A, MyEnum::B)); +} + +TEST(Hash, Enum) { + hash_default_hash hash; + + for (MyEnum e : {MyEnum::A, MyEnum::B, MyEnum::C}) { + auto h = hash(e); + EXPECT_EQ(h, hash_default_hash{}(static_cast(e))); + EXPECT_NE(h, hash(MyEnum::D)); + } +} + +using StringTypes = ::testing::Types; + +template +struct EqString : ::testing::Test { + hash_default_eq key_eq; +}; + +TYPED_TEST_CASE(EqString, StringTypes); + +template +struct HashString : ::testing::Test { + hash_default_hash hasher; +}; + +TYPED_TEST_CASE(HashString, StringTypes); + +TYPED_TEST(EqString, Works) { + auto eq = this->key_eq; + EXPECT_TRUE(eq("a", "a")); + EXPECT_TRUE(eq("a", absl::string_view("a"))); + EXPECT_TRUE(eq("a", std::string("a"))); + EXPECT_FALSE(eq("a", "b")); + EXPECT_FALSE(eq("a", absl::string_view("b"))); + EXPECT_FALSE(eq("a", std::string("b"))); +} + +TYPED_TEST(HashString, Works) { + auto hash = this->hasher; + auto h = hash("a"); + EXPECT_EQ(h, hash(absl::string_view("a"))); + EXPECT_EQ(h, hash(std::string("a"))); + EXPECT_NE(h, hash(absl::string_view("b"))); + EXPECT_NE(h, hash(std::string("b"))); +} + +struct NoDeleter { + template + void operator()(const T* ptr) const {} +}; + +using PointerTypes = + ::testing::Types, + std::unique_ptr, + std::unique_ptr, std::unique_ptr, + std::shared_ptr, std::shared_ptr>; + +template +struct EqPointer : ::testing::Test { + hash_default_eq key_eq; +}; + +TYPED_TEST_CASE(EqPointer, PointerTypes); + +template +struct HashPointer : ::testing::Test { + hash_default_hash hasher; +}; + +TYPED_TEST_CASE(HashPointer, PointerTypes); + +TYPED_TEST(EqPointer, Works) { + int dummy; + auto eq = this->key_eq; + auto sptr = std::make_shared(); + std::shared_ptr csptr = sptr; + int* ptr = sptr.get(); + const int* cptr = ptr; + std::unique_ptr uptr(ptr); + std::unique_ptr cuptr(ptr); + + EXPECT_TRUE(eq(ptr, cptr)); + EXPECT_TRUE(eq(ptr, sptr)); + EXPECT_TRUE(eq(ptr, uptr)); + EXPECT_TRUE(eq(ptr, csptr)); + EXPECT_TRUE(eq(ptr, cuptr)); + EXPECT_FALSE(eq(&dummy, cptr)); + EXPECT_FALSE(eq(&dummy, sptr)); + EXPECT_FALSE(eq(&dummy, uptr)); + EXPECT_FALSE(eq(&dummy, csptr)); + EXPECT_FALSE(eq(&dummy, cuptr)); +} + +TEST(Hash, DerivedAndBase) { + struct Base {}; + struct Derived : Base {}; + + hash_default_hash hasher; + + Base base; + Derived derived; + EXPECT_NE(hasher(&base), hasher(&derived)); + EXPECT_EQ(hasher(static_cast(&derived)), hasher(&derived)); + + auto dp = std::make_shared(); + EXPECT_EQ(hasher(static_cast(dp.get())), hasher(dp)); +} + +TEST(Hash, FunctionPointer) { + using Func = int (*)(); + hash_default_hash hasher; + hash_default_eq eq; + + Func p1 = [] { return 1; }, p2 = [] { return 2; }; + EXPECT_EQ(hasher(p1), hasher(p1)); + EXPECT_TRUE(eq(p1, p1)); + + EXPECT_NE(hasher(p1), hasher(p2)); + EXPECT_FALSE(eq(p1, p2)); +} + +TYPED_TEST(HashPointer, Works) { + int dummy; + auto hash = this->hasher; + auto sptr = std::make_shared(); + std::shared_ptr csptr = sptr; + int* ptr = sptr.get(); + const int* cptr = ptr; + std::unique_ptr uptr(ptr); + std::unique_ptr cuptr(ptr); + + EXPECT_EQ(hash(ptr), hash(cptr)); + EXPECT_EQ(hash(ptr), hash(sptr)); + EXPECT_EQ(hash(ptr), hash(uptr)); + EXPECT_EQ(hash(ptr), hash(csptr)); + EXPECT_EQ(hash(ptr), hash(cuptr)); + EXPECT_NE(hash(&dummy), hash(cptr)); + EXPECT_NE(hash(&dummy), hash(sptr)); + EXPECT_NE(hash(&dummy), hash(uptr)); + EXPECT_NE(hash(&dummy), hash(csptr)); + EXPECT_NE(hash(&dummy), hash(cuptr)); +} + +// Cartesian product of (string, std::string, absl::string_view) +// with (string, 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>; +// clang-format on + +constexpr char kFirstString[] = "abc123"; +constexpr char kSecondString[] = "ijk456"; + +template +struct StringLikeTest : public ::testing::Test { + typename T::first_type a1{kFirstString}; + typename T::second_type b1{kFirstString}; + typename T::first_type a2{kSecondString}; + typename T::second_type b2{kSecondString}; + hash_default_eq eq; + hash_default_hash hash; +}; + +TYPED_TEST_CASE_P(StringLikeTest); + +TYPED_TEST_P(StringLikeTest, Eq) { + EXPECT_TRUE(this->eq(this->a1, this->b1)); + EXPECT_TRUE(this->eq(this->b1, this->a1)); +} + +TYPED_TEST_P(StringLikeTest, NotEq) { + EXPECT_FALSE(this->eq(this->a1, this->b2)); + EXPECT_FALSE(this->eq(this->b2, this->a1)); +} + +TYPED_TEST_P(StringLikeTest, HashEq) { + EXPECT_EQ(this->hash(this->a1), this->hash(this->b1)); + EXPECT_EQ(this->hash(this->a2), this->hash(this->b2)); + // It would be a poor hash function which collides on these strings. + EXPECT_NE(this->hash(this->a1), this->hash(this->b2)); +} + +TYPED_TEST_CASE(StringLikeTest, StringTypesCartesianProduct); + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl + +enum Hash : size_t { + kStd = 0x2, // std::hash +#ifdef _MSC_VER + kExtension = kStd, // In MSVC, std::hash == ::hash +#else // _MSC_VER + kExtension = 0x4, // ::hash (GCC extension) +#endif // _MSC_VER +}; + +// H is a bitmask of Hash enumerations. +// Hashable is hashable via all means specified in H. +template +struct Hashable { + static constexpr bool HashableBy(Hash h) { return h & H; } +}; + +namespace std { +template +struct hash> { + template , + class = typename std::enable_if::type> + size_t operator()(E) const { + return kStd; + } +}; +} // namespace std + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +template +size_t Hash(const T& v) { + return hash_default_hash()(v); +} + +TEST(Delegate, HashDispatch) { + EXPECT_EQ(Hash(kStd), Hash(Hashable())); +} + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc new file mode 100644 index 00000000000..aef41d7259c --- /dev/null +++ b/absl/container/internal/hash_generator_testing.cc @@ -0,0 +1,74 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/hash_generator_testing.h" + +#include + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace hash_internal { +namespace { + +class RandomDeviceSeedSeq { + public: + using result_type = typename std::random_device::result_type; + + template + void generate(Iterator start, Iterator end) { + while (start != end) { + *start = gen_(); + ++start; + } + } + + private: + std::random_device gen_; +}; + +} // namespace + +std::mt19937_64* GetSharedRng() { + RandomDeviceSeedSeq seed_seq; + static auto* rng = new std::mt19937_64(seed_seq); + return rng; +} + +std::string Generator::operator()() const { + // NOLINTNEXTLINE(runtime/int) + std::uniform_int_distribution chars(0x20, 0x7E); + std::string res; + res.resize(32); + std::generate(res.begin(), res.end(), + [&]() { return chars(*GetSharedRng()); }); + return res; +} + +absl::string_view Generator::operator()() const { + static auto* arena = new std::deque(); + // NOLINTNEXTLINE(runtime/int) + std::uniform_int_distribution chars(0x20, 0x7E); + arena->emplace_back(); + auto& res = arena->back(); + res.resize(32); + std::generate(res.begin(), res.end(), + [&]() { return chars(*GetSharedRng()); }); + return res; +} + +} // namespace hash_internal +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h new file mode 100644 index 00000000000..65e8896401c --- /dev/null +++ b/absl/container/internal/hash_generator_testing.h @@ -0,0 +1,152 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Generates random values for testing. Specialized only for the few types we +// care about. + +#ifndef ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/container/internal/hash_policy_testing.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace hash_internal { +namespace generator_internal { + +template +struct IsMap : std::false_type {}; + +template +struct IsMap> : std::true_type {}; + +} // namespace generator_internal + +std::mt19937_64* GetSharedRng(); + +enum Enum { + kEnumEmpty, + kEnumDeleted, +}; + +enum class EnumClass : uint64_t { + kEmpty, + kDeleted, +}; + +inline std::ostream& operator<<(std::ostream& o, const EnumClass& ec) { + return o << static_cast(ec); +} + +template +struct Generator; + +template +struct Generator::value>::type> { + T operator()() const { + std::uniform_int_distribution dist; + return dist(*GetSharedRng()); + } +}; + +template <> +struct Generator { + Enum operator()() const { + std::uniform_int_distribution::type> + dist; + while (true) { + auto variate = dist(*GetSharedRng()); + if (variate != kEnumEmpty && variate != kEnumDeleted) + return static_cast(variate); + } + } +}; + +template <> +struct Generator { + EnumClass operator()() const { + std::uniform_int_distribution< + typename std::underlying_type::type> + dist; + while (true) { + EnumClass variate = static_cast(dist(*GetSharedRng())); + if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted) + return static_cast(variate); + } + } +}; + +template <> +struct Generator { + std::string operator()() const; +}; + +template <> +struct Generator { + absl::string_view operator()() const; +}; + +template <> +struct Generator { + NonStandardLayout operator()() const { + return NonStandardLayout(Generator()()); + } +}; + +template +struct Generator> { + std::pair operator()() const { + return std::pair(Generator::type>()(), + Generator::type>()()); + } +}; + +template +struct Generator> { + std::tuple operator()() const { + return std::tuple(Generator::type>()()...); + } +}; + +template +struct Generator().key()), + decltype(std::declval().value())>> + : Generator().key())>::type, + typename std::decay().value())>::type>> {}; + +template +using GeneratedType = decltype( + std::declval::value, + typename Container::value_type, + typename Container::key_type>::type>&>()()); + +} // namespace hash_internal +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..9c310ad4b8d --- /dev/null +++ b/absl/container/internal/hash_policy_testing.h @@ -0,0 +1,184 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Utilities to help tests verify that hash tables properly handle stateful +// allocators and hash functions. + +#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/hash/hash.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace hash_testing_internal { + +template +struct WithId { + WithId() : id_(next_id()) {} + WithId(const WithId& that) : id_(that.id_) {} + WithId(WithId&& that) : id_(that.id_) { that.id_ = 0; } + WithId& operator=(const WithId& that) { + id_ = that.id_; + return *this; + } + WithId& operator=(WithId&& that) { + id_ = that.id_; + that.id_ = 0; + return *this; + } + + size_t id() const { return id_; } + + friend bool operator==(const WithId& a, const WithId& b) { + return a.id_ == b.id_; + } + friend bool operator!=(const WithId& a, const WithId& b) { return !(a == b); } + + protected: + explicit WithId(size_t id) : id_(id) {} + + private: + size_t id_; + + template + static size_t next_id() { + // 0 is reserved for moved from state. + static size_t gId = 1; + return gId++; + } +}; + +} // namespace hash_testing_internal + +struct NonStandardLayout { + NonStandardLayout() {} + explicit NonStandardLayout(std::string s) : value(std::move(s)) {} + virtual ~NonStandardLayout() {} + + friend bool operator==(const NonStandardLayout& a, + const NonStandardLayout& b) { + return a.value == b.value; + } + friend bool operator!=(const NonStandardLayout& a, + const NonStandardLayout& b) { + return a.value != b.value; + } + + template + friend H AbslHashValue(H h, const NonStandardLayout& v) { + return H::combine(std::move(h), v.value); + } + + std::string value; +}; + +struct StatefulTestingHash + : absl::container_internal::hash_testing_internal::WithId< + StatefulTestingHash> { + template + size_t operator()(const T& t) const { + return absl::Hash{}(t); + } +}; + +struct StatefulTestingEqual + : absl::container_internal::hash_testing_internal::WithId< + StatefulTestingEqual> { + template + bool operator()(const T& t, const U& u) const { + return t == u; + } +}; + +// It is expected that Alloc() == Alloc() for all allocators so we cannot use +// WithId base. We need to explicitly assign ids. +template +struct Alloc : std::allocator { + using propagate_on_container_swap = std::true_type; + + // Using old paradigm for this to ensure compatibility. + explicit Alloc(size_t id = 0) : id_(id) {} + + Alloc(const Alloc&) = default; + Alloc& operator=(const Alloc&) = default; + + template + Alloc(const Alloc& that) : std::allocator(that), id_(that.id()) {} + + template + struct rebind { + using other = Alloc; + }; + + size_t id() const { return id_; } + + friend bool operator==(const Alloc& a, const Alloc& b) { + return a.id_ == b.id_; + } + friend bool operator!=(const Alloc& a, const Alloc& b) { return !(a == b); } + + private: + size_t id_ = (std::numeric_limits::max)(); +}; + +template +auto items(const Map& m) -> std::vector< + std::pair> { + using std::get; + std::vector> res; + res.reserve(m.size()); + for (const auto& v : m) res.emplace_back(get<0>(v), get<1>(v)); + return res; +} + +template +auto keys(const Set& s) + -> std::vector::type> { + std::vector::type> res; + res.reserve(s.size()); + for (const auto& v : s) res.emplace_back(v); + return res; +} + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl + +// ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions +// where the unordered containers are missing certain constructors that +// take allocator arguments. This test is defined ad-hoc for the platforms +// we care about (notably Crosstool 17) because libstdcxx's useless +// versioning scheme precludes a more principled solution. +// From GCC-4.9 Changelog: (src: https://gcc.gnu.org/gcc-4.9/changes.html) +// "the unordered associative containers in and +// meet the allocator-aware container requirements;" +#if (defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425 ) || \ +( __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9 )) +#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 0 +#else +#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 1 +#endif + +#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ diff --git a/absl/container/internal/hash_policy_testing_test.cc b/absl/container/internal/hash_policy_testing_test.cc new file mode 100644 index 00000000000..00c436b3ae8 --- /dev/null +++ b/absl/container/internal/hash_policy_testing_test.cc @@ -0,0 +1,45 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/hash_policy_testing.h" + +#include "gtest/gtest.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +TEST(_, Hash) { + StatefulTestingHash h1; + EXPECT_EQ(1, h1.id()); + StatefulTestingHash h2; + EXPECT_EQ(2, h2.id()); + StatefulTestingHash h1c(h1); + EXPECT_EQ(1, h1c.id()); + StatefulTestingHash h2m(std::move(h2)); + EXPECT_EQ(2, h2m.id()); + EXPECT_EQ(0, h2.id()); + StatefulTestingHash h3; + EXPECT_EQ(3, h3.id()); + h3 = StatefulTestingHash(); + EXPECT_EQ(4, h3.id()); + h3 = std::move(h1); + EXPECT_EQ(1, h3.id()); +} + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/hash_policy_traits.h b/absl/container/internal/hash_policy_traits.h new file mode 100644 index 00000000000..41e2621227f --- /dev/null +++ b/absl/container/internal/hash_policy_traits.h @@ -0,0 +1,191 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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_HASH_POLICY_TRAITS_H_ +#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ + +#include +#include +#include +#include + +#include "absl/meta/type_traits.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { + +// Defines how slots are initialized/destroyed/moved. +template +struct hash_policy_traits { + private: + struct ReturnKey { + // We return `Key` here. + // When Key=T&, we forward the lvalue reference. + // When Key=T, we return by value to avoid a dangling reference. + // eg, for string_hash_map. + template + Key operator()(Key&& k, const Args&...) const { + return std::forward(k); + } + }; + + template + struct ConstantIteratorsImpl : std::false_type {}; + + template + struct ConstantIteratorsImpl> + : P::constant_iterators {}; + + public: + // The actual object stored in the hash table. + using slot_type = typename Policy::slot_type; + + // The type of the keys stored in the hashtable. + using key_type = typename Policy::key_type; + + // The argument type for insertions into the hashtable. This is different + // from value_type for increased performance. See initializer_list constructor + // and insert() member functions for more details. + using init_type = typename Policy::init_type; + + using reference = decltype(Policy::element(std::declval())); + using pointer = typename std::remove_reference::type*; + using value_type = typename std::remove_reference::type; + + // Policies can set this variable to tell raw_hash_set that all iterators + // should be constant, even `iterator`. This is useful for set-like + // containers. + // Defaults to false if not provided by the policy. + using constant_iterators = ConstantIteratorsImpl<>; + + // PRECONDITION: `slot` is UNINITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + Policy::construct(alloc, slot, std::forward(args)...); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is UNINITIALIZED + template + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::destroy(alloc, slot); + } + + // Transfers the `old_slot` to `new_slot`. Any memory allocated by the + // allocator inside `old_slot` to `new_slot` can be transferred. + // + // OPTIONAL: defaults to: + // + // clone(new_slot, std::move(*old_slot)); + // destroy(old_slot); + // + // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED + // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is + // UNINITIALIZED + template + static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) { + transfer_impl(alloc, new_slot, old_slot, 0); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + template + static auto element(slot_type* slot) -> decltype(P::element(slot)) { + return P::element(slot); + } + + // Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`. + // + // If `slot` is nullptr, returns the constant amount of memory owned by any + // full slot or -1 if slots own variable amounts of memory. + // + // PRECONDITION: `slot` is INITIALIZED or nullptr + template + static size_t space_used(const slot_type* slot) { + return P::space_used(slot); + } + + // Provides generalized access to the key for elements, both for elements in + // the table and for elements that have not yet been inserted (or even + // constructed). We would like an API that allows us to say: `key(args...)` + // but we cannot do that for all cases, so we use this more general API that + // can be used for many things, including the following: + // + // - Given an element in a table, get its key. + // - Given an element initializer, get its key. + // - Given `emplace()` arguments, get the element key. + // + // Implementations of this must adhere to a very strict technical + // specification around aliasing and consuming arguments: + // + // Let `value_type` be the result type of `element()` without ref- and + // cv-qualifiers. The first argument is a functor, the rest are constructor + // arguments for `value_type`. Returns `std::forward(f)(k, xs...)`, where + // `k` is the element key, and `xs...` are the new constructor arguments for + // `value_type`. It's allowed for `k` to alias `xs...`, and for both to alias + // `ts...`. The key won't be touched once `xs...` are used to construct an + // element; `ts...` won't be touched at all, which allows `apply()` to consume + // any rvalues among them. + // + // If `value_type` is constructible from `Ts&&...`, `Policy::apply()` must not + // trigger a hard compile error unless it originates from `f`. In other words, + // `Policy::apply()` must be SFINAE-friendly. If `value_type` is not + // constructible from `Ts&&...`, either SFINAE or a hard compile error is OK. + // + // If `Ts...` is `[cv] value_type[&]` or `[cv] init_type[&]`, + // `Policy::apply()` must work. A compile error is not allowed, SFINAE or not. + template + static auto apply(F&& f, Ts&&... ts) + -> decltype(P::apply(std::forward(f), std::forward(ts)...)) { + return P::apply(std::forward(f), std::forward(ts)...); + } + + // Returns the "key" portion of the slot. + // Used for node handle manipulation. + template + static auto key(slot_type* slot) + -> decltype(P::apply(ReturnKey(), element(slot))) { + return P::apply(ReturnKey(), element(slot)); + } + + // Returns the "value" (as opposed to the "key") portion of the element. Used + // by maps to implement `operator[]`, `at()` and `insert_or_assign()`. + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + private: + // Use auto -> decltype as an enabler. + template + static auto transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, int) + -> decltype((void)P::transfer(alloc, new_slot, old_slot)) { + P::transfer(alloc, new_slot, old_slot); + } + template + static void transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, char) { + construct(alloc, new_slot, std::move(element(old_slot))); + destroy(alloc, old_slot); + } +}; + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..07cecdfa9b2 --- /dev/null +++ b/absl/container/internal/hash_policy_traits_test.cc @@ -0,0 +1,144 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/hash_policy_traits.h" + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +using ::testing::MockFunction; +using ::testing::Return; +using ::testing::ReturnRef; + +using Alloc = std::allocator; +using Slot = int; + +struct PolicyWithoutOptionalOps { + using slot_type = Slot; + using key_type = Slot; + using init_type = Slot; + + static std::function construct; + static std::function destroy; + + static std::function element; + static int apply(int v) { return apply_impl(v); } + static std::function apply_impl; + static std::function value; +}; + +std::function PolicyWithoutOptionalOps::construct; +std::function PolicyWithoutOptionalOps::destroy; + +std::function PolicyWithoutOptionalOps::element; +std::function PolicyWithoutOptionalOps::apply_impl; +std::function PolicyWithoutOptionalOps::value; + +struct PolicyWithOptionalOps : PolicyWithoutOptionalOps { + static std::function transfer; +}; + +std::function PolicyWithOptionalOps::transfer; + +struct Test : ::testing::Test { + Test() { + PolicyWithoutOptionalOps::construct = [&](void* a1, Slot* a2, Slot a3) { + construct.Call(a1, a2, std::move(a3)); + }; + PolicyWithoutOptionalOps::destroy = [&](void* a1, Slot* a2) { + destroy.Call(a1, a2); + }; + + PolicyWithoutOptionalOps::element = [&](Slot* a1) -> Slot& { + return element.Call(a1); + }; + PolicyWithoutOptionalOps::apply_impl = [&](int a1) -> int { + return apply.Call(a1); + }; + PolicyWithoutOptionalOps::value = [&](Slot* a1) -> Slot& { + return value.Call(a1); + }; + + PolicyWithOptionalOps::transfer = [&](void* a1, Slot* a2, Slot* a3) { + return transfer.Call(a1, a2, a3); + }; + } + + std::allocator alloc; + int a = 53; + + MockFunction construct; + MockFunction destroy; + + MockFunction element; + MockFunction apply; + MockFunction value; + + MockFunction transfer; +}; + +TEST_F(Test, construct) { + EXPECT_CALL(construct, Call(&alloc, &a, 53)); + hash_policy_traits::construct(&alloc, &a, 53); +} + +TEST_F(Test, destroy) { + EXPECT_CALL(destroy, Call(&alloc, &a)); + hash_policy_traits::destroy(&alloc, &a); +} + +TEST_F(Test, element) { + int b = 0; + EXPECT_CALL(element, Call(&a)).WillOnce(ReturnRef(b)); + EXPECT_EQ(&b, &hash_policy_traits::element(&a)); +} + +TEST_F(Test, apply) { + EXPECT_CALL(apply, Call(42)).WillOnce(Return(1337)); + EXPECT_EQ(1337, (hash_policy_traits::apply(42))); +} + +TEST_F(Test, value) { + int b = 0; + EXPECT_CALL(value, Call(&a)).WillOnce(ReturnRef(b)); + EXPECT_EQ(&b, &hash_policy_traits::value(&a)); +} + +TEST_F(Test, without_transfer) { + int b = 42; + EXPECT_CALL(element, Call(&b)).WillOnce(::testing::ReturnRef(b)); + EXPECT_CALL(construct, Call(&alloc, &a, b)); + EXPECT_CALL(destroy, Call(&alloc, &b)); + hash_policy_traits::transfer(&alloc, &a, &b); +} + +TEST_F(Test, with_transfer) { + int b = 42; + EXPECT_CALL(transfer, Call(&alloc, &a, &b)); + hash_policy_traits::transfer(&alloc, &a, &b); +} + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/hashtable_debug.h b/absl/container/internal/hashtable_debug.h new file mode 100644 index 00000000000..b6a43512a0d --- /dev/null +++ b/absl/container/internal/hashtable_debug.h @@ -0,0 +1,110 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 library provides APIs to debug the probing behavior of hash tables. +// +// In general, the probing behavior is a black box for users and only the +// side effects can be measured in the form of performance differences. +// These APIs give a glimpse on the actual behavior of the probing algorithms in +// these hashtables given a specified hash function and a set of elements. +// +// The probe count distribution can be used to assess the quality of the hash +// function for that particular hash table. Note that a hash function that +// performs well in one hash table implementation does not necessarily performs +// well in a different one. +// +// This library supports std::unordered_{set,map}, dense_hash_{set,map} and +// absl::{flat,node,string}_hash_{set,map}. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ + +#include +#include +#include +#include + +#include "absl/container/internal/hashtable_debug_hooks.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { + +// Returns the number of probes required to lookup `key`. Returns 0 for a +// search with no collisions. Higher values mean more hash collisions occurred; +// however, the exact meaning of this number varies according to the container +// type. +template +size_t GetHashtableDebugNumProbes( + const C& c, const typename C::key_type& key) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::GetNumProbes(c, key); +} + +// Gets a histogram of the number of probes for each elements in the container. +// The sum of all the values in the vector is equal to container.size(). +template +std::vector GetHashtableDebugNumProbesHistogram(const C& container) { + std::vector v; + for (auto it = container.begin(); it != container.end(); ++it) { + 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[num_probes]++; + } + return v; +} + +struct HashtableDebugProbeSummary { + size_t total_elements; + size_t total_num_probes; + double mean; +}; + +// Gets a summary of the probe count distribution for the elements in the +// container. +template +HashtableDebugProbeSummary GetHashtableDebugProbeSummary(const C& container) { + auto probes = GetHashtableDebugNumProbesHistogram(container); + HashtableDebugProbeSummary summary = {}; + for (size_t i = 0; i < probes.size(); ++i) { + summary.total_elements += probes[i]; + summary.total_num_probes += probes[i] * i; + } + summary.mean = 1.0 * summary.total_num_probes / summary.total_elements; + return summary; +} + +// Returns the number of bytes requested from the allocator by the container +// and not freed. +template +size_t AllocatedByteSize(const C& c) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::AllocatedByteSize(c); +} + +// Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type `C` +// and `c.size()` is equal to `num_elements`. +template +size_t LowerBoundAllocatedByteSize(size_t num_elements) { + return absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess::LowerBoundAllocatedByteSize(num_elements); +} + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..50ba6ba5304 --- /dev/null +++ b/absl/container/internal/hashtable_debug_hooks.h @@ -0,0 +1,83 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Provides the internal API for hashtable_debug.h. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ + +#include + +#include +#include +#include + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace hashtable_debug_internal { + +// If it is a map, call get<0>(). +using std::get; +template +auto GetKey(const typename T::value_type& pair, int) -> decltype(get<0>(pair)) { + return get<0>(pair); +} + +// If it is not a map, return the value directly. +template +const typename T::key_type& GetKey(const typename T::key_type& key, char) { + return key; +} + +// Containers should specialize this to provide debug information for that +// container. +template +struct HashtableDebugAccess { + // Returns the number of probes required to find `key` in `c`. The "number of + // probes" is a concept that can vary by container. Implementations should + // return 0 when `key` was found in the minimum number of operations and + // should increment the result for each non-trivial operation required to find + // `key`. + // + // The default implementation uses the bucket api from the standard and thus + // works for `std::unordered_*` containers. + static size_t GetNumProbes(const Container& c, + const typename Container::key_type& key) { + if (!c.bucket_count()) return {}; + size_t num_probes = 0; + size_t bucket = c.bucket(key); + for (auto it = c.begin(bucket), e = c.end(bucket);; ++it, ++num_probes) { + if (it == e) return num_probes; + if (c.key_eq()(key, GetKey(*it, 0))) return num_probes; + } + } + + // Returns the number of bytes requested from the allocator by the container + // and not freed. + // + // static size_t AllocatedByteSize(const Container& c); + + // Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type + // `Container` and `c.size()` is equal to `num_elements`. + // + // static size_t LowerBoundAllocatedByteSize(size_t num_elements); +}; + +} // namespace hashtable_debug_internal +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ diff --git a/absl/container/internal/layout.h b/absl/container/internal/layout.h new file mode 100644 index 00000000000..f11a6ad29cf --- /dev/null +++ b/absl/container/internal/layout.h @@ -0,0 +1,740 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// MOTIVATION AND TUTORIAL +// +// If you want to put in a single heap allocation N doubles followed by M ints, +// it's easy if N and M are known at compile time. +// +// struct S { +// double a[N]; +// int b[M]; +// }; +// +// S* p = new S; +// +// But what if N and M are known only in run time? Class template Layout to the +// rescue! It's a portable generalization of the technique known as struct hack. +// +// // This object will tell us everything we need to know about the memory +// // layout of double[N] followed by int[M]. It's structurally identical to +// // size_t[2] that stores N and M. It's very cheap to create. +// const Layout layout(N, M); +// +// // Allocate enough memory for both arrays. `AllocSize()` tells us how much +// // memory is needed. We are free to use any allocation function we want as +// // long as it returns aligned memory. +// std::unique_ptr p(new unsigned char[layout.AllocSize()]); +// +// // Obtain the pointer to the array of doubles. +// // Equivalent to `reinterpret_cast(p.get())`. +// // +// // We could have written layout.Pointer<0>(p) instead. If all the types are +// // unique you can use either form, but if some types are repeated you must +// // use the index form. +// double* a = layout.Pointer(p.get()); +// +// // Obtain the pointer to the array of ints. +// // Equivalent to `reinterpret_cast(p.get() + N * 8)`. +// int* b = layout.Pointer(p); +// +// If we are unable to specify sizes of all fields, we can pass as many sizes as +// we can to `Partial()`. In return, it'll allow us to access the fields whose +// locations and sizes can be computed from the provided information. +// `Partial()` comes in handy when the array sizes are embedded into the +// allocation. +// +// // size_t[1] containing N, size_t[1] containing M, double[N], int[M]. +// using L = Layout; +// +// unsigned char* Allocate(size_t n, size_t m) { +// const L layout(1, 1, n, m); +// unsigned char* p = new unsigned char[layout.AllocSize()]; +// *layout.Pointer<0>(p) = n; +// *layout.Pointer<1>(p) = m; +// return p; +// } +// +// void Use(unsigned char* p) { +// // First, extract N and M. +// // Specify that the first array has only one element. Using `prefix` we +// // can access the first two arrays but not more. +// constexpr auto prefix = L::Partial(1); +// size_t n = *prefix.Pointer<0>(p); +// size_t m = *prefix.Pointer<1>(p); +// +// // Now we can get pointers to the payload. +// const L layout(1, 1, n, m); +// double* a = layout.Pointer(p); +// int* b = layout.Pointer(p); +// } +// +// The layout we used above combines fixed-size with dynamically-sized fields. +// This is quite common. Layout is optimized for this use case and generates +// optimal code. All computations that can be performed at compile time are +// indeed performed at compile time. +// +// Efficiency tip: The order of fields matters. In `Layout` try to +// ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no +// padding in between arrays. +// +// You can manually override the alignment of an array by wrapping the type in +// `Aligned`. `Layout<..., Aligned, ...>` has exactly the same API +// and behavior as `Layout<..., T, ...>` except that the first element of the +// array of `T` is aligned to `N` (the rest of the elements follow without +// padding). `N` cannot be less than `alignof(T)`. +// +// `AllocSize()` and `Pointer()` are the most basic methods for dealing with +// memory layouts. Check out the reference or code below to discover more. +// +// EXAMPLE +// +// // Immutable move-only string with sizeof equal to sizeof(void*). The +// // string size and the characters are kept in the same heap allocation. +// class CompactString { +// public: +// CompactString(const char* s = "") { +// const size_t size = strlen(s); +// // size_t[1] followed by char[size + 1]. +// const L layout(1, size + 1); +// p_.reset(new unsigned char[layout.AllocSize()]); +// // If running under ASAN, mark the padding bytes, if any, to catch +// // memory errors. +// layout.PoisonPadding(p_.get()); +// // Store the size in the allocation. +// *layout.Pointer(p_.get()) = size; +// // Store the characters in the allocation. +// memcpy(layout.Pointer(p_.get()), s, size + 1); +// } +// +// size_t size() const { +// // Equivalent to reinterpret_cast(*p). +// return *L::Partial().Pointer(p_.get()); +// } +// +// const char* c_str() const { +// // Equivalent to reinterpret_cast(p.get() + sizeof(size_t)). +// // The argument in Partial(1) specifies that we have size_t[1] in front +// // of the characters. +// return L::Partial(1).Pointer(p_.get()); +// } +// +// private: +// // Our heap allocation contains a size_t followed by an array of chars. +// using L = Layout; +// std::unique_ptr p_; +// }; +// +// int main() { +// CompactString s = "hello"; +// assert(s.size() == 5); +// assert(strcmp(s.c_str(), "hello") == 0); +// } +// +// DOCUMENTATION +// +// The interface exported by this file consists of: +// - class `Layout<>` and its public members. +// - The public members of class `internal_layout::LayoutImpl<>`. That class +// isn't intended to be used directly, and its name and template parameter +// list are internal implementation details, but the class itself provides +// most of the functionality in this file. See comments on its members for +// detailed documentation. +// +// `Layout::Partial(count1,..., countm)` (where `m` <= `n`) returns a +// `LayoutImpl<>` object. `Layout layout(count1,..., countn)` +// creates a `Layout` object, which exposes the same functionality by inheriting +// from `LayoutImpl<>`. + +#ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_ +#define ABSL_CONTAINER_INTERNAL_LAYOUT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ADDRESS_SANITIZER +#include +#endif + +#include "absl/meta/type_traits.h" +#include "absl/strings/str_cat.h" +#include "absl/types/span.h" +#include "absl/utility/utility.h" + +#if defined(__GXX_RTTI) +#define ABSL_INTERNAL_HAS_CXA_DEMANGLE +#endif + +#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE +#include +#endif + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { + +// A type wrapper that instructs `Layout` to use the specific alignment for the +// array. `Layout<..., Aligned, ...>` has exactly the same API +// and behavior as `Layout<..., T, ...>` except that the first element of the +// array of `T` is aligned to `N` (the rest of the elements follow without +// padding). +// +// Requires: `N >= alignof(T)` and `N` is a power of 2. +template +struct Aligned; + +namespace internal_layout { + +template +struct NotAligned {}; + +template +struct NotAligned> { + static_assert(sizeof(T) == 0, "Aligned cannot be const-qualified"); +}; + +template +using IntToSize = size_t; + +template +using TypeToSize = size_t; + +template +struct Type : NotAligned { + using type = T; +}; + +template +struct Type> { + using type = T; +}; + +template +struct SizeOf : NotAligned, std::integral_constant {}; + +template +struct SizeOf> : std::integral_constant {}; + +// Note: workaround for https://gcc.gnu.org/PR88115 +template +struct AlignOf : NotAligned { + static constexpr size_t value = alignof(T); +}; + +template +struct AlignOf> { + static_assert(N % alignof(T) == 0, + "Custom alignment can't be lower than the type's alignment"); + static constexpr size_t value = N; +}; + +// Does `Ts...` contain `T`? +template +using Contains = absl::disjunction...>; + +template +using CopyConst = + typename std::conditional::value, const To, To>::type; + +// Note: We're not qualifying this with absl:: because it doesn't compile under +// MSVC. +template +using SliceType = Span; + +// This namespace contains no types. It prevents functions defined in it from +// being found by ADL. +namespace adl_barrier { + +template +constexpr size_t Find(Needle, Needle, Ts...) { + static_assert(!Contains(), "Duplicate element type"); + return 0; +} + +template +constexpr size_t Find(Needle, T, Ts...) { + return adl_barrier::Find(Needle(), Ts()...) + 1; +} + +constexpr bool IsPow2(size_t n) { return !(n & (n - 1)); } + +// Returns `q * m` for the smallest `q` such that `q * m >= n`. +// Requires: `m` is a power of two. It's enforced by IsLegalElementType below. +constexpr size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); } + +constexpr size_t Min(size_t a, size_t b) { return b < a ? b : a; } + +constexpr size_t Max(size_t a) { return a; } + +template +constexpr size_t Max(size_t a, size_t b, Ts... rest) { + return adl_barrier::Max(b < a ? a : b, rest...); +} + +template +std::string TypeName() { + std::string out; + int status = 0; + char* demangled = nullptr; +#ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE + demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); +#endif + if (status == 0 && demangled != nullptr) { // Demangling succeeded. + absl::StrAppend(&out, "<", demangled, ">"); + free(demangled); + } else { +#if defined(__GXX_RTTI) || defined(_CPPRTTI) + absl::StrAppend(&out, "<", typeid(T).name(), ">"); +#endif + } + return out; +} + +} // namespace adl_barrier + +template +using EnableIf = typename std::enable_if::type; + +// Can `T` be a template argument of `Layout`? +template +using IsLegalElementType = std::integral_constant< + bool, !std::is_reference::value && !std::is_volatile::value && + !std::is_reference::type>::value && + !std::is_volatile::type>::value && + adl_barrier::IsPow2(AlignOf::value)>; + +template +class LayoutImpl; + +// Public base class of `Layout` and the result type of `Layout::Partial()`. +// +// `Elements...` contains all template arguments of `Layout` that created this +// instance. +// +// `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments +// passed to `Layout::Partial()` or `Layout::Layout()`. +// +// `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is +// `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we +// can compute offsets). +template +class LayoutImpl, absl::index_sequence, + absl::index_sequence> { + private: + static_assert(sizeof...(Elements) > 0, "At least one field is required"); + static_assert(absl::conjunction...>::value, + "Invalid element type (see IsLegalElementType)"); + + enum { + NumTypes = sizeof...(Elements), + NumSizes = sizeof...(SizeSeq), + NumOffsets = sizeof...(OffsetSeq), + }; + + // These are guaranteed by `Layout`. + static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1), + "Internal error"); + static_assert(NumTypes > 0, "Internal error"); + + // Returns the index of `T` in `Elements...`. Results in a compilation error + // if `Elements...` doesn't contain exactly one instance of `T`. + template + static constexpr size_t ElementIndex() { + static_assert(Contains, Type::type>...>(), + "Type not found"); + return adl_barrier::Find(Type(), + Type::type>()...); + } + + template + using ElementAlignment = + AlignOf>::type>; + + public: + // Element types of all arrays packed in a tuple. + using ElementTypes = std::tuple::type...>; + + // Element type of the Nth array. + template + using ElementType = typename std::tuple_element::type; + + constexpr explicit LayoutImpl(IntToSize... sizes) + : size_{sizes...} {} + + // Alignment of the layout, equal to the strictest alignment of all elements. + // All pointers passed to the methods of layout must be aligned to this value. + static constexpr size_t Alignment() { + return adl_barrier::Max(AlignOf::value...); + } + + // Offset in bytes of the Nth array. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Offset<0>() == 0); // The ints starts from 0. + // assert(x.Offset<1>() == 16); // The doubles starts from 16. + // + // Requires: `N <= NumSizes && N < sizeof...(Ts)`. + template = 0> + constexpr size_t Offset() const { + return 0; + } + + template = 0> + constexpr size_t Offset() const { + static_assert(N < NumOffsets, "Index out of bounds"); + return adl_barrier::Align( + Offset() + SizeOf>() * size_[N - 1], + ElementAlignment::value); + } + + // Offset in bytes of the array with the specified element type. There must + // be exactly one such array and its zero-based index must be at most + // `NumSizes`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Offset() == 0); // The ints starts from 0. + // assert(x.Offset() == 16); // The doubles starts from 16. + template + constexpr size_t Offset() const { + return Offset()>(); + } + + // Offsets in bytes of all arrays for which the offsets are known. + constexpr std::array Offsets() const { + return {{Offset()...}}; + } + + // The number of elements in the Nth array. This is the Nth argument of + // `Layout::Partial()` or `Layout::Layout()` (zero-based). + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Size<0>() == 3); + // assert(x.Size<1>() == 4); + // + // Requires: `N < NumSizes`. + template + constexpr size_t Size() const { + static_assert(N < NumSizes, "Index out of bounds"); + return size_[N]; + } + + // The number of elements in the array with the specified element type. + // There must be exactly one such array and its zero-based index must be + // at most `NumSizes`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // assert(x.Size() == 3); + // assert(x.Size() == 4); + template + constexpr size_t Size() const { + return Size()>(); + } + + // The number of elements of all arrays for which they are known. + constexpr std::array Sizes() const { + return {{Size()...}}; + } + + // Pointer to the beginning of the Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // int* ints = x.Pointer<0>(p); + // double* doubles = x.Pointer<1>(p); + // + // Requires: `N <= NumSizes && N < sizeof...(Ts)`. + // Requires: `p` is aligned to `Alignment()`. + template + CopyConst>* Pointer(Char* p) const { + using C = typename std::remove_const::type; + static_assert( + std::is_same() || std::is_same() || + std::is_same(), + "The argument must be a pointer to [const] [signed|unsigned] char"); + constexpr size_t alignment = Alignment(); + (void)alignment; + assert(reinterpret_cast(p) % alignment == 0); + return reinterpret_cast>*>(p + Offset()); + } + + // Pointer to the beginning of the array with the specified element type. + // There must be exactly one such array and its zero-based index must be at + // most `NumSizes`. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // int* ints = x.Pointer(p); + // double* doubles = x.Pointer(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + CopyConst* Pointer(Char* p) const { + return Pointer()>(p); + } + + // Pointers to all arrays for which pointers are known. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // + // int* ints; + // double* doubles; + // std::tie(ints, doubles) = x.Pointers(p); + // + // Requires: `p` is aligned to `Alignment()`. + // + // Note: We're not using ElementType alias here because it does not compile + // under MSVC. + template + std::tuple::type>*...> + Pointers(Char* p) const { + return std::tuple>*...>( + Pointer(p)...); + } + + // The Nth array. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice<0>(p); + // Span doubles = x.Slice<1>(p); + // + // Requires: `N < NumSizes`. + // Requires: `p` is aligned to `Alignment()`. + template + SliceType>> Slice(Char* p) const { + return SliceType>>(Pointer(p), Size()); + } + + // The array with the specified element type. There must be exactly one + // such array and its zero-based index must be less than `NumSizes`. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // Span ints = x.Slice(p); + // Span doubles = x.Slice(p); + // + // Requires: `p` is aligned to `Alignment()`. + template + SliceType> Slice(Char* p) const { + return Slice()>(p); + } + + // All arrays with known sizes. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; + // + // Span ints; + // Span doubles; + // std::tie(ints, doubles) = x.Slices(p); + // + // Requires: `p` is aligned to `Alignment()`. + // + // Note: We're not using ElementType alias here because it does not compile + // under MSVC. + template + std::tuple::type>>...> + Slices(Char* p) const { + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63875 (fixed + // in 6.1). + (void)p; + return std::tuple>>...>( + Slice(p)...); + } + + // The size of the allocation that fits all arrays. + // + // // int[3], 4 bytes of padding, double[4]. + // Layout x(3, 4); + // unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes + // + // Requires: `NumSizes == sizeof...(Ts)`. + constexpr size_t AllocSize() const { + static_assert(NumTypes == NumSizes, "You must specify sizes of all fields"); + return Offset() + + SizeOf>() * size_[NumTypes - 1]; + } + + // If built with --config=asan, poisons padding bytes (if any) in the + // allocation. The pointer must point to a memory block at least + // `AllocSize()` bytes in length. + // + // `Char` must be `[const] [signed|unsigned] char`. + // + // Requires: `p` is aligned to `Alignment()`. + template = 0> + void PoisonPadding(const Char* p) const { + Pointer<0>(p); // verify the requirements on `Char` and `p` + } + + template = 0> + void PoisonPadding(const Char* p) const { + static_assert(N < NumOffsets, "Index out of bounds"); + (void)p; +#ifdef ADDRESS_SANITIZER + PoisonPadding(p); + // The `if` is an optimization. It doesn't affect the observable behaviour. + if (ElementAlignment::value % ElementAlignment::value) { + size_t start = + Offset() + SizeOf>() * size_[N - 1]; + ASAN_POISON_MEMORY_REGION(p + start, Offset() - start); + } +#endif + } + + // Human-readable description of the memory layout. Useful for debugging. + // Slow. + // + // // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed + // // by an unknown number of doubles. + // auto x = Layout::Partial(5, 3); + // assert(x.DebugString() == + // "@0(1)[5]; @8(4)[3]; @24(8)"); + // + // Each field is in the following format: @offset(sizeof)[size] ( + // may be missing depending on the target platform). For example, + // @8(4)[3] means that at offset 8 we have an array of ints, where each + // int is 4 bytes, and we have 3 of those ints. The size of the last field may + // be missing (as in the example above). Only fields with known offsets are + // described. Type names may differ across platforms: one compiler might + // produce "unsigned*" where another produces "unsigned int *". + std::string DebugString() const { + const auto offsets = Offsets(); + const size_t sizes[] = {SizeOf>()...}; + 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], + "(", sizes[i + 1], ")"); + } + // NumSizes is a constant that may be zero. Some compilers cannot see that + // inside the if statement "size_[NumSizes - 1]" must be valid. + int last = static_cast(NumSizes) - 1; + if (NumTypes == NumSizes && last >= 0) { + absl::StrAppend(&res, "[", size_[last], "]"); + } + return res; + } + + private: + // Arguments of `Layout::Partial()` or `Layout::Layout()`. + size_t size_[NumSizes > 0 ? NumSizes : 1]; +}; + +template +using LayoutType = LayoutImpl< + std::tuple, absl::make_index_sequence, + absl::make_index_sequence>; + +} // namespace internal_layout + +// Descriptor of arrays of various types and sizes laid out in memory one after +// another. See the top of the file for documentation. +// +// Check out the public API of internal_layout::LayoutImpl above. The type is +// internal to the library but its methods are public, and they are inherited +// by `Layout`. +template +class Layout : public internal_layout::LayoutType { + public: + static_assert(sizeof...(Ts) > 0, "At least one field is required"); + static_assert( + absl::conjunction...>::value, + "Invalid element type (see IsLegalElementType)"); + + // The result type of `Partial()` with `NumSizes` arguments. + template + using PartialType = internal_layout::LayoutType; + + // `Layout` knows the element types of the arrays we want to lay out in + // memory but not the number of elements in each array. + // `Partial(size1, ..., sizeN)` allows us to specify the latter. The + // resulting immutable object can be used to obtain pointers to the + // individual arrays. + // + // It's allowed to pass fewer array sizes than the number of arrays. E.g., + // if all you need is to the offset of the second array, you only need to + // pass one argument -- the number of elements in the first array. + // + // // int[3] followed by 4 bytes of padding and an unknown number of + // // doubles. + // auto x = Layout::Partial(3); + // // doubles start at byte 16. + // assert(x.Offset<1>() == 16); + // + // If you know the number of elements in all arrays, you can still call + // `Partial()` but it's more convenient to use the constructor of `Layout`. + // + // Layout x(3, 5); + // + // Note: The sizes of the arrays must be specified in number of elements, + // not in bytes. + // + // Requires: `sizeof...(Sizes) <= sizeof...(Ts)`. + // Requires: all arguments are convertible to `size_t`. + template + static constexpr PartialType Partial(Sizes&&... sizes) { + static_assert(sizeof...(Sizes) <= sizeof...(Ts), ""); + return PartialType(absl::forward(sizes)...); + } + + // Creates a layout with the sizes of all arrays specified. If you know + // only the sizes of the first N arrays (where N can be zero), you can use + // `Partial()` defined above. The constructor is essentially equivalent to + // calling `Partial()` and passing in all array sizes; the constructor is + // provided as a convenient abbreviation. + // + // Note: The sizes of the arrays must be specified in number of elements, + // not in bytes. + constexpr explicit Layout(internal_layout::TypeToSize... sizes) + : internal_layout::LayoutType(sizes...) {} +}; + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc new file mode 100644 index 00000000000..b9f98471281 --- /dev/null +++ b/absl/container/internal/layout_test.cc @@ -0,0 +1,1557 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/layout.h" + +// We need ::max_align_t because some libstdc++ versions don't provide +// std::max_align_t +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/types/span.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +using ::absl::Span; +using ::testing::ElementsAre; + +size_t Distance(const void* from, const void* to) { + ABSL_RAW_CHECK(from <= to, "Distance must be non-negative"); + return static_cast(to) - static_cast(from); +} + +template +Expected Type(Actual val) { + static_assert(std::is_same(), ""); + return val; +} + +// Helper class to test different size and alignments. +struct alignas(8) Int128 { + uint64_t a, b; + friend bool operator==(Int128 lhs, Int128 rhs) { + return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b); + } + + static std::string Name() { + return internal_layout::adl_barrier::TypeName(); + } +}; + +// Properties of types that this test relies on. +static_assert(sizeof(int8_t) == 1, ""); +static_assert(alignof(int8_t) == 1, ""); +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(Int128) == 16, ""); +static_assert(alignof(Int128) == 8, ""); + +template +void SameType() { + static_assert(std::is_same(), ""); +} + +TEST(Layout, ElementType) { + { + using L = Layout; + SameType>(); + SameType>(); + SameType>(); + } + { + using L = Layout; + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + } + { + using L = Layout; + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + SameType>(); + } +} + +TEST(Layout, ElementTypes) { + { + using L = Layout; + SameType, L::ElementTypes>(); + SameType, decltype(L::Partial())::ElementTypes>(); + SameType, decltype(L::Partial(0))::ElementTypes>(); + } + { + using L = Layout; + SameType, L::ElementTypes>(); + SameType, decltype(L::Partial())::ElementTypes>(); + SameType, decltype(L::Partial(0))::ElementTypes>(); + } + { + using L = Layout; + SameType, L::ElementTypes>(); + SameType, + decltype(L::Partial())::ElementTypes>(); + SameType, + decltype(L::Partial(0))::ElementTypes>(); + SameType, + decltype(L::Partial(0, 0))::ElementTypes>(); + SameType, + decltype(L::Partial(0, 0, 0))::ElementTypes>(); + } +} + +TEST(Layout, OffsetByIndex) { + { + using L = Layout; + EXPECT_EQ(0, L::Partial().Offset<0>()); + EXPECT_EQ(0, L::Partial(3).Offset<0>()); + EXPECT_EQ(0, L(3).Offset<0>()); + } + { + using L = Layout; + EXPECT_EQ(0, L::Partial().Offset<0>()); + EXPECT_EQ(0, L::Partial(3).Offset<0>()); + EXPECT_EQ(12, L::Partial(3).Offset<1>()); + EXPECT_EQ(0, L::Partial(3, 5).Offset<0>()); + EXPECT_EQ(12, L::Partial(3, 5).Offset<1>()); + EXPECT_EQ(0, L(3, 5).Offset<0>()); + EXPECT_EQ(12, L(3, 5).Offset<1>()); + } + { + using L = Layout; + EXPECT_EQ(0, L::Partial().Offset<0>()); + EXPECT_EQ(0, L::Partial(0).Offset<0>()); + EXPECT_EQ(0, L::Partial(0).Offset<1>()); + EXPECT_EQ(0, L::Partial(1).Offset<0>()); + EXPECT_EQ(4, L::Partial(1).Offset<1>()); + EXPECT_EQ(0, L::Partial(5).Offset<0>()); + EXPECT_EQ(8, L::Partial(5).Offset<1>()); + EXPECT_EQ(0, L::Partial(0, 0).Offset<0>()); + EXPECT_EQ(0, L::Partial(0, 0).Offset<1>()); + EXPECT_EQ(0, L::Partial(0, 0).Offset<2>()); + EXPECT_EQ(0, L::Partial(1, 0).Offset<0>()); + EXPECT_EQ(4, L::Partial(1, 0).Offset<1>()); + EXPECT_EQ(8, L::Partial(1, 0).Offset<2>()); + EXPECT_EQ(0, L::Partial(5, 3).Offset<0>()); + EXPECT_EQ(8, L::Partial(5, 3).Offset<1>()); + EXPECT_EQ(24, L::Partial(5, 3).Offset<2>()); + EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<0>()); + EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<1>()); + EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<2>()); + EXPECT_EQ(0, L::Partial(1, 0, 0).Offset<0>()); + EXPECT_EQ(4, L::Partial(1, 0, 0).Offset<1>()); + EXPECT_EQ(8, L::Partial(1, 0, 0).Offset<2>()); + EXPECT_EQ(0, L::Partial(5, 3, 1).Offset<0>()); + EXPECT_EQ(24, L::Partial(5, 3, 1).Offset<2>()); + EXPECT_EQ(8, L::Partial(5, 3, 1).Offset<1>()); + EXPECT_EQ(0, L(5, 3, 1).Offset<0>()); + EXPECT_EQ(24, L(5, 3, 1).Offset<2>()); + EXPECT_EQ(8, L(5, 3, 1).Offset<1>()); + } +} + +TEST(Layout, OffsetByType) { + { + using L = Layout; + EXPECT_EQ(0, L::Partial().Offset()); + EXPECT_EQ(0, L::Partial(3).Offset()); + EXPECT_EQ(0, L(3).Offset()); + } + { + using L = Layout; + EXPECT_EQ(0, L::Partial().Offset()); + EXPECT_EQ(0, L::Partial(0).Offset()); + EXPECT_EQ(0, L::Partial(0).Offset()); + EXPECT_EQ(0, L::Partial(1).Offset()); + EXPECT_EQ(4, L::Partial(1).Offset()); + EXPECT_EQ(0, L::Partial(5).Offset()); + EXPECT_EQ(8, L::Partial(5).Offset()); + EXPECT_EQ(0, L::Partial(0, 0).Offset()); + EXPECT_EQ(0, L::Partial(0, 0).Offset()); + EXPECT_EQ(0, L::Partial(0, 0).Offset()); + EXPECT_EQ(0, L::Partial(1, 0).Offset()); + EXPECT_EQ(4, L::Partial(1, 0).Offset()); + EXPECT_EQ(8, L::Partial(1, 0).Offset()); + EXPECT_EQ(0, L::Partial(5, 3).Offset()); + EXPECT_EQ(8, L::Partial(5, 3).Offset()); + EXPECT_EQ(24, L::Partial(5, 3).Offset()); + EXPECT_EQ(0, L::Partial(0, 0, 0).Offset()); + EXPECT_EQ(0, L::Partial(0, 0, 0).Offset()); + EXPECT_EQ(0, L::Partial(0, 0, 0).Offset()); + EXPECT_EQ(0, L::Partial(1, 0, 0).Offset()); + EXPECT_EQ(4, L::Partial(1, 0, 0).Offset()); + EXPECT_EQ(8, L::Partial(1, 0, 0).Offset()); + EXPECT_EQ(0, L::Partial(5, 3, 1).Offset()); + EXPECT_EQ(24, L::Partial(5, 3, 1).Offset()); + EXPECT_EQ(8, L::Partial(5, 3, 1).Offset()); + EXPECT_EQ(0, L(5, 3, 1).Offset()); + EXPECT_EQ(24, L(5, 3, 1).Offset()); + EXPECT_EQ(8, L(5, 3, 1).Offset()); + } +} + +TEST(Layout, Offsets) { + { + using L = Layout; + EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0)); + EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0)); + EXPECT_THAT(L(3).Offsets(), ElementsAre(0)); + } + { + using L = Layout; + EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0)); + EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0, 12)); + EXPECT_THAT(L::Partial(3, 5).Offsets(), ElementsAre(0, 12)); + EXPECT_THAT(L(3, 5).Offsets(), ElementsAre(0, 12)); + } + { + using L = Layout; + EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0)); + EXPECT_THAT(L::Partial(1).Offsets(), ElementsAre(0, 4)); + EXPECT_THAT(L::Partial(5).Offsets(), ElementsAre(0, 8)); + EXPECT_THAT(L::Partial(0, 0).Offsets(), ElementsAre(0, 0, 0)); + EXPECT_THAT(L::Partial(1, 0).Offsets(), ElementsAre(0, 4, 8)); + EXPECT_THAT(L::Partial(5, 3).Offsets(), ElementsAre(0, 8, 24)); + EXPECT_THAT(L::Partial(0, 0, 0).Offsets(), ElementsAre(0, 0, 0)); + EXPECT_THAT(L::Partial(1, 0, 0).Offsets(), ElementsAre(0, 4, 8)); + EXPECT_THAT(L::Partial(5, 3, 1).Offsets(), ElementsAre(0, 8, 24)); + EXPECT_THAT(L(5, 3, 1).Offsets(), ElementsAre(0, 8, 24)); + } +} + +TEST(Layout, AllocSize) { + { + using L = Layout; + EXPECT_EQ(0, L::Partial(0).AllocSize()); + EXPECT_EQ(12, L::Partial(3).AllocSize()); + EXPECT_EQ(12, L(3).AllocSize()); + } + { + using L = Layout; + EXPECT_EQ(32, L::Partial(3, 5).AllocSize()); + EXPECT_EQ(32, L(3, 5).AllocSize()); + } + { + using L = Layout; + EXPECT_EQ(0, L::Partial(0, 0, 0).AllocSize()); + EXPECT_EQ(8, L::Partial(1, 0, 0).AllocSize()); + EXPECT_EQ(8, L::Partial(0, 1, 0).AllocSize()); + EXPECT_EQ(16, L::Partial(0, 0, 1).AllocSize()); + EXPECT_EQ(24, L::Partial(1, 1, 1).AllocSize()); + EXPECT_EQ(136, L::Partial(3, 5, 7).AllocSize()); + EXPECT_EQ(136, L(3, 5, 7).AllocSize()); + } +} + +TEST(Layout, SizeByIndex) { + { + using L = Layout; + EXPECT_EQ(0, L::Partial(0).Size<0>()); + EXPECT_EQ(3, L::Partial(3).Size<0>()); + EXPECT_EQ(3, L(3).Size<0>()); + } + { + using L = Layout; + EXPECT_EQ(0, L::Partial(0).Size<0>()); + EXPECT_EQ(3, L::Partial(3).Size<0>()); + EXPECT_EQ(3, L::Partial(3, 5).Size<0>()); + EXPECT_EQ(5, L::Partial(3, 5).Size<1>()); + EXPECT_EQ(3, L(3, 5).Size<0>()); + EXPECT_EQ(5, L(3, 5).Size<1>()); + } + { + using L = Layout; + EXPECT_EQ(3, L::Partial(3).Size<0>()); + EXPECT_EQ(3, L::Partial(3, 5).Size<0>()); + EXPECT_EQ(5, L::Partial(3, 5).Size<1>()); + EXPECT_EQ(3, L::Partial(3, 5, 7).Size<0>()); + EXPECT_EQ(5, L::Partial(3, 5, 7).Size<1>()); + EXPECT_EQ(7, L::Partial(3, 5, 7).Size<2>()); + EXPECT_EQ(3, L(3, 5, 7).Size<0>()); + EXPECT_EQ(5, L(3, 5, 7).Size<1>()); + EXPECT_EQ(7, L(3, 5, 7).Size<2>()); + } +} + +TEST(Layout, SizeByType) { + { + using L = Layout; + EXPECT_EQ(0, L::Partial(0).Size()); + EXPECT_EQ(3, L::Partial(3).Size()); + EXPECT_EQ(3, L(3).Size()); + } + { + using L = Layout; + EXPECT_EQ(3, L::Partial(3).Size()); + EXPECT_EQ(3, L::Partial(3, 5).Size()); + EXPECT_EQ(5, L::Partial(3, 5).Size()); + EXPECT_EQ(3, L::Partial(3, 5, 7).Size()); + EXPECT_EQ(5, L::Partial(3, 5, 7).Size()); + EXPECT_EQ(7, L::Partial(3, 5, 7).Size()); + EXPECT_EQ(3, L(3, 5, 7).Size()); + EXPECT_EQ(5, L(3, 5, 7).Size()); + EXPECT_EQ(7, L(3, 5, 7).Size()); + } +} + +TEST(Layout, Sizes) { + { + using L = Layout; + EXPECT_THAT(L::Partial().Sizes(), ElementsAre()); + EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3)); + EXPECT_THAT(L(3).Sizes(), ElementsAre(3)); + } + { + using L = Layout; + EXPECT_THAT(L::Partial().Sizes(), ElementsAre()); + EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3)); + EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5)); + EXPECT_THAT(L(3, 5).Sizes(), ElementsAre(3, 5)); + } + { + using L = Layout; + EXPECT_THAT(L::Partial().Sizes(), ElementsAre()); + EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3)); + EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5)); + EXPECT_THAT(L::Partial(3, 5, 7).Sizes(), ElementsAre(3, 5, 7)); + EXPECT_THAT(L(3, 5, 7).Sizes(), ElementsAre(3, 5, 7)); + } +} + +TEST(Layout, PointerByIndex) { + alignas(max_align_t) const unsigned char p[100] = {}; + { + using L = Layout; + EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L(3).Pointer<0>(p)))); + } + { + using L = Layout; + EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer<0>(p)))); + EXPECT_EQ(12, Distance(p, Type(L::Partial(3).Pointer<1>(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(3, 5).Pointer<0>(p)))); + EXPECT_EQ(12, + Distance(p, Type(L::Partial(3, 5).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L(3, 5).Pointer<0>(p)))); + EXPECT_EQ(12, Distance(p, Type(L(3, 5).Pointer<1>(p)))); + } + { + using L = Layout; + EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(1).Pointer<0>(p)))); + EXPECT_EQ(4, Distance(p, Type(L::Partial(1).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(5).Pointer<0>(p)))); + EXPECT_EQ(8, Distance(p, Type(L::Partial(5).Pointer<1>(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(0, 0).Pointer<0>(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(0, 0).Pointer<1>(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(0, 0).Pointer<2>(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(1, 0).Pointer<0>(p)))); + EXPECT_EQ(4, + Distance(p, Type(L::Partial(1, 0).Pointer<1>(p)))); + EXPECT_EQ(8, + Distance(p, Type(L::Partial(1, 0).Pointer<2>(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(5, 3).Pointer<0>(p)))); + EXPECT_EQ(8, + Distance(p, Type(L::Partial(5, 3).Pointer<1>(p)))); + EXPECT_EQ(24, + Distance(p, Type(L::Partial(5, 3).Pointer<2>(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<0>(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<1>(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<2>(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(1, 0, 0).Pointer<0>(p)))); + EXPECT_EQ( + 4, Distance(p, Type(L::Partial(1, 0, 0).Pointer<1>(p)))); + EXPECT_EQ( + 8, Distance(p, Type(L::Partial(1, 0, 0).Pointer<2>(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(5, 3, 1).Pointer<0>(p)))); + EXPECT_EQ( + 24, + Distance(p, Type(L::Partial(5, 3, 1).Pointer<2>(p)))); + EXPECT_EQ( + 8, Distance(p, Type(L::Partial(5, 3, 1).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L(5, 3, 1).Pointer<0>(p)))); + EXPECT_EQ(24, Distance(p, Type(L(5, 3, 1).Pointer<2>(p)))); + EXPECT_EQ(8, Distance(p, Type(L(5, 3, 1).Pointer<1>(p)))); + } +} + +TEST(Layout, PointerByType) { + alignas(max_align_t) const unsigned char p[100] = {}; + { + using L = Layout; + EXPECT_EQ(0, + Distance(p, Type(L::Partial().Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(3).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L(3).Pointer(p)))); + } + { + using L = Layout; + EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(0).Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(0).Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(1).Pointer(p)))); + EXPECT_EQ(4, + Distance(p, Type(L::Partial(1).Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(5).Pointer(p)))); + EXPECT_EQ(8, + Distance(p, Type(L::Partial(5).Pointer(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(0, 0).Pointer(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(0, 0).Pointer(p)))); + EXPECT_EQ( + 0, + Distance(p, Type(L::Partial(0, 0).Pointer(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(1, 0).Pointer(p)))); + EXPECT_EQ( + 4, Distance(p, Type(L::Partial(1, 0).Pointer(p)))); + EXPECT_EQ( + 8, + Distance(p, Type(L::Partial(1, 0).Pointer(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(5, 3).Pointer(p)))); + EXPECT_EQ( + 8, Distance(p, Type(L::Partial(5, 3).Pointer(p)))); + EXPECT_EQ( + 24, + Distance(p, Type(L::Partial(5, 3).Pointer(p)))); + EXPECT_EQ( + 0, + Distance(p, Type(L::Partial(0, 0, 0).Pointer(p)))); + EXPECT_EQ( + 0, + Distance(p, Type(L::Partial(0, 0, 0).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type( + L::Partial(0, 0, 0).Pointer(p)))); + EXPECT_EQ( + 0, + Distance(p, Type(L::Partial(1, 0, 0).Pointer(p)))); + EXPECT_EQ( + 4, + Distance(p, Type(L::Partial(1, 0, 0).Pointer(p)))); + EXPECT_EQ(8, Distance(p, Type( + L::Partial(1, 0, 0).Pointer(p)))); + EXPECT_EQ( + 0, + Distance(p, Type(L::Partial(5, 3, 1).Pointer(p)))); + EXPECT_EQ(24, Distance(p, Type( + L::Partial(5, 3, 1).Pointer(p)))); + EXPECT_EQ( + 8, + Distance(p, Type(L::Partial(5, 3, 1).Pointer(p)))); + EXPECT_EQ(24, + Distance(p, Type(L(5, 3, 1).Pointer(p)))); + EXPECT_EQ(8, Distance(p, Type(L(5, 3, 1).Pointer(p)))); + } +} + +TEST(Layout, MutablePointerByIndex) { + alignas(max_align_t) unsigned char p[100]; + { + using L = Layout; + EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L(3).Pointer<0>(p)))); + } + { + using L = Layout; + EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer<0>(p)))); + EXPECT_EQ(12, Distance(p, Type(L::Partial(3).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(3, 5).Pointer<0>(p)))); + EXPECT_EQ(12, Distance(p, Type(L::Partial(3, 5).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L(3, 5).Pointer<0>(p)))); + EXPECT_EQ(12, Distance(p, Type(L(3, 5).Pointer<1>(p)))); + } + { + using L = Layout; + EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(1).Pointer<0>(p)))); + EXPECT_EQ(4, Distance(p, Type(L::Partial(1).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(5).Pointer<0>(p)))); + EXPECT_EQ(8, Distance(p, Type(L::Partial(5).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer<2>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(1, 0).Pointer<0>(p)))); + EXPECT_EQ(4, Distance(p, Type(L::Partial(1, 0).Pointer<1>(p)))); + EXPECT_EQ(8, Distance(p, Type(L::Partial(1, 0).Pointer<2>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(5, 3).Pointer<0>(p)))); + EXPECT_EQ(8, Distance(p, Type(L::Partial(5, 3).Pointer<1>(p)))); + EXPECT_EQ(24, Distance(p, Type(L::Partial(5, 3).Pointer<2>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<0>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<2>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(1, 0, 0).Pointer<0>(p)))); + EXPECT_EQ(4, Distance(p, Type(L::Partial(1, 0, 0).Pointer<1>(p)))); + EXPECT_EQ(8, Distance(p, Type(L::Partial(1, 0, 0).Pointer<2>(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(5, 3, 1).Pointer<0>(p)))); + EXPECT_EQ(24, + Distance(p, Type(L::Partial(5, 3, 1).Pointer<2>(p)))); + EXPECT_EQ(8, Distance(p, Type(L::Partial(5, 3, 1).Pointer<1>(p)))); + EXPECT_EQ(0, Distance(p, Type(L(5, 3, 1).Pointer<0>(p)))); + EXPECT_EQ(24, Distance(p, Type(L(5, 3, 1).Pointer<2>(p)))); + EXPECT_EQ(8, Distance(p, Type(L(5, 3, 1).Pointer<1>(p)))); + } +} + +TEST(Layout, MutablePointerByType) { + alignas(max_align_t) unsigned char p[100]; + { + using L = Layout; + EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L(3).Pointer(p)))); + } + { + using L = Layout; + EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(1).Pointer(p)))); + EXPECT_EQ(4, Distance(p, Type(L::Partial(1).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(5).Pointer(p)))); + EXPECT_EQ(8, Distance(p, Type(L::Partial(5).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(0, 0).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(1, 0).Pointer(p)))); + EXPECT_EQ(4, Distance(p, Type(L::Partial(1, 0).Pointer(p)))); + EXPECT_EQ(8, + Distance(p, Type(L::Partial(1, 0).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L::Partial(5, 3).Pointer(p)))); + EXPECT_EQ(8, Distance(p, Type(L::Partial(5, 3).Pointer(p)))); + EXPECT_EQ(24, + Distance(p, Type(L::Partial(5, 3).Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(0, 0, 0).Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(0, 0, 0).Pointer(p)))); + EXPECT_EQ( + 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(1, 0, 0).Pointer(p)))); + EXPECT_EQ(4, + Distance(p, Type(L::Partial(1, 0, 0).Pointer(p)))); + EXPECT_EQ( + 8, Distance(p, Type(L::Partial(1, 0, 0).Pointer(p)))); + EXPECT_EQ(0, + Distance(p, Type(L::Partial(5, 3, 1).Pointer(p)))); + EXPECT_EQ( + 24, Distance(p, Type(L::Partial(5, 3, 1).Pointer(p)))); + EXPECT_EQ(8, + Distance(p, Type(L::Partial(5, 3, 1).Pointer(p)))); + EXPECT_EQ(0, Distance(p, Type(L(5, 3, 1).Pointer(p)))); + EXPECT_EQ(24, Distance(p, Type(L(5, 3, 1).Pointer(p)))); + EXPECT_EQ(8, Distance(p, Type(L(5, 3, 1).Pointer(p)))); + } +} + +TEST(Layout, Pointers) { + alignas(max_align_t) const unsigned char p[100] = {}; + using L = Layout; + { + const auto x = L::Partial(); + EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)), + Type>(x.Pointers(p))); + } + { + const auto x = L::Partial(1); + EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)), + (Type>(x.Pointers(p)))); + } + { + const auto x = L::Partial(1, 2); + EXPECT_EQ( + std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), + (Type>( + x.Pointers(p)))); + } + { + const auto x = L::Partial(1, 2, 3); + EXPECT_EQ( + std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), + (Type>( + x.Pointers(p)))); + } + { + const L x(1, 2, 3); + EXPECT_EQ( + std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), + (Type>( + x.Pointers(p)))); + } +} + +TEST(Layout, MutablePointers) { + alignas(max_align_t) unsigned char p[100]; + using L = Layout; + { + const auto x = L::Partial(); + EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)), + Type>(x.Pointers(p))); + } + { + const auto x = L::Partial(1); + EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)), + (Type>(x.Pointers(p)))); + } + { + const auto x = L::Partial(1, 2); + EXPECT_EQ( + std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), + (Type>(x.Pointers(p)))); + } + { + const auto x = L::Partial(1, 2, 3); + EXPECT_EQ( + std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), + (Type>(x.Pointers(p)))); + } + { + const L x(1, 2, 3); + EXPECT_EQ( + std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), + (Type>(x.Pointers(p)))); + } +} + +TEST(Layout, SliceByIndexSize) { + alignas(max_align_t) const unsigned char p[100] = {}; + { + using L = Layout; + EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size()); + EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); + EXPECT_EQ(3, L(3).Slice<0>(p).size()); + } + { + using L = Layout; + EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); + EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size()); + EXPECT_EQ(5, L(3, 5).Slice<1>(p).size()); + } + { + using L = Layout; + EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); + EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size()); + EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size()); + EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size()); + EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size()); + EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size()); + EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size()); + EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size()); + EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size()); + } +} + +TEST(Layout, SliceByTypeSize) { + alignas(max_align_t) const unsigned char p[100] = {}; + { + using L = Layout; + EXPECT_EQ(0, L::Partial(0).Slice(p).size()); + EXPECT_EQ(3, L::Partial(3).Slice(p).size()); + EXPECT_EQ(3, L(3).Slice(p).size()); + } + { + using L = Layout; + EXPECT_EQ(3, L::Partial(3).Slice(p).size()); + EXPECT_EQ(3, L::Partial(3, 5).Slice(p).size()); + EXPECT_EQ(5, L::Partial(3, 5).Slice(p).size()); + EXPECT_EQ(3, L::Partial(3, 5, 7).Slice(p).size()); + EXPECT_EQ(5, L::Partial(3, 5, 7).Slice(p).size()); + EXPECT_EQ(7, L::Partial(3, 5, 7).Slice(p).size()); + EXPECT_EQ(3, L(3, 5, 7).Slice(p).size()); + EXPECT_EQ(5, L(3, 5, 7).Slice(p).size()); + EXPECT_EQ(7, L(3, 5, 7).Slice(p).size()); + } +} + +TEST(Layout, MutableSliceByIndexSize) { + alignas(max_align_t) unsigned char p[100]; + { + using L = Layout; + EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size()); + EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); + EXPECT_EQ(3, L(3).Slice<0>(p).size()); + } + { + using L = Layout; + EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); + EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size()); + EXPECT_EQ(5, L(3, 5).Slice<1>(p).size()); + } + { + using L = Layout; + EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); + EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size()); + EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size()); + EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size()); + EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size()); + EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size()); + EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size()); + EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size()); + EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size()); + } +} + +TEST(Layout, MutableSliceByTypeSize) { + alignas(max_align_t) unsigned char p[100]; + { + using L = Layout; + EXPECT_EQ(0, L::Partial(0).Slice(p).size()); + EXPECT_EQ(3, L::Partial(3).Slice(p).size()); + EXPECT_EQ(3, L(3).Slice(p).size()); + } + { + using L = Layout; + EXPECT_EQ(3, L::Partial(3).Slice(p).size()); + EXPECT_EQ(3, L::Partial(3, 5).Slice(p).size()); + EXPECT_EQ(5, L::Partial(3, 5).Slice(p).size()); + EXPECT_EQ(3, L::Partial(3, 5, 7).Slice(p).size()); + EXPECT_EQ(5, L::Partial(3, 5, 7).Slice(p).size()); + EXPECT_EQ(7, L::Partial(3, 5, 7).Slice(p).size()); + EXPECT_EQ(3, L(3, 5, 7).Slice(p).size()); + EXPECT_EQ(5, L(3, 5, 7).Slice(p).size()); + EXPECT_EQ(7, L(3, 5, 7).Slice(p).size()); + } +} + +TEST(Layout, SliceByIndexData) { + alignas(max_align_t) const unsigned char p[100] = {}; + { + using L = Layout; + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(0).Slice<0>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(3).Slice<0>(p)).data())); + EXPECT_EQ(0, Distance(p, Type>(L(3).Slice<0>(p)).data())); + } + { + using L = Layout; + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(3).Slice<0>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, + Type>(L::Partial(3, 5).Slice<0>(p)).data())); + EXPECT_EQ( + 12, + Distance(p, + Type>(L::Partial(3, 5).Slice<1>(p)).data())); + EXPECT_EQ(0, + Distance(p, Type>(L(3, 5).Slice<0>(p)).data())); + EXPECT_EQ(12, + Distance(p, Type>(L(3, 5).Slice<1>(p)).data())); + } + { + using L = Layout; + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(0).Slice<0>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(1).Slice<0>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(5).Slice<0>(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(0, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, + Type>(L::Partial(0, 0).Slice<1>(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(1, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 4, + Distance(p, + Type>(L::Partial(1, 0).Slice<1>(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(5, 3).Slice<0>(p)).data())); + EXPECT_EQ( + 8, + Distance(p, + Type>(L::Partial(5, 3).Slice<1>(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, Type>(L::Partial(0, 0, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, + Type>(L::Partial(0, 0, 0).Slice<1>(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, + Type>(L::Partial(0, 0, 0).Slice<2>(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, Type>(L::Partial(1, 0, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 4, + Distance( + p, + Type>(L::Partial(1, 0, 0).Slice<1>(p)).data())); + EXPECT_EQ( + 8, + Distance( + p, + Type>(L::Partial(1, 0, 0).Slice<2>(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, Type>(L::Partial(5, 3, 1).Slice<0>(p)).data())); + EXPECT_EQ( + 24, + Distance( + p, + Type>(L::Partial(5, 3, 1).Slice<2>(p)).data())); + EXPECT_EQ( + 8, + Distance( + p, + Type>(L::Partial(5, 3, 1).Slice<1>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type>(L(5, 3, 1).Slice<0>(p)).data())); + EXPECT_EQ( + 24, + Distance(p, Type>(L(5, 3, 1).Slice<2>(p)).data())); + EXPECT_EQ( + 8, Distance(p, Type>(L(5, 3, 1).Slice<1>(p)).data())); + } +} + +TEST(Layout, SliceByTypeData) { + alignas(max_align_t) const unsigned char p[100] = {}; + { + using L = Layout; + EXPECT_EQ( + 0, + Distance( + p, Type>(L::Partial(0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, Type>(L::Partial(3).Slice(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type>(L(3).Slice(p)).data())); + } + { + using L = Layout; + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(0).Slice(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(1).Slice(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(5).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, Type>(L::Partial(0, 0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, + Type>(L::Partial(0, 0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, Type>(L::Partial(1, 0).Slice(p)).data())); + EXPECT_EQ( + 4, + Distance( + p, + Type>(L::Partial(1, 0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, Type>(L::Partial(5, 3).Slice(p)).data())); + EXPECT_EQ( + 8, + Distance( + p, + Type>(L::Partial(5, 3).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, + Type>(L::Partial(0, 0, 0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(0, 0, 0).Slice(p)) + .data())); + EXPECT_EQ(0, Distance(p, Type>( + L::Partial(0, 0, 0).Slice(p)) + .data())); + EXPECT_EQ( + 0, + Distance( + p, + Type>(L::Partial(1, 0, 0).Slice(p)).data())); + EXPECT_EQ( + 4, + Distance(p, Type>(L::Partial(1, 0, 0).Slice(p)) + .data())); + EXPECT_EQ(8, Distance(p, Type>( + L::Partial(1, 0, 0).Slice(p)) + .data())); + EXPECT_EQ( + 0, + Distance( + p, + Type>(L::Partial(5, 3, 1).Slice(p)).data())); + EXPECT_EQ(24, Distance(p, Type>( + L::Partial(5, 3, 1).Slice(p)) + .data())); + EXPECT_EQ( + 8, + Distance(p, Type>(L::Partial(5, 3, 1).Slice(p)) + .data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L(5, 3, 1).Slice(p)).data())); + EXPECT_EQ( + 24, + Distance(p, + Type>(L(5, 3, 1).Slice(p)).data())); + EXPECT_EQ( + 8, Distance( + p, Type>(L(5, 3, 1).Slice(p)).data())); + } +} + +TEST(Layout, MutableSliceByIndexData) { + alignas(max_align_t) unsigned char p[100]; + { + using L = Layout; + EXPECT_EQ(0, + Distance(p, Type>(L::Partial(0).Slice<0>(p)).data())); + EXPECT_EQ(0, + Distance(p, Type>(L::Partial(3).Slice<0>(p)).data())); + EXPECT_EQ(0, Distance(p, Type>(L(3).Slice<0>(p)).data())); + } + { + using L = Layout; + EXPECT_EQ(0, + Distance(p, Type>(L::Partial(3).Slice<0>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type>(L::Partial(3, 5).Slice<0>(p)).data())); + EXPECT_EQ( + 12, + Distance(p, Type>(L::Partial(3, 5).Slice<1>(p)).data())); + EXPECT_EQ(0, Distance(p, Type>(L(3, 5).Slice<0>(p)).data())); + EXPECT_EQ(12, Distance(p, Type>(L(3, 5).Slice<1>(p)).data())); + } + { + using L = Layout; + EXPECT_EQ(0, + Distance(p, Type>(L::Partial(0).Slice<0>(p)).data())); + EXPECT_EQ(0, + Distance(p, Type>(L::Partial(1).Slice<0>(p)).data())); + EXPECT_EQ(0, + Distance(p, Type>(L::Partial(5).Slice<0>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type>(L::Partial(0, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type>(L::Partial(0, 0).Slice<1>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type>(L::Partial(1, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 4, Distance(p, Type>(L::Partial(1, 0).Slice<1>(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type>(L::Partial(5, 3).Slice<0>(p)).data())); + EXPECT_EQ( + 8, Distance(p, Type>(L::Partial(5, 3).Slice<1>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(0, 0, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(0, 0, 0).Slice<1>(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(0, 0, 0).Slice<2>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(1, 0, 0).Slice<0>(p)).data())); + EXPECT_EQ( + 4, + Distance(p, Type>(L::Partial(1, 0, 0).Slice<1>(p)).data())); + EXPECT_EQ( + 8, Distance( + p, Type>(L::Partial(1, 0, 0).Slice<2>(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(5, 3, 1).Slice<0>(p)).data())); + EXPECT_EQ( + 24, Distance( + p, Type>(L::Partial(5, 3, 1).Slice<2>(p)).data())); + EXPECT_EQ( + 8, + Distance(p, Type>(L::Partial(5, 3, 1).Slice<1>(p)).data())); + EXPECT_EQ(0, Distance(p, Type>(L(5, 3, 1).Slice<0>(p)).data())); + EXPECT_EQ(24, + Distance(p, Type>(L(5, 3, 1).Slice<2>(p)).data())); + EXPECT_EQ(8, Distance(p, Type>(L(5, 3, 1).Slice<1>(p)).data())); + } +} + +TEST(Layout, MutableSliceByTypeData) { + alignas(max_align_t) unsigned char p[100]; + { + using L = Layout; + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(3).Slice(p)).data())); + EXPECT_EQ(0, Distance(p, Type>(L(3).Slice(p)).data())); + } + { + using L = Layout; + EXPECT_EQ( + 0, Distance(p, Type>(L::Partial(0).Slice(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type>(L::Partial(1).Slice(p)).data())); + EXPECT_EQ( + 0, Distance(p, Type>(L::Partial(5).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(0, 0).Slice(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(0, 0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(1, 0).Slice(p)).data())); + EXPECT_EQ( + 4, Distance( + p, Type>(L::Partial(1, 0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance(p, Type>(L::Partial(5, 3).Slice(p)).data())); + EXPECT_EQ( + 8, Distance( + p, Type>(L::Partial(5, 3).Slice(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(0, 0, 0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, Type>(L::Partial(0, 0, 0).Slice(p)).data())); + EXPECT_EQ( + 0, + Distance( + p, + Type>(L::Partial(0, 0, 0).Slice(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(1, 0, 0).Slice(p)).data())); + EXPECT_EQ( + 4, + Distance( + p, Type>(L::Partial(1, 0, 0).Slice(p)).data())); + EXPECT_EQ( + 8, + Distance( + p, + Type>(L::Partial(1, 0, 0).Slice(p)).data())); + EXPECT_EQ( + 0, Distance( + p, Type>(L::Partial(5, 3, 1).Slice(p)).data())); + EXPECT_EQ( + 24, + Distance( + p, + Type>(L::Partial(5, 3, 1).Slice(p)).data())); + EXPECT_EQ( + 8, + Distance( + p, Type>(L::Partial(5, 3, 1).Slice(p)).data())); + EXPECT_EQ(0, + Distance(p, Type>(L(5, 3, 1).Slice(p)).data())); + EXPECT_EQ( + 24, + Distance(p, Type>(L(5, 3, 1).Slice(p)).data())); + EXPECT_EQ( + 8, Distance(p, Type>(L(5, 3, 1).Slice(p)).data())); + } +} + +MATCHER_P(IsSameSlice, slice, "") { + return arg.size() == slice.size() && arg.data() == slice.data(); +} + +template +class TupleMatcher { + public: + explicit TupleMatcher(M... matchers) : matchers_(std::move(matchers)...) {} + + template + bool MatchAndExplain(const Tuple& p, + testing::MatchResultListener* /* listener */) const { + static_assert(std::tuple_size::value == sizeof...(M), ""); + return MatchAndExplainImpl( + p, absl::make_index_sequence::value>{}); + } + + // For the matcher concept. Left empty as we don't really need the diagnostics + // right now. + void DescribeTo(::std::ostream* os) const {} + void DescribeNegationTo(::std::ostream* os) const {} + + private: + template + bool MatchAndExplainImpl(const Tuple& p, absl::index_sequence) const { + // Using std::min as a simple variadic "and". + return std::min( + {true, testing::SafeMatcherCast< + const typename std::tuple_element::type&>( + std::get(matchers_)) + .Matches(std::get(p))...}); + } + + std::tuple matchers_; +}; + +template +testing::PolymorphicMatcher> Tuple(M... matchers) { + return testing::MakePolymorphicMatcher( + TupleMatcher(std::move(matchers)...)); +} + +TEST(Layout, Slices) { + alignas(max_align_t) const unsigned char p[100] = {}; + using L = Layout; + { + const auto x = L::Partial(); + EXPECT_THAT(Type>(x.Slices(p)), Tuple()); + } + { + const auto x = L::Partial(1); + EXPECT_THAT(Type>>(x.Slices(p)), + Tuple(IsSameSlice(x.Slice<0>(p)))); + } + { + const auto x = L::Partial(1, 2); + EXPECT_THAT( + (Type, Span>>(x.Slices(p))), + Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)))); + } + { + const auto x = L::Partial(1, 2, 3); + EXPECT_THAT((Type, Span, + Span>>(x.Slices(p))), + Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), + IsSameSlice(x.Slice<2>(p)))); + } + { + const L x(1, 2, 3); + EXPECT_THAT((Type, Span, + Span>>(x.Slices(p))), + Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), + IsSameSlice(x.Slice<2>(p)))); + } +} + +TEST(Layout, MutableSlices) { + alignas(max_align_t) unsigned char p[100] = {}; + using L = Layout; + { + const auto x = L::Partial(); + EXPECT_THAT(Type>(x.Slices(p)), Tuple()); + } + { + const auto x = L::Partial(1); + EXPECT_THAT(Type>>(x.Slices(p)), + Tuple(IsSameSlice(x.Slice<0>(p)))); + } + { + const auto x = L::Partial(1, 2); + EXPECT_THAT((Type, Span>>(x.Slices(p))), + Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)))); + } + { + const auto x = L::Partial(1, 2, 3); + EXPECT_THAT( + (Type, Span, Span>>(x.Slices(p))), + Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), + IsSameSlice(x.Slice<2>(p)))); + } + { + const L x(1, 2, 3); + EXPECT_THAT( + (Type, Span, Span>>(x.Slices(p))), + Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), + IsSameSlice(x.Slice<2>(p)))); + } +} + +TEST(Layout, UnalignedTypes) { + constexpr Layout x(1, 2, 3); + alignas(max_align_t) unsigned char p[x.AllocSize() + 1]; + EXPECT_THAT(x.Pointers(p + 1), Tuple(p + 1, p + 2, p + 4)); +} + +TEST(Layout, CustomAlignment) { + constexpr Layout> x(1, 2); + alignas(max_align_t) unsigned char p[x.AllocSize()]; + EXPECT_EQ(10, x.AllocSize()); + EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 8)); +} + +TEST(Layout, OverAligned) { + constexpr size_t M = alignof(max_align_t); + constexpr Layout> x(1, 3); + alignas(2 * M) unsigned char p[x.AllocSize()]; + EXPECT_EQ(2 * M + 3, x.AllocSize()); + EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 2 * M)); +} + +TEST(Layout, Alignment) { + static_assert(Layout::Alignment() == 1, ""); + static_assert(Layout::Alignment() == 4, ""); + 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, ""); +} + +TEST(Layout, ConstexprPartial) { + constexpr size_t M = alignof(max_align_t); + constexpr Layout> x(1, 3); + static_assert(x.Partial(1).template Offset<1>() == 2 * M, ""); +} +// [from, to) +struct Region { + size_t from; + size_t to; +}; + +void ExpectRegionPoisoned(const unsigned char* p, size_t n, bool poisoned) { +#ifdef ADDRESS_SANITIZER + for (size_t i = 0; i != n; ++i) { + EXPECT_EQ(poisoned, __asan_address_is_poisoned(p + i)); + } +#endif +} + +template +void ExpectPoisoned(const unsigned char (&buf)[N], + std::initializer_list reg) { + size_t prev = 0; + for (const Region& r : reg) { + ExpectRegionPoisoned(buf + prev, r.from - prev, false); + ExpectRegionPoisoned(buf + r.from, r.to - r.from, true); + prev = r.to; + } + ExpectRegionPoisoned(buf + prev, N - prev, false); +} + +TEST(Layout, PoisonPadding) { + using L = Layout; + + constexpr size_t n = L::Partial(1, 2, 3, 4).AllocSize(); + { + constexpr auto x = L::Partial(); + alignas(max_align_t) const unsigned char c[n] = {}; + x.PoisonPadding(c); + EXPECT_EQ(x.Slices(c), x.Slices(c)); + ExpectPoisoned(c, {}); + } + { + constexpr auto x = L::Partial(1); + alignas(max_align_t) const unsigned char c[n] = {}; + x.PoisonPadding(c); + EXPECT_EQ(x.Slices(c), x.Slices(c)); + ExpectPoisoned(c, {{1, 8}}); + } + { + constexpr auto x = L::Partial(1, 2); + alignas(max_align_t) const unsigned char c[n] = {}; + x.PoisonPadding(c); + EXPECT_EQ(x.Slices(c), x.Slices(c)); + ExpectPoisoned(c, {{1, 8}}); + } + { + constexpr auto x = L::Partial(1, 2, 3); + alignas(max_align_t) const unsigned char c[n] = {}; + x.PoisonPadding(c); + EXPECT_EQ(x.Slices(c), x.Slices(c)); + ExpectPoisoned(c, {{1, 8}, {36, 40}}); + } + { + constexpr auto x = L::Partial(1, 2, 3, 4); + alignas(max_align_t) const unsigned char c[n] = {}; + x.PoisonPadding(c); + EXPECT_EQ(x.Slices(c), x.Slices(c)); + ExpectPoisoned(c, {{1, 8}, {36, 40}}); + } + { + constexpr L x(1, 2, 3, 4); + alignas(max_align_t) const unsigned char c[n] = {}; + x.PoisonPadding(c); + EXPECT_EQ(x.Slices(c), x.Slices(c)); + ExpectPoisoned(c, {{1, 8}, {36, 40}}); + } +} + +TEST(Layout, DebugString) { + { + constexpr auto x = Layout::Partial(); + EXPECT_EQ("@0(1)", x.DebugString()); + } + { + constexpr auto x = Layout::Partial(1); + EXPECT_EQ("@0(1)[1]; @4(4)", x.DebugString()); + } + { + constexpr auto x = Layout::Partial(1, 2); + EXPECT_EQ("@0(1)[1]; @4(4)[2]; @12(1)", + x.DebugString()); + } + { + constexpr auto x = Layout::Partial(1, 2, 3); + EXPECT_EQ( + "@0(1)[1]; @4(4)[2]; @12(1)[3]; " + "@16" + + Int128::Name() + "(16)", + x.DebugString()); + } + { + constexpr auto x = Layout::Partial(1, 2, 3, 4); + EXPECT_EQ( + "@0(1)[1]; @4(4)[2]; @12(1)[3]; " + "@16" + + Int128::Name() + "(16)[4]", + x.DebugString()); + } + { + constexpr Layout x(1, 2, 3, 4); + EXPECT_EQ( + "@0(1)[1]; @4(4)[2]; @12(1)[3]; " + "@16" + + Int128::Name() + "(16)[4]", + x.DebugString()); + } +} + +TEST(Layout, CharTypes) { + constexpr Layout x(1); + alignas(max_align_t) char c[x.AllocSize()] = {}; + alignas(max_align_t) unsigned char uc[x.AllocSize()] = {}; + alignas(max_align_t) signed char sc[x.AllocSize()] = {}; + alignas(max_align_t) const char cc[x.AllocSize()] = {}; + alignas(max_align_t) const unsigned char cuc[x.AllocSize()] = {}; + alignas(max_align_t) const signed char csc[x.AllocSize()] = {}; + + Type(x.Pointer<0>(c)); + Type(x.Pointer<0>(uc)); + Type(x.Pointer<0>(sc)); + Type(x.Pointer<0>(cc)); + Type(x.Pointer<0>(cuc)); + Type(x.Pointer<0>(csc)); + + Type(x.Pointer(c)); + Type(x.Pointer(uc)); + Type(x.Pointer(sc)); + Type(x.Pointer(cc)); + Type(x.Pointer(cuc)); + Type(x.Pointer(csc)); + + Type>(x.Pointers(c)); + Type>(x.Pointers(uc)); + Type>(x.Pointers(sc)); + Type>(x.Pointers(cc)); + Type>(x.Pointers(cuc)); + Type>(x.Pointers(csc)); + + Type>(x.Slice<0>(c)); + Type>(x.Slice<0>(uc)); + Type>(x.Slice<0>(sc)); + Type>(x.Slice<0>(cc)); + Type>(x.Slice<0>(cuc)); + Type>(x.Slice<0>(csc)); + + Type>>(x.Slices(c)); + Type>>(x.Slices(uc)); + Type>>(x.Slices(sc)); + Type>>(x.Slices(cc)); + Type>>(x.Slices(cuc)); + Type>>(x.Slices(csc)); +} + +TEST(Layout, ConstElementType) { + constexpr Layout x(1); + alignas(int32_t) char c[x.AllocSize()] = {}; + const char* cc = c; + const int32_t* p = reinterpret_cast(cc); + + EXPECT_EQ(alignof(int32_t), x.Alignment()); + + EXPECT_EQ(0, x.Offset<0>()); + EXPECT_EQ(0, x.Offset()); + + EXPECT_THAT(x.Offsets(), ElementsAre(0)); + + EXPECT_EQ(1, x.Size<0>()); + EXPECT_EQ(1, x.Size()); + + EXPECT_THAT(x.Sizes(), ElementsAre(1)); + + EXPECT_EQ(sizeof(int32_t), x.AllocSize()); + + EXPECT_EQ(p, Type(x.Pointer<0>(c))); + EXPECT_EQ(p, Type(x.Pointer<0>(cc))); + + EXPECT_EQ(p, Type(x.Pointer(c))); + EXPECT_EQ(p, Type(x.Pointer(cc))); + + EXPECT_THAT(Type>(x.Pointers(c)), Tuple(p)); + EXPECT_THAT(Type>(x.Pointers(cc)), Tuple(p)); + + EXPECT_THAT(Type>(x.Slice<0>(c)), + IsSameSlice(Span(p, 1))); + EXPECT_THAT(Type>(x.Slice<0>(cc)), + IsSameSlice(Span(p, 1))); + + EXPECT_THAT(Type>(x.Slice(c)), + IsSameSlice(Span(p, 1))); + EXPECT_THAT(Type>(x.Slice(cc)), + IsSameSlice(Span(p, 1))); + + EXPECT_THAT(Type>>(x.Slices(c)), + Tuple(IsSameSlice(Span(p, 1)))); + EXPECT_THAT(Type>>(x.Slices(cc)), + Tuple(IsSameSlice(Span(p, 1)))); +} + +namespace example { + +// Immutable move-only string with sizeof equal to sizeof(void*). The string +// size and the characters are kept in the same heap allocation. +class CompactString { + public: + CompactString(const char* s = "") { // NOLINT + const size_t size = strlen(s); + // size_t[1], followed by char[size + 1]. + // This statement doesn't allocate memory. + const L layout(1, size + 1); + // AllocSize() tells us how much memory we need to allocate for all our + // data. + p_.reset(new unsigned char[layout.AllocSize()]); + // If running under ASAN, mark the padding bytes, if any, to catch memory + // errors. + layout.PoisonPadding(p_.get()); + // Store the size in the allocation. + // Pointer() is a synonym for Pointer<0>(). + *layout.Pointer(p_.get()) = size; + // Store the characters in the allocation. + memcpy(layout.Pointer(p_.get()), s, size + 1); + } + + size_t size() const { + // Equivalent to reinterpret_cast(*p). + return *L::Partial().Pointer(p_.get()); + } + + const char* c_str() const { + // Equivalent to reinterpret_cast(p.get() + sizeof(size_t)). + // The argument in Partial(1) specifies that we have size_t[1] in front of + // the characters. + return L::Partial(1).Pointer(p_.get()); + } + + private: + // Our heap allocation contains a size_t followed by an array of chars. + using L = Layout; + std::unique_ptr p_; +}; + +TEST(CompactString, Works) { + CompactString s = "hello"; + EXPECT_EQ(5, s.size()); + EXPECT_STREQ("hello", s.c_str()); +} + +} // namespace example + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/node_hash_policy.h b/absl/container/internal/node_hash_policy.h new file mode 100644 index 00000000000..e8d89f63fac --- /dev/null +++ b/absl/container/internal/node_hash_policy.h @@ -0,0 +1,90 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Adapts a policy for nodes. +// +// The node policy should model: +// +// struct Policy { +// // Returns a new node allocated and constructed using the allocator, using +// // the specified arguments. +// template +// value_type* new_element(Alloc* alloc, Args&&... args) const; +// +// // Destroys and deallocates node using the allocator. +// template +// void delete_element(Alloc* alloc, value_type* node) const; +// }; +// +// It may also optionally define `value()` and `apply()`. For documentation on +// these, see hash_policy_traits.h. + +#ifndef ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ +#define ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ + +#include +#include +#include +#include +#include + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { + +template +struct node_hash_policy { + static_assert(std::is_lvalue_reference::value, ""); + + using slot_type = typename std::remove_cv< + typename std::remove_reference::type>::type*; + + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + *slot = Policy::new_element(alloc, std::forward(args)...); + } + + template + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::delete_element(alloc, *slot); + } + + template + static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) { + *new_slot = *old_slot; + } + + static size_t space_used(const slot_type* slot) { + if (slot == nullptr) return Policy::element_space_used(nullptr); + return Policy::element_space_used(*slot); + } + + static Reference element(slot_type* slot) { return **slot; } + + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + + template + static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward(ts)...)) { + return P::apply(std::forward(ts)...); + } +}; + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..a73c7bbafc4 --- /dev/null +++ b/absl/container/internal/node_hash_policy_test.cc @@ -0,0 +1,69 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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/node_hash_policy.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/hash_policy_traits.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +using ::testing::Pointee; + +struct Policy : node_hash_policy { + using key_type = int; + using init_type = int; + + template + static int* new_element(Alloc* alloc, int value) { + return new int(value); + } + + template + static void delete_element(Alloc* alloc, int* elem) { + delete elem; + } +}; + +using NodePolicy = hash_policy_traits; + +struct NodeTest : ::testing::Test { + std::allocator alloc; + int n = 53; + int* a = &n; +}; + +TEST_F(NodeTest, ConstructDestroy) { + NodePolicy::construct(&alloc, &a, 42); + EXPECT_THAT(a, Pointee(42)); + NodePolicy::destroy(&alloc, &a); +} + +TEST_F(NodeTest, transfer) { + int s = 42; + int* b = &s; + NodePolicy::transfer(&alloc, &a, &b); + EXPECT_EQ(&s, a); +} + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h new file mode 100644 index 00000000000..53d4619abd6 --- /dev/null +++ b/absl/container/internal/raw_hash_map.h @@ -0,0 +1,187 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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_RAW_HASH_MAP_H_ +#define ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ + +#include +#include +#include + +#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 { +namespace container_internal { + +template +class raw_hash_map : public raw_hash_set { + // P is Policy. It's passed as a template argument to support maps that have + // incomplete types as values, as in unordered_map. + // MappedReference<> may be a non-reference type. + template + using MappedReference = decltype(P::value( + std::addressof(std::declval()))); + + // MappedConstReference<> may be a non-reference type. + template + using MappedConstReference = decltype(P::value( + std::addressof(std::declval()))); + + using KeyArgImpl = container_internal::KeyArg::value && + IsTransparent::value>; + + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + template + using key_arg = typename KeyArgImpl::template type; + + static_assert(!std::is_reference::value, ""); + // TODO(alkis): remove this assertion and verify that reference mapped_type is + // supported. + static_assert(!std::is_reference::value, ""); + + using iterator = typename raw_hash_map::raw_hash_set::iterator; + using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator; + + raw_hash_map() {} + using raw_hash_map::raw_hash_set::raw_hash_set; + + // The last two template parameters ensure that both arguments are rvalues + // (lvalue arguments are handled by the overloads below). This is necessary + // for supporting bitfield arguments. + // + // union { int n : 1; }; + // flat_hash_map m; + // m.insert_or_assign(n, n); + template + std::pair insert_or_assign(key_arg&& k, V&& v) { + return insert_or_assign_impl(std::forward(k), std::forward(v)); + } + + template + std::pair insert_or_assign(key_arg&& k, const V& v) { + return insert_or_assign_impl(std::forward(k), v); + } + + template + std::pair insert_or_assign(const key_arg& k, V&& v) { + return insert_or_assign_impl(k, std::forward(v)); + } + + template + std::pair insert_or_assign(const key_arg& k, const V& v) { + return insert_or_assign_impl(k, v); + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, V&& v) { + return insert_or_assign(std::forward(k), std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, key_arg&& k, const V& v) { + return insert_or_assign(std::forward(k), v).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, V&& v) { + return insert_or_assign(k, std::forward(v)).first; + } + + template + iterator insert_or_assign(const_iterator, const key_arg& k, const V& v) { + return insert_or_assign(k, v).first; + } + + template ::value, int>::type = 0, + K* = nullptr> + std::pair try_emplace(key_arg&& k, Args&&... args) { + return try_emplace_impl(std::forward(k), std::forward(args)...); + } + + template ::value, int>::type = 0> + std::pair try_emplace(const key_arg& k, Args&&... args) { + return try_emplace_impl(k, std::forward(args)...); + } + + template + iterator try_emplace(const_iterator, key_arg&& k, Args&&... args) { + return try_emplace(std::forward(k), std::forward(args)...).first; + } + + template + iterator try_emplace(const_iterator, const key_arg& k, Args&&... args) { + return try_emplace(k, std::forward(args)...).first; + } + + template + MappedReference

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

at(const key_arg& key) const { + auto it = this->find(key); + if (it == this->end()) std::abort(); + return Policy::value(&*it); + } + + template + MappedReference

operator[](key_arg&& key) { + return Policy::value(&*try_emplace(std::forward(key)).first); + } + + template + MappedReference

operator[](const key_arg& key) { + return Policy::value(&*try_emplace(key).first); + } + + private: + template + std::pair insert_or_assign_impl(K&& k, V&& v) { + auto res = this->find_or_prepare_insert(k); + if (res.second) + this->emplace_at(res.first, std::forward(k), std::forward(v)); + else + Policy::value(&*this->iterator_at(res.first)) = std::forward(v); + return {this->iterator_at(res.first), res.second}; + } + + template + std::pair try_emplace_impl(K&& k, Args&&... args) { + auto res = this->find_or_prepare_insert(k); + if (res.second) + this->emplace_at(res.first, std::piecewise_construct, + std::forward_as_tuple(std::forward(k)), + std::forward_as_tuple(std::forward(args)...)); + return {this->iterator_at(res.first), res.second}; + } +}; + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..4e690dacb21 --- /dev/null +++ b/absl/container/internal/raw_hash_set.cc @@ -0,0 +1,48 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/raw_hash_set.h" + +#include +#include + +#include "absl/base/config.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { + +constexpr size_t Group::kWidth; + +// Returns "random" seed. +inline size_t RandomSeed() { +#if ABSL_HAVE_THREAD_LOCAL + static thread_local size_t counter = 0; + size_t value = ++counter; +#else // ABSL_HAVE_THREAD_LOCAL + static std::atomic counter(0); + size_t value = counter.fetch_add(1, std::memory_order_relaxed); +#endif // ABSL_HAVE_THREAD_LOCAL + return value ^ static_cast(reinterpret_cast(&counter)); +} + +bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) { + // To avoid problems with weak hashes and single bit tests, we use % 13. + // TODO(kfm,sbenza): revisit after we do unconditional mixing + return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6; +} + +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h new file mode 100644 index 00000000000..0c42e4ae13a --- /dev/null +++ b/absl/container/internal/raw_hash_set.h @@ -0,0 +1,1950 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// An open-addressing +// hashtable with quadratic probing. +// +// This is a low level hashtable on top of which different interfaces can be +// implemented, like flat_hash_set, node_hash_set, string_hash_set, etc. +// +// The table interface is similar to that of std::unordered_set. Notable +// differences are that most member functions support heterogeneous keys when +// BOTH the hash and eq functions are marked as transparent. They do so by +// providing a typedef called `is_transparent`. +// +// When heterogeneous lookup is enabled, functions that take key_type act as if +// they have an overload set like: +// +// iterator find(const key_type& key); +// template +// iterator find(const K& key); +// +// size_type erase(const key_type& key); +// template +// size_type erase(const K& key); +// +// std::pair equal_range(const key_type& key); +// template +// std::pair equal_range(const K& key); +// +// When heterogeneous lookup is disabled, only the explicit `key_type` overloads +// exist. +// +// find() also supports passing the hash explicitly: +// +// iterator find(const key_type& key, size_t hash); +// template +// iterator find(const U& key, size_t hash); +// +// In addition the pointer to element and iterator stability guarantees are +// weaker: all iterators and pointers are invalidated after a new element is +// inserted. +// +// IMPLEMENTATION DETAILS +// +// The table stores elements inline in a slot array. In addition to the slot +// array the table maintains some control state per slot. The extra state is one +// byte per slot and stores empty or deleted marks, or alternatively 7 bits from +// the hash of an occupied slot. The table is split into logical groups of +// slots, like so: +// +// Group 1 Group 2 Group 3 +// +---------------+---------------+---------------+ +// | | | | | | | | | | | | | | | | | | | | | | | | | +// +---------------+---------------+---------------+ +// +// On lookup the hash is split into two parts: +// - H2: 7 bits (those stored in the control bytes) +// - H1: the rest of the bits +// The groups are probed using H1. For each group the slots are matched to H2 in +// parallel. Because H2 is 7 bits (128 states) and the number of slots per group +// is low (8 or 16) in almost all cases a match in H2 is also a lookup hit. +// +// On insert, once the right group is found (as in lookup), its slots are +// filled in order. +// +// On erase a slot is cleared. In case the group did not have any empty slots +// before the erase, the erased slot is marked as deleted. +// +// Groups without empty slots (but maybe with deleted slots) extend the probe +// sequence. The probing algorithm is quadratic. Given N the number of groups, +// the probing function for the i'th probe is: +// +// P(0) = H1 % N +// +// P(i) = (P(i - 1) + i) % N +// +// This probing function guarantees that after N probes, all the groups of the +// table will be probed exactly once. + +#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 +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/internal/bits.h" +#include "absl/base/internal/endian.h" +#include "absl/base/port.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/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 { +namespace container_internal { + +template +class probe_seq { + public: + probe_seq(size_t hash, size_t mask) { + assert(((mask + 1) & mask) == 0 && "not a mask"); + mask_ = mask; + offset_ = hash & mask_; + } + size_t offset() const { return offset_; } + size_t offset(size_t i) const { return (offset_ + i) & mask_; } + + void next() { + index_ += Width; + offset_ += index_; + offset_ &= mask_; + } + // 0-based probe index. The i-th probe in the probe sequence. + size_t index() const { return index_; } + + private: + size_t mask_; + size_t offset_; + size_t index_ = 0; +}; + +template +struct RequireUsableKey { + template + std::pair< + decltype(std::declval()(std::declval())), + decltype(std::declval()(std::declval(), + std::declval()))>* + operator()(const PassedKey&, const Args&...) const; +}; + +template +struct IsDecomposable : std::false_type {}; + +template +struct IsDecomposable< + absl::void_t(), + 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() { + using std::swap; + return noexcept(swap(std::declval(), std::declval())); +} + +template +int TrailingZeros(T x) { + return sizeof(T) == 8 ? base_internal::CountTrailingZerosNonZero64( + static_cast(x)) + : base_internal::CountTrailingZerosNonZero32( + static_cast(x)); +} + +template +int LeadingZeros(T x) { + return sizeof(T) == 8 + ? base_internal::CountLeadingZeros64(static_cast(x)) + : base_internal::CountLeadingZeros32(static_cast(x)); +} + +// An abstraction over a bitmask. It provides an easy way to iterate through the +// indexes of the set bits of a bitmask. When Shift=0 (platforms with SSE), +// this is a true bitmask. On non-SSE, platforms the arithematic used to +// emulate the SSE behavior works in bytes (Shift=3) and leaves each bytes as +// either 0x00 or 0x80. +// +// For example: +// for (int i : BitMask(0x5)) -> yields 0, 2 +// for (int i : BitMask(0x0000000080800000)) -> yields 2, 3 +template +class BitMask { + static_assert(std::is_unsigned::value, ""); + static_assert(Shift == 0 || Shift == 3, ""); + + public: + // These are useful for unit tests (gunit). + using value_type = int; + using iterator = BitMask; + using const_iterator = BitMask; + + explicit BitMask(T mask) : mask_(mask) {} + BitMask& operator++() { + mask_ &= (mask_ - 1); + return *this; + } + explicit operator bool() const { return mask_ != 0; } + int operator*() const { return LowestBitSet(); } + int LowestBitSet() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + int HighestBitSet() const { + return (sizeof(T) * CHAR_BIT - container_internal::LeadingZeros(mask_) - + 1) >> + Shift; + } + + BitMask begin() const { return *this; } + BitMask end() const { return BitMask(0); } + + int TrailingZeros() const { + return container_internal::TrailingZeros(mask_) >> Shift; + } + + int LeadingZeros() const { + constexpr int total_significant_bits = SignificantBits << Shift; + constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits; + return container_internal::LeadingZeros(mask_ << extra_bits) >> Shift; + } + + private: + friend bool operator==(const BitMask& a, const BitMask& b) { + return a.mask_ == b.mask_; + } + friend bool operator!=(const BitMask& a, const BitMask& b) { + return a.mask_ != b.mask_; + } + + T mask_; +}; + +using ctrl_t = signed char; +using h2_t = uint8_t; + +// The values here are selected for maximum performance. See the static asserts +// below for details. +enum Ctrl : ctrl_t { + kEmpty = -128, // 0b10000000 + kDeleted = -2, // 0b11111110 + kSentinel = -1, // 0b11111111 +}; +static_assert( + kEmpty & kDeleted & kSentinel & 0x80, + "Special markers need to have the MSB to make checking for them efficient"); +static_assert(kEmpty < kSentinel && kDeleted < kSentinel, + "kEmpty and kDeleted must be smaller than kSentinel to make the " + "SIMD test of IsEmptyOrDeleted() efficient"); +static_assert(kSentinel == -1, + "kSentinel must be -1 to elide loading it from memory into SIMD " + "registers (pcmpeqd xmm, xmm)"); +static_assert(kEmpty == -128, + "kEmpty must be -128 to make the SIMD check for its " + "existence efficient (psignb xmm, xmm)"); +static_assert(~kEmpty & ~kDeleted & kSentinel & 0x7F, + "kEmpty and kDeleted must share an unset bit that is not shared " + "by kSentinel to make the scalar test for MatchEmptyOrDeleted() " + "efficient"); +static_assert(kDeleted == -2, + "kDeleted must be -2 to make the implementation of " + "ConvertSpecialToEmptyAndFullToDeleted efficient"); + +// A single block of empty control bytes for tables without any slots allocated. +// This enables removing a branch in the hot path of find(). +inline ctrl_t* EmptyGroup() { + alignas(16) static constexpr ctrl_t empty_group[] = { + kSentinel, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, + kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty}; + return const_cast(empty_group); +} + +// Mixes a randomly generated per-process seed with `hash` and `ctrl` to +// randomize insertion order within groups. +bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl); + +// Returns a hash seed. +// +// The seed consists of the ctrl_ pointer, which adds enough entropy to ensure +// non-determinism of iteration order in most cases. +inline size_t HashSeed(const ctrl_t* ctrl) { + // The low bits of the pointer have little or no entropy because of + // alignment. We shift the pointer to try to use higher entropy bits. A + // good number seems to be 12 bits, because that aligns with page size. + return reinterpret_cast(ctrl) >> 12; +} + +inline size_t H1(size_t hash, const ctrl_t* ctrl) { + return (hash >> 7) ^ HashSeed(ctrl); +} +inline ctrl_t H2(size_t hash) { return hash & 0x7F; } + +inline bool IsEmpty(ctrl_t c) { return c == kEmpty; } +inline bool IsFull(ctrl_t c) { return c >= 0; } +inline bool IsDeleted(ctrl_t c) { return c == kDeleted; } +inline bool IsEmptyOrDeleted(ctrl_t c) { return c < kSentinel; } + +#if SWISSTABLE_HAVE_SSE2 + +// https://github.com/abseil/abseil-cpp/issues/209 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853 +// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char +// Work around this by using the portable implementation of Group +// when using -funsigned-char under GCC. +inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) { +#if defined(__GNUC__) && !defined(__clang__) + if (std::is_unsigned::value) { + const __m128i mask = _mm_set1_epi8(0x80); + const __m128i diff = _mm_subs_epi8(b, a); + return _mm_cmpeq_epi8(_mm_and_si128(diff, mask), mask); + } +#endif + return _mm_cmpgt_epi8(a, b); +} + +struct GroupSse2Impl { + static constexpr size_t kWidth = 16; // the number of slots per group + + explicit GroupSse2Impl(const ctrl_t* pos) { + ctrl = _mm_loadu_si128(reinterpret_cast(pos)); + } + + // Returns a bitmask representing the positions of slots that match hash. + BitMask Match(h2_t hash) const { + auto match = _mm_set1_epi8(hash); + return BitMask( + _mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))); + } + + // Returns a bitmask representing the positions of empty slots. + BitMask MatchEmpty() const { +#if SWISSTABLE_HAVE_SSSE3 + // This only works because kEmpty is -128. + return BitMask( + _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))); +#else + return Match(kEmpty); +#endif + } + + // Returns a bitmask representing the positions of empty or deleted slots. + BitMask MatchEmptyOrDeleted() const { + auto special = _mm_set1_epi8(kSentinel); + return BitMask( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl))); + } + + // Returns the number of trailing empty or deleted elements in the group. + uint32_t CountLeadingEmptyOrDeleted() const { + auto special = _mm_set1_epi8(kSentinel); + return TrailingZeros( + _mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1); + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + auto msbs = _mm_set1_epi8(static_cast(-128)); + auto x126 = _mm_set1_epi8(126); +#if SWISSTABLE_HAVE_SSSE3 + auto res = _mm_or_si128(_mm_shuffle_epi8(x126, ctrl), msbs); +#else + auto zero = _mm_setzero_si128(); + auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl); + auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126)); +#endif + _mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res); + } + + __m128i ctrl; +}; +#endif // SWISSTABLE_HAVE_SSE2 + +struct GroupPortableImpl { + static constexpr size_t kWidth = 8; + + explicit GroupPortableImpl(const ctrl_t* pos) + : ctrl(little_endian::Load64(pos)) {} + + BitMask Match(h2_t hash) const { + // For the technique, see: + // http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord + // (Determine if a word has a byte equal to n). + // + // Caveat: there are false positives but: + // - they only occur if there is a real match + // - they never occur on kEmpty, kDeleted, kSentinel + // - they will be handled gracefully by subsequent checks in code + // + // Example: + // v = 0x1716151413121110 + // hash = 0x12 + // retval = (v - lsbs) & ~v & msbs = 0x0000000080800000 + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl ^ (lsbs * hash); + return BitMask((x - lsbs) & ~x & msbs); + } + + BitMask MatchEmpty() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask((ctrl & (~ctrl << 6)) & msbs); + } + + BitMask MatchEmptyOrDeleted() const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + return BitMask((ctrl & (~ctrl << 7)) & msbs); + } + + uint32_t CountLeadingEmptyOrDeleted() const { + constexpr uint64_t gaps = 0x00FEFEFEFEFEFEFEULL; + return (TrailingZeros(((~ctrl & (ctrl >> 7)) | gaps) + 1) + 7) >> 3; + } + + void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const { + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl & msbs; + auto res = (~x + (x >> 7)) & ~lsbs; + little_endian::Store64(dst, res); + } + + uint64_t ctrl; +}; + +#if SWISSTABLE_HAVE_SSE2 +using Group = GroupSse2Impl; +#else +using Group = GroupPortableImpl; +#endif + +template +class raw_hash_set; + +inline bool IsValidCapacity(size_t n) { + return ((n + 1) & n) == 0 && n >= Group::kWidth - 1; +} + +// PRECONDITION: +// IsValidCapacity(capacity) +// ctrl[capacity] == kSentinel +// ctrl[i] != kSentinel for all i < capacity +// Applies mapping for every byte in ctrl: +// DELETED -> EMPTY +// EMPTY -> EMPTY +// FULL -> DELETED +inline void ConvertDeletedToEmptyAndFullToDeleted( + ctrl_t* ctrl, size_t capacity) { + assert(ctrl[capacity] == kSentinel); + assert(IsValidCapacity(capacity)); + for (ctrl_t* pos = ctrl; pos != ctrl + capacity + 1; pos += Group::kWidth) { + Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos); + } + // Copy the cloned ctrl bytes. + std::memcpy(ctrl + capacity + 1, ctrl, Group::kWidth); + ctrl[capacity] = kSentinel; +} + +// Rounds up the capacity to the next power of 2 minus 1 and ensures it is +// greater or equal to Group::kWidth - 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); +} + +// 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())); + } + + 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; +}; + +// Policy: a policy defines how to perform different operations on +// the slots of the hashtable (see hash_policy_traits.h for the full interface +// of policy). +// +// Hash: a (possibly polymorphic) functor that hashes keys of the hashtable. The +// functor should accept a key and return size_t as hash. For best performance +// it is important that the hash function provides high entropy across all bits +// of the hash. +// +// Eq: a (possibly polymorphic) functor that compares two keys for equality. It +// should accept two (of possibly different type) keys and return a bool: true +// 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 +// 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>; + + public: + using init_type = typename PolicyTraits::init_type; + using key_type = typename PolicyTraits::key_type; + // TODO(sbenza): Hide slot_type as it is an implementation detail. Needs user + // code fixes! + using slot_type = typename PolicyTraits::slot_type; + using allocator_type = Alloc; + using size_type = size_t; + using difference_type = ptrdiff_t; + using hasher = Hash; + using key_equal = Eq; + using policy_type = Policy; + using value_type = typename PolicyTraits::value_type; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename absl::allocator_traits< + allocator_type>::template rebind_traits::pointer; + using const_pointer = typename absl::allocator_traits< + allocator_type>::template rebind_traits::const_pointer; + + // Alias used for heterogeneous lookup functions. + // `key_arg` evaluates to `K` when the functors are transparent and to + // `key_type` otherwise. It permits template argument deduction on `K` for the + // transparent case. + template + using key_arg = typename KeyArgImpl::template type; + + private: + // Give an early error when key_type is not hashable/eq. + auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k)); + auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k)); + + using Layout = absl::container_internal::Layout; + + static Layout MakeLayout(size_t capacity) { + assert(IsValidCapacity(capacity)); + return Layout(capacity + Group::kWidth + 1, capacity); + } + + using AllocTraits = absl::allocator_traits; + using SlotAlloc = typename absl::allocator_traits< + allocator_type>::template rebind_alloc; + using SlotAllocTraits = typename absl::allocator_traits< + allocator_type>::template rebind_traits; + + static_assert(std::is_lvalue_reference::value, + "Policy::element() must return a reference"); + + template + struct SameAsElementReference + : std::is_same::type>::type, + typename std::remove_cv< + typename std::remove_reference::type>::type> {}; + + // An enabler for insert(T&&): T must be convertible to init_type or be the + // same as [cv] value_type [ref]. + // Note: we separate SameAsElementReference into its own type to avoid using + // reference unless we need to. MSVC doesn't seem to like it in some + // cases. + template + using RequiresInsertable = typename std::enable_if< + absl::disjunction, + SameAsElementReference>::value, + int>::type; + + // RequiresNotInit is a workaround for gcc prior to 7.1. + // See https://godbolt.org/g/Y4xsUh. + template + using RequiresNotInit = + typename std::enable_if::value, int>::type; + + template + using IsDecomposable = IsDecomposable; + + public: + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + static_assert(std::is_same::value, + "Allocators with custom pointer types are not supported"); + + class iterator { + friend class raw_hash_set; + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename raw_hash_set::value_type; + using reference = + absl::conditional_t; + using pointer = absl::remove_reference_t*; + using difference_type = typename raw_hash_set::difference_type; + + iterator() {} + + // PRECONDITION: not an end() iterator. + reference operator*() const { return PolicyTraits::element(slot_); } + + // PRECONDITION: not an end() iterator. + pointer operator->() const { return &operator*(); } + + // PRECONDITION: not an end() iterator. + iterator& operator++() { + ++ctrl_; + ++slot_; + skip_empty_or_deleted(); + return *this; + } + // PRECONDITION: not an end() iterator. + iterator operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } + + friend bool operator==(const iterator& a, const iterator& b) { + return a.ctrl_ == b.ctrl_; + } + friend bool operator!=(const iterator& a, const iterator& b) { + return !(a == b); + } + + private: + iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end() + iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {} + + void skip_empty_or_deleted() { + while (IsEmptyOrDeleted(*ctrl_)) { + // ctrl is not necessarily aligned to Group::kWidth. It is also likely + // to read past the space for ctrl bytes and into slots. This is ok + // because ctrl has sizeof() == 1 and slot has sizeof() >= 1 so there + // is no way to read outside the combined slot array. + uint32_t shift = Group{ctrl_}.CountLeadingEmptyOrDeleted(); + ctrl_ += shift; + slot_ += shift; + } + } + + ctrl_t* ctrl_ = nullptr; + slot_type* slot_; + }; + + class const_iterator { + friend class raw_hash_set; + + public: + using iterator_category = typename iterator::iterator_category; + using value_type = typename raw_hash_set::value_type; + using reference = typename raw_hash_set::const_reference; + using pointer = typename raw_hash_set::const_pointer; + using difference_type = typename raw_hash_set::difference_type; + + const_iterator() {} + // Implicit construction from iterator. + const_iterator(iterator i) : inner_(std::move(i)) {} + + reference operator*() const { return *inner_; } + pointer operator->() const { return inner_.operator->(); } + + const_iterator& operator++() { + ++inner_; + return *this; + } + const_iterator operator++(int) { return inner_++; } + + friend bool operator==(const const_iterator& a, const const_iterator& b) { + return a.inner_ == b.inner_; + } + friend bool operator!=(const const_iterator& a, const const_iterator& b) { + return !(a == b); + } + + private: + const_iterator(const ctrl_t* ctrl, const slot_type* slot) + : inner_(const_cast(ctrl), const_cast(slot)) {} + + iterator inner_; + }; + + using node_type = container_internal::node_handle; + + raw_hash_set() noexcept( + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::value&& + std::is_nothrow_default_constructible::value) {} + + explicit raw_hash_set(size_t bucket_count, const hasher& hash = hasher(), + const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) { + if (bucket_count) { + capacity_ = NormalizeCapacity(bucket_count); + growth_left() = static_cast(capacity_ * kMaxLoadFactor); + initialize_slots(); + } + } + + raw_hash_set(size_t bucket_count, const hasher& hash, + const allocator_type& alloc) + : raw_hash_set(bucket_count, hash, key_equal(), alloc) {} + + raw_hash_set(size_t bucket_count, const allocator_type& alloc) + : raw_hash_set(bucket_count, hasher(), key_equal(), alloc) {} + + explicit raw_hash_set(const allocator_type& alloc) + : raw_hash_set(0, hasher(), key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count = 0, + const hasher& hash = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(bucket_count, hash, eq, alloc) { + insert(first, last); + } + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(first, last, bucket_count, hash, key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(first, last, bucket_count, hasher(), key_equal(), alloc) {} + + template + raw_hash_set(InputIter first, InputIter last, const allocator_type& alloc) + : raw_hash_set(first, last, 0, hasher(), key_equal(), alloc) {} + + // Instead of accepting std::initializer_list as the first + // argument like std::unordered_set does, we have two overloads + // 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. + // std::unordered_set s = {"abc", "def"}; + // + // // Turns {"abc", "def"} into std::initializer_list, then + // // copies the strings into the set. + // absl::flat_hash_set s = {"abc", "def"}; + // + // The same trick is used in insert(). + // + // The enabler is necessary to prevent this constructor from triggering where + // the copy constructor is meant to be called. + // + // absl::flat_hash_set a, b{a}; + // + // RequiresNotInit is a workaround for gcc prior to 7.1. + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hash = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count = 0, + const hasher& hash = hasher(), const key_equal& eq = key_equal(), + const allocator_type& alloc = allocator_type()) + : raw_hash_set(init.begin(), init.end(), bucket_count, hash, eq, alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count, + const hasher& hash, const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hash, key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, size_t bucket_count, + const allocator_type& alloc) + : raw_hash_set(init, bucket_count, hasher(), key_equal(), alloc) {} + + template = 0, RequiresInsertable = 0> + raw_hash_set(std::initializer_list init, const allocator_type& alloc) + : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + raw_hash_set(std::initializer_list init, + const allocator_type& alloc) + : raw_hash_set(init, 0, hasher(), key_equal(), alloc) {} + + raw_hash_set(const raw_hash_set& that) + : raw_hash_set(that, AllocTraits::select_on_container_copy_construction( + that.alloc_ref())) {} + + raw_hash_set(const raw_hash_set& that, const allocator_type& a) + : raw_hash_set(0, that.hash_ref(), that.eq_ref(), a) { + reserve(that.size()); + // Because the table is guaranteed to be empty, we can do something faster + // 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); + } + size_ = that.size(); + growth_left() -= that.size(); + } + + raw_hash_set(raw_hash_set&& that) noexcept( + std::is_nothrow_copy_constructible::value&& + std::is_nothrow_copy_constructible::value&& + std::is_nothrow_copy_constructible::value) + : ctrl_(absl::exchange(that.ctrl_, EmptyGroup())), + slots_(absl::exchange(that.slots_, nullptr)), + size_(absl::exchange(that.size_, 0)), + capacity_(absl::exchange(that.capacity_, 0)), + // 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. + settings_(that.settings_) { + // growth_left was copied above, reset the one from `that`. + that.growth_left() = 0; + } + + raw_hash_set(raw_hash_set&& that, const allocator_type& a) + : ctrl_(EmptyGroup()), + slots_(nullptr), + size_(0), + capacity_(0), + settings_(0, that.hash_ref(), that.eq_ref(), a) { + if (a == that.alloc_ref()) { + std::swap(ctrl_, that.ctrl_); + std::swap(slots_, that.slots_); + std::swap(size_, that.size_); + std::swap(capacity_, that.capacity_); + std::swap(growth_left(), that.growth_left()); + } else { + reserve(that.size()); + // Note: this will copy elements of dense_set and unordered_set instead of + // moving them. This can be fixed if it ever becomes an issue. + for (auto& elem : that) insert(std::move(elem)); + } + } + + raw_hash_set& operator=(const raw_hash_set& that) { + raw_hash_set tmp(that, + AllocTraits::propagate_on_container_copy_assignment::value + ? that.alloc_ref() + : alloc_ref()); + swap(tmp); + return *this; + } + + raw_hash_set& operator=(raw_hash_set&& that) noexcept( + absl::allocator_traits::is_always_equal::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_assignable::value) { + // TODO(sbenza): We should only use the operations from the noexcept clause + // to make sure we actually adhere to that contract. + return move_assign( + std::move(that), + typename AllocTraits::propagate_on_container_move_assignment()); + } + + ~raw_hash_set() { destroy_slots(); } + + iterator begin() { + auto it = iterator_at(0); + it.skip_empty_or_deleted(); + return it; + } + iterator end() { return {ctrl_ + capacity_}; } + + const_iterator begin() const { + return const_cast(this)->begin(); + } + const_iterator end() const { return const_cast(this)->end(); } + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + bool empty() const { return !size(); } + size_t size() const { return size_; } + size_t capacity() const { return capacity_; } + size_t max_size() const { return (std::numeric_limits::max)(); } + + 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 + // container is small because allocation takes comparatively long time + // compared to destruction of the elements of the container. So we pick the + // largest bucket_count() threshold for which iteration is still fast and + // past that we simply deallocate the array. + if (capacity_ > 127) { + destroy_slots(); + } else if (capacity_) { + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + size_ = 0; + reset_ctrl(); + growth_left() = static_cast(capacity_ * kMaxLoadFactor); + } + assert(empty()); + } + + // This overload kicks in when the argument is an rvalue of insertable and + // decomposable type other than init_type. + // + // flat_hash_map m; + // m.insert(std::make_pair("abc", 42)); + template = 0, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + std::pair insert(T&& value) { + return emplace(std::forward(value)); + } + + // This overload kicks in when the argument is a bitfield or an lvalue of + // insertable and decomposable type. + // + // union { int n : 1; }; + // flat_hash_set s; + // s.insert(n); + // + // flat_hash_set s; + // const char* p = "hello"; + // s.insert(p); + // + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + template < + class T, RequiresInsertable = 0, + typename std::enable_if::value, int>::type = 0> + std::pair insert(const T& value) { + return emplace(value); + } + + // 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; + // s.insert({"abc", 42}); + std::pair insert(init_type&& value) { + return emplace(std::move(value)); + } + + template = 0, + typename std::enable_if::value, int>::type = 0, + T* = nullptr> + iterator insert(const_iterator, T&& value) { + return insert(std::forward(value)).first; + } + + // TODO(romanp): Once we stop supporting gcc 5.1 and below, replace + // RequiresInsertable with RequiresInsertable. + // We are hitting this bug: https://godbolt.org/g/1Vht4f. + template < + class T, RequiresInsertable = 0, + typename std::enable_if::value, int>::type = 0> + iterator insert(const_iterator, const T& value) { + return insert(value).first; + } + + iterator insert(const_iterator, init_type&& value) { + return insert(std::move(value)).first; + } + + template + void insert(InputIt first, InputIt last) { + for (; first != last; ++first) insert(*first); + } + + template = 0, RequiresInsertable = 0> + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + insert_return_type insert(node_type&& node) { + if (!node) return {end(), false, node_type()}; + const auto& elem = PolicyTraits::element(node.slot()); + auto res = PolicyTraits::apply( + InsertSlot{*this, std::move(*node.slot())}, elem); + if (res.second) { + node.reset(); + return {res.first, true, node_type()}; + } else { + return {res.first, false, std::move(node)}; + } + } + + iterator insert(const_iterator, node_type&& node) { + return insert(std::move(node)).first; + } + + // This overload kicks in if we can deduce the key from args. This enables us + // to avoid constructing value_type if an entry with the same key already + // exists. + // + // For example: + // + // flat_hash_map m = {{"abc", "def"}}; + // // Creates no std::string copies and makes no heap allocations. + // m.emplace("abc", "xyz"); + template ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + return PolicyTraits::apply(EmplaceDecomposable{*this}, + std::forward(args)...); + } + + // This overload kicks in if we cannot deduce the key from args. It constructs + // value_type unconditionally and then either moves it into the table or + // destroys. + template ::value, int>::type = 0> + std::pair emplace(Args&&... args) { + typename std::aligned_storage::type + raw; + slot_type* slot = reinterpret_cast(&raw); + + PolicyTraits::construct(&alloc_ref(), slot, std::forward(args)...); + const auto& elem = PolicyTraits::element(slot); + return PolicyTraits::apply(InsertSlot{*this, std::move(*slot)}, elem); + } + + template + iterator emplace_hint(const_iterator, Args&&... args) { + return emplace(std::forward(args)...).first; + } + + // Extension API: support for lazy emplace. + // + // Looks up key in the table. If found, returns the iterator to the element. + // Otherwise calls f with one argument of type raw_hash_set::constructor. f + // MUST call raw_hash_set::constructor with arguments as if a + // raw_hash_set::value_type is constructed, otherwise the behavior is + // undefined. + // + // For example: + // + // std::unordered_set s; + // // Makes ArenaStr even if "abc" is in the map. + // s.insert(ArenaString(&arena, "abc")); + // + // flat_hash_set s; + // // Makes ArenaStr only if "abc" is not in the map. + // s.lazy_emplace("abc", [&](const constructor& ctor) { + // ctor(&arena, "abc"); + // }); + // + // WARNING: This API is currently experimental. If there is a way to implement + // the same thing with the rest of the API, prefer that. + class constructor { + friend class raw_hash_set; + + public: + template + void operator()(Args&&... args) const { + assert(*slot_); + PolicyTraits::construct(alloc_, *slot_, std::forward(args)...); + *slot_ = nullptr; + } + + private: + constructor(allocator_type* a, slot_type** slot) : alloc_(a), slot_(slot) {} + + allocator_type* alloc_; + slot_type** slot_; + }; + + template + iterator lazy_emplace(const key_arg& key, F&& f) { + auto res = find_or_prepare_insert(key); + if (res.second) { + slot_type* slot = slots_ + res.first; + std::forward(f)(constructor(&alloc_ref(), &slot)); + assert(!slot); + } + return iterator_at(res.first); + } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.erase("abc"); + // + // flat_hash_set s; + // // Uses "abc" directly without copying it into std::string. + // s.erase("abc"); + template + size_type erase(const key_arg& key) { + auto it = find(key); + if (it == end()) return 0; + erase(it); + return 1; + } + + // Erases the element pointed to by `it`. Unlike `std::unordered_set::erase`, + // this method returns void to reduce algorithmic complexity to O(1). In + // order to erase while iterating across a map, use the following idiom (which + // also works for standard containers): + // + // for (auto it = m.begin(), end = m.end(); it != end;) { + // if () { + // m.erase(it++); + // } else { + // ++it; + // } + // } + void erase(const_iterator cit) { erase(cit.inner_); } + + // This overload is necessary because otherwise erase(const K&) would be + // a better match if non-const iterator is passed as an argument. + void erase(iterator it) { + assert(it != end()); + PolicyTraits::destroy(&alloc_ref(), it.slot_); + erase_meta_only(it); + } + + iterator erase(const_iterator first, const_iterator last) { + while (first != last) { + erase(first++); + } + return last.inner_; + } + + // Moves elements from `src` into `this`. + // If the element already exists in `this`, it is left unmodified in `src`. + template + void merge(raw_hash_set& src) { // NOLINT + assert(this != &src); + for (auto it = src.begin(), e = src.end(); it != e; ++it) { + if (PolicyTraits::apply(InsertSlot{*this, std::move(*it.slot_)}, + PolicyTraits::element(it.slot_)) + .second) { + src.erase_meta_only(it); + } + } + } + + template + void merge(raw_hash_set&& src) { + merge(src); + } + + node_type extract(const_iterator position) { + node_type node(alloc_ref(), position.inner_.slot_); + erase_meta_only(position); + return node; + } + + template < + class K = key_type, + typename std::enable_if::value, int>::type = 0> + node_type extract(const key_arg& key) { + auto it = find(key); + return it == end() ? node_type() : extract(const_iterator{it}); + } + + void swap(raw_hash_set& that) noexcept( + IsNoThrowSwappable() && IsNoThrowSwappable() && + (!AllocTraits::propagate_on_container_swap::value || + IsNoThrowSwappable())) { + using std::swap; + swap(ctrl_, that.ctrl_); + swap(slots_, that.slots_); + swap(size_, that.size_); + swap(capacity_, that.capacity_); + swap(growth_left(), that.growth_left()); + swap(hash_ref(), that.hash_ref()); + swap(eq_ref(), that.eq_ref()); + if (AllocTraits::propagate_on_container_swap::value) { + swap(alloc_ref(), that.alloc_ref()); + } else { + // If the allocators do not compare equal it is officially undefined + // behavior. We choose to do nothing. + } + } + + 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()))); + // n == 0 unconditionally rehashes as per the standard. + if (n == 0 || m > capacity_) { + resize(m); + } + } + + void reserve(size_t n) { + rehash(NumSlotsFast(n)); + } + + // Extension API: support for heterogeneous keys. + // + // std::unordered_set s; + // // Turns "abc" into std::string. + // s.count("abc"); + // + // ch_set s; + // // Uses "abc" directly without copying it into std::string. + // s.count("abc"); + template + size_t count(const key_arg& key) const { + return find(key) == end() ? 0 : 1; + } + + // Issues CPU prefetch instructions for the memory needed to find or insert + // a key. Like all lookup functions, this support heterogeneous keys. + // + // NOTE: This is a very low level operation and should not be used without + // specific benchmarks indicating its importance. + template + void prefetch(const key_arg& key) const { + (void)key; +#if defined(__GNUC__) + auto seq = probe(hash_ref()(key)); + __builtin_prefetch(static_cast(ctrl_ + seq.offset())); + __builtin_prefetch(static_cast(slots_ + seq.offset())); +#endif // __GNUC__ + } + + // The API of find() has two extensions. + // + // 1. The hash can be passed by the user. It must be equal to the hash of the + // key. + // + // 2. The type of the key argument doesn't have to be key_type. This is so + // called heterogeneous key support. + template + iterator find(const key_arg& key, size_t hash) { + auto seq = probe(hash); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slots_ + seq.offset(i))))) + return iterator_at(seq.offset(i)); + } + if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return end(); + seq.next(); + } + } + template + iterator find(const key_arg& key) { + return find(key, hash_ref()(key)); + } + + template + const_iterator find(const key_arg& key, size_t hash) const { + return const_cast(this)->find(key, hash); + } + template + const_iterator find(const key_arg& key) const { + return find(key, hash_ref()(key)); + } + + template + bool contains(const key_arg& key) const { + return find(key) != end(); + } + + template + std::pair equal_range(const key_arg& key) { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + template + std::pair equal_range( + const key_arg& key) const { + auto it = find(key); + if (it != end()) return {it, std::next(it)}; + return {it, it}; + } + + size_t bucket_count() const { return capacity_; } + float load_factor() const { + return capacity_ ? static_cast(size()) / capacity_ : 0.0; + } + float max_load_factor() const { return 1.0f; } + void max_load_factor(float) { + // Does nothing. + } + + hasher hash_function() const { return hash_ref(); } + key_equal key_eq() const { return eq_ref(); } + allocator_type get_allocator() const { return alloc_ref(); } + + friend bool operator==(const raw_hash_set& a, const raw_hash_set& b) { + if (a.size() != b.size()) return false; + const raw_hash_set* outer = &a; + const raw_hash_set* inner = &b; + if (outer->capacity() > inner->capacity()) std::swap(outer, inner); + for (const value_type& elem : *outer) + if (!inner->has_element(elem)) return false; + return true; + } + + friend bool operator!=(const raw_hash_set& a, const raw_hash_set& b) { + return !(a == b); + } + + friend void swap(raw_hash_set& a, + raw_hash_set& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); + } + + private: + template + friend struct absl::container_internal::hashtable_debug_internal:: + HashtableDebugAccess; + + struct FindElement { + template + const_iterator operator()(const K& key, Args&&...) const { + return s.find(key); + } + const raw_hash_set& s; + }; + + struct HashElement { + template + size_t operator()(const K& key, Args&&...) const { + return h(key); + } + const hasher& h; + }; + + template + struct EqualElement { + template + bool operator()(const K2& lhs, Args&&...) const { + return eq(lhs, rhs); + } + const K1& rhs; + const key_equal& eq; + }; + + struct EmplaceDecomposable { + template + std::pair operator()(const K& key, Args&&... args) const { + auto res = s.find_or_prepare_insert(key); + if (res.second) { + s.emplace_at(res.first, std::forward(args)...); + } + return {s.iterator_at(res.first), res.second}; + } + raw_hash_set& s; + }; + + template + struct InsertSlot { + template + std::pair operator()(const K& key, Args&&...) && { + auto res = s.find_or_prepare_insert(key); + if (res.second) { + PolicyTraits::transfer(&s.alloc_ref(), s.slots_ + res.first, &slot); + } else if (do_destroy) { + PolicyTraits::destroy(&s.alloc_ref(), &slot); + } + return {s.iterator_at(res.first), res.second}; + } + raw_hash_set& s; + // Constructed slot. Either moved into place or destroyed. + 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 + // another place. + void erase_meta_only(const_iterator it) { + assert(IsFull(*it.inner_.ctrl_) && "erasing a dangling iterator"); + --size_; + const size_t index = it.inner_.ctrl_ - ctrl_; + const size_t index_before = (index - Group::kWidth) & capacity_; + const auto empty_after = Group(it.inner_.ctrl_).MatchEmpty(); + const auto empty_before = Group(ctrl_ + index_before).MatchEmpty(); + + // We count how many consecutive non empties we have to the right and to the + // left of `it`. If the sum is >= kWidth then there is at least one probe + // window that might have seen a full group. + bool was_never_full = + empty_before && empty_after && + static_cast(empty_after.TrailingZeros() + + empty_before.LeadingZeros()) < Group::kWidth; + + set_ctrl(index, was_never_full ? kEmpty : kDeleted); + growth_left() += was_never_full; + } + + void initialize_slots() { + assert(capacity_); + 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_; + } + + void destroy_slots() { + if (!capacity_) return; + for (size_t i = 0; i != capacity_; ++i) { + if (IsFull(ctrl_[i])) { + PolicyTraits::destroy(&alloc_ref(), slots_ + i); + } + } + auto layout = MakeLayout(capacity_); + // Unpoison before returning the memory to the allocator. + SanitizerUnpoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); + Deallocate(&alloc_ref(), ctrl_, layout.AllocSize()); + ctrl_ = EmptyGroup(); + slots_ = nullptr; + size_ = 0; + capacity_ = 0; + growth_left() = 0; + } + + void resize(size_t new_capacity) { + assert(IsValidCapacity(new_capacity)); + auto* old_ctrl = ctrl_; + auto* old_slots = slots_; + const size_t old_capacity = capacity_; + capacity_ = new_capacity; + initialize_slots(); + + 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); + set_ctrl(new_i, H2(hash)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); + } + } + if (old_capacity) { + SanitizerUnpoisonMemoryRegion(old_slots, + sizeof(slot_type) * old_capacity); + auto layout = MakeLayout(old_capacity); + Deallocate(&alloc_ref(), old_ctrl, + layout.AllocSize()); + } + } + + void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE { + assert(IsValidCapacity(capacity_)); + // Algorithm: + // - mark all DELETED slots as EMPTY + // - mark all FULL slots as DELETED + // - for each slot marked as DELETED + // hash = Hash(element) + // target = find_first_non_full(hash) + // if target is in the same group + // mark slot as FULL + // else if target is EMPTY + // transfer element to target + // mark slot as EMPTY + // mark target as FULL + // else if target is DELETED + // swap current element with target element + // mark target as FULL + // repeat procedure for current slot with moved from element (target) + ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_); + typename std::aligned_storage::type + raw; + 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); + + // Verify if the old and new i fall within the same group wrt the hash. + // If they do, we don't need to move the object as it falls already in the + // best probe we can. + const auto probe_index = [&](size_t pos) { + return ((pos - probe(hash).offset()) & capacity_) / Group::kWidth; + }; + + // Element doesn't move. + if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) { + set_ctrl(i, H2(hash)); + continue; + } + if (IsEmpty(ctrl_[new_i])) { + // Transfer element to the empty spot. + // set_ctrl poisons/unpoisons the slots so we have to call it at the + // right time. + set_ctrl(new_i, H2(hash)); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slots_ + i); + set_ctrl(i, kEmpty); + } else { + assert(IsDeleted(ctrl_[new_i])); + set_ctrl(new_i, H2(hash)); + // Until we are done rehashing, DELETED marks previously FULL slots. + // Swap i and new_i elements. + PolicyTraits::transfer(&alloc_ref(), slot, slots_ + i); + PolicyTraits::transfer(&alloc_ref(), slots_ + i, slots_ + new_i); + PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, slot); + --i; // repeat + } + } + growth_left() = static_cast(capacity_ * kMaxLoadFactor) - size_; + } + + void rehash_and_grow_if_necessary() { + if (capacity_ == 0) { + resize(Group::kWidth - 1); + } else if (size() <= kMaxLoadFactor / 2 * capacity_) { + // Squash DELETED without growing if there is enough capacity. + drop_deletes_without_resize(); + } else { + // Otherwise grow the container. + resize(capacity_ * 2 + 1); + } + } + + bool has_element(const value_type& elem) const { + size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, elem); + auto seq = probe(hash); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::element(slots_ + seq.offset(i)) == + elem)) + return true; + } + if (ABSL_PREDICT_TRUE(g.MatchEmpty())) return false; + seq.next(); + assert(seq.index() < capacity_ && "full table!"); + } + return false; + } + + // Probes the raw_hash_set with the probe sequence for hash and returns the + // pointer to the first empty or deleted slot. + // NOTE: this function must work with tables having both kEmpty and kDelete + // in one group. Such tables appears during drop_deletes_without_resize. + // + // This function is very useful when insertions happen and: + // - the input is already a set + // - there are enough slots + // - the element with the hash is not in the table + size_t 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 + // 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()); +#endif + } + assert(seq.index() < capacity_ && "full table!"); + seq.next(); + } + } + + // TODO(alkis): Optimize this assuming *this and that don't overlap. + raw_hash_set& move_assign(raw_hash_set&& that, std::true_type) { + raw_hash_set tmp(std::move(that)); + swap(tmp); + return *this; + } + raw_hash_set& move_assign(raw_hash_set&& that, std::false_type) { + raw_hash_set tmp(std::move(that), alloc_ref()); + swap(tmp); + return *this; + } + + protected: + template + std::pair find_or_prepare_insert(const K& key) { + auto hash = hash_ref()(key); + auto seq = probe(hash); + while (true) { + Group g{ctrl_ + seq.offset()}; + for (int i : g.Match(H2(hash))) { + if (ABSL_PREDICT_TRUE(PolicyTraits::apply( + EqualElement{key, eq_ref()}, + PolicyTraits::element(slots_ + seq.offset(i))))) + return {seq.offset(i), false}; + } + if (ABSL_PREDICT_TRUE(g.MatchEmpty())) break; + seq.next(); + } + return {prepare_insert(hash), true}; + } + + 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]))) { + rehash_and_grow_if_necessary(); + target = find_first_non_full(hash); + } + ++size_; + growth_left() -= IsEmpty(ctrl_[target]); + set_ctrl(target, H2(hash)); + return target; + } + + // Constructs the value in the space pointed by the iterator. This only works + // after an unsuccessful find_or_prepare_insert() and before any other + // modifications happen in the raw_hash_set. + // + // PRECONDITION: i is an index returned from find_or_prepare_insert(k), where + // k is the key decomposed from `forward(args)...`, and the bool + // returned by find_or_prepare_insert(k) was true. + // POSTCONDITION: *m.iterator_at(i) == value_type(forward(args)...). + template + void emplace_at(size_t i, Args&&... args) { + PolicyTraits::construct(&alloc_ref(), slots_ + i, + std::forward(args)...); + + assert(PolicyTraits::apply(FindElement{*this}, *iterator_at(i)) == + iterator_at(i) && + "constructed value does not match the lookup key"); + } + + iterator iterator_at(size_t i) { return {ctrl_ + i, slots_ + i}; } + const_iterator iterator_at(size_t i) const { return {ctrl_ + i, slots_ + i}; } + + private: + friend struct RawHashSetTestOnlyAccess; + + probe_seq probe(size_t hash) const { + return probe_seq(H1(hash, ctrl_), capacity_); + } + + // Reset all ctrl bytes back to kEmpty, except the sentinel. + void reset_ctrl() { + std::memset(ctrl_, kEmpty, capacity_ + Group::kWidth); + ctrl_[capacity_] = kSentinel; + SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); + } + + // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at + // the end too. + void set_ctrl(size_t i, ctrl_t h) { + assert(i < capacity_); + + if (IsFull(h)) { + SanitizerUnpoisonObject(slots_ + i); + } else { + SanitizerPoisonObject(slots_ + i); + } + + ctrl_[i] = h; + ctrl_[((i - Group::kWidth) & capacity_) + Group::kWidth] = h; + } + + size_t& growth_left() { return settings_.template get<0>(); } + + hasher& hash_ref() { return settings_.template get<1>(); } + const hasher& hash_ref() const { return settings_.template get<1>(); } + key_equal& eq_ref() { return settings_.template get<2>(); } + const key_equal& eq_ref() const { return settings_.template get<2>(); } + allocator_type& alloc_ref() { return settings_.template get<3>(); } + const allocator_type& alloc_ref() const { + 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 + ctrl_t* ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t] + slot_type* slots_ = nullptr; // [capacity * slot_type] + size_t size_ = 0; // number of full slots + size_t capacity_ = 0; // total number of slots + absl::container_internal::CompressedTuple + settings_{0, hasher{}, key_equal{}, allocator_type{}}; +}; + +namespace hashtable_debug_internal { +template +struct HashtableDebugAccess> { + using Traits = typename Set::PolicyTraits; + using Slot = typename Traits::slot_type; + + static size_t GetNumProbes(const Set& set, + const typename Set::key_type& key) { + size_t num_probes = 0; + size_t hash = set.hash_ref()(key); + auto seq = set.probe(hash); + while (true) { + container_internal::Group g{set.ctrl_ + seq.offset()}; + for (int i : g.Match(container_internal::H2(hash))) { + if (Traits::apply( + typename Set::template EqualElement{ + key, set.eq_ref()}, + Traits::element(set.slots_ + seq.offset(i)))) + return num_probes; + ++num_probes; + } + if (g.MatchEmpty()) return num_probes; + seq.next(); + ++num_probes; + } + } + + static size_t AllocatedByteSize(const Set& c) { + size_t capacity = c.capacity_; + if (capacity == 0) return 0; + auto layout = Set::MakeLayout(capacity); + size_t m = layout.AllocSize(); + + size_t per_slot = Traits::space_used(static_cast(nullptr)); + if (per_slot != ~size_t{}) { + m += per_slot * c.size(); + } else { + for (size_t i = 0; i != capacity; ++i) { + if (container_internal::IsFull(c.ctrl_[i])) { + m += Traits::space_used(c.slots_ + i); + } + } + } + return m; + } + + static size_t LowerBoundAllocatedByteSize(size_t size) { + size_t capacity = container_internal::NormalizeCapacity( + std::ceil(size / Set::kMaxLoadFactor)); + if (capacity == 0) return 0; + auto layout = Set::MakeLayout(capacity); + size_t m = layout.AllocSize(); + size_t per_slot = Traits::space_used(static_cast(nullptr)); + if (per_slot != ~size_t{}) { + m += per_slot * size; + } + return m; + } +}; + +} // namespace hashtable_debug_internal +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // 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 new file mode 100644 index 00000000000..f5779d627e0 --- /dev/null +++ b/absl/container/internal/raw_hash_set_allocator_test.cc @@ -0,0 +1,430 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "gtest/gtest.h" +#include "absl/container/internal/raw_hash_set.h" +#include "absl/container/internal/tracked.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { +namespace { + +enum AllocSpec { + kPropagateOnCopy = 1, + kPropagateOnMove = 2, + kPropagateOnSwap = 4, +}; + +struct AllocState { + size_t num_allocs = 0; + std::set owned; +}; + +template +class CheckedAlloc { + public: + template + friend class CheckedAlloc; + + using value_type = T; + + CheckedAlloc() {} + explicit CheckedAlloc(size_t id) : id_(id) {} + CheckedAlloc(const CheckedAlloc&) = default; + CheckedAlloc& operator=(const CheckedAlloc&) = default; + + template + CheckedAlloc(const CheckedAlloc& that) + : id_(that.id_), state_(that.state_) {} + + template + struct rebind { + using other = CheckedAlloc; + }; + + using propagate_on_container_copy_assignment = + std::integral_constant; + + using propagate_on_container_move_assignment = + std::integral_constant; + + using propagate_on_container_swap = + std::integral_constant; + + CheckedAlloc select_on_container_copy_construction() const { + if (Spec & kPropagateOnCopy) return *this; + return {}; + } + + T* allocate(size_t n) { + T* ptr = std::allocator().allocate(n); + track_alloc(ptr); + return ptr; + } + void deallocate(T* ptr, size_t n) { + memset(ptr, 0, n * sizeof(T)); // The freed memory must be unpoisoned. + track_dealloc(ptr); + return std::allocator().deallocate(ptr, n); + } + + friend bool operator==(const CheckedAlloc& a, const CheckedAlloc& b) { + return a.id_ == b.id_; + } + friend bool operator!=(const CheckedAlloc& a, const CheckedAlloc& b) { + return !(a == b); + } + + size_t num_allocs() const { return state_->num_allocs; } + + void swap(CheckedAlloc& that) { + using std::swap; + swap(id_, that.id_); + swap(state_, that.state_); + } + + friend void swap(CheckedAlloc& a, CheckedAlloc& b) { a.swap(b); } + + friend std::ostream& operator<<(std::ostream& o, const CheckedAlloc& a) { + return o << "alloc(" << a.id_ << ")"; + } + + private: + void track_alloc(void* ptr) { + AllocState* state = state_.get(); + ++state->num_allocs; + if (!state->owned.insert(ptr).second) + ADD_FAILURE() << *this << " got previously allocated memory: " << ptr; + } + void track_dealloc(void* ptr) { + if (state_->owned.erase(ptr) != 1) + ADD_FAILURE() << *this + << " deleting memory owned by another allocator: " << ptr; + } + + size_t id_ = std::numeric_limits::max(); + + std::shared_ptr state_ = std::make_shared(); +}; + +struct Identity { + int32_t operator()(int32_t v) const { return v; } +}; + +struct Policy { + using slot_type = Tracked; + using init_type = Tracked; + using key_type = int32_t; + + template + static void construct(allocator_type* alloc, slot_type* slot, + Args&&... args) { + std::allocator_traits::construct( + *alloc, slot, std::forward(args)...); + } + + template + static void destroy(allocator_type* alloc, slot_type* slot) { + std::allocator_traits::destroy(*alloc, slot); + } + + template + static void transfer(allocator_type* alloc, slot_type* new_slot, + slot_type* old_slot) { + construct(alloc, new_slot, std::move(*old_slot)); + destroy(alloc, old_slot); + } + + template + static auto apply(F&& f, int32_t v) -> decltype(std::forward(f)(v, v)) { + return std::forward(f)(v, v); + } + + template + static auto apply(F&& f, const slot_type& v) + -> decltype(std::forward(f)(v.val(), v)) { + return std::forward(f)(v.val(), v); + } + + template + static auto apply(F&& f, slot_type&& v) + -> decltype(std::forward(f)(v.val(), std::move(v))) { + return std::forward(f)(v.val(), std::move(v)); + } + + static slot_type& element(slot_type* slot) { return *slot; } +}; + +template +struct PropagateTest : public ::testing::Test { + using Alloc = CheckedAlloc, Spec>; + + using Table = raw_hash_set, Alloc>; + + PropagateTest() { + EXPECT_EQ(a1, t1.get_allocator()); + EXPECT_NE(a2, t1.get_allocator()); + } + + Alloc a1 = Alloc(1); + Table t1 = Table(0, a1); + Alloc a2 = Alloc(2); +}; + +using PropagateOnAll = + PropagateTest; +using NoPropagateOnCopy = PropagateTest; +using NoPropagateOnMove = PropagateTest; + +TEST_F(PropagateOnAll, Empty) { EXPECT_EQ(0, a1.num_allocs()); } + +TEST_F(PropagateOnAll, InsertAllocates) { + auto it = t1.insert(0).first; + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(PropagateOnAll, InsertDecomposes) { + auto it = t1.insert(0).first; + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); + + EXPECT_FALSE(t1.insert(0).second); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(PropagateOnAll, RehashMoves) { + auto it = t1.insert(0).first; + EXPECT_EQ(0, it->num_moves()); + t1.rehash(2 * t1.capacity()); + EXPECT_EQ(2, a1.num_allocs()); + it = t1.find(0); + EXPECT_EQ(1, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(PropagateOnAll, CopyConstructor) { + auto it = t1.insert(0).first; + Table u(t1); + EXPECT_EQ(2, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(NoPropagateOnCopy, CopyConstructor) { + auto it = t1.insert(0).first; + Table u(t1); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(1, u.get_allocator().num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(PropagateOnAll, CopyConstructorWithSameAlloc) { + auto it = t1.insert(0).first; + Table u(t1, a1); + EXPECT_EQ(2, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(NoPropagateOnCopy, CopyConstructorWithSameAlloc) { + auto it = t1.insert(0).first; + Table u(t1, a1); + EXPECT_EQ(2, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(PropagateOnAll, CopyConstructorWithDifferentAlloc) { + auto it = t1.insert(0).first; + Table u(t1, a2); + EXPECT_EQ(a2, u.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(1, a2.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(NoPropagateOnCopy, CopyConstructorWithDifferentAlloc) { + auto it = t1.insert(0).first; + Table u(t1, a2); + EXPECT_EQ(a2, u.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(1, a2.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(PropagateOnAll, MoveConstructor) { + auto it = t1.insert(0).first; + Table u(std::move(t1)); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(NoPropagateOnMove, MoveConstructor) { + auto it = t1.insert(0).first; + Table u(std::move(t1)); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(PropagateOnAll, MoveConstructorWithSameAlloc) { + auto it = t1.insert(0).first; + Table u(std::move(t1), a1); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(NoPropagateOnMove, MoveConstructorWithSameAlloc) { + auto it = t1.insert(0).first; + Table u(std::move(t1), a1); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(PropagateOnAll, MoveConstructorWithDifferentAlloc) { + auto it = t1.insert(0).first; + Table u(std::move(t1), a2); + it = u.find(0); + EXPECT_EQ(a2, u.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(1, a2.num_allocs()); + EXPECT_EQ(1, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(NoPropagateOnMove, MoveConstructorWithDifferentAlloc) { + auto it = t1.insert(0).first; + Table u(std::move(t1), a2); + it = u.find(0); + EXPECT_EQ(a2, u.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(1, a2.num_allocs()); + EXPECT_EQ(1, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(PropagateOnAll, CopyAssignmentWithSameAlloc) { + auto it = t1.insert(0).first; + Table u(0, a1); + u = t1; + EXPECT_EQ(2, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(NoPropagateOnCopy, CopyAssignmentWithSameAlloc) { + auto it = t1.insert(0).first; + Table u(0, a1); + u = t1; + EXPECT_EQ(2, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(PropagateOnAll, CopyAssignmentWithDifferentAlloc) { + auto it = t1.insert(0).first; + Table u(0, a2); + u = t1; + EXPECT_EQ(a1, u.get_allocator()); + EXPECT_EQ(2, a1.num_allocs()); + EXPECT_EQ(0, a2.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(NoPropagateOnCopy, CopyAssignmentWithDifferentAlloc) { + auto it = t1.insert(0).first; + Table u(0, a2); + u = t1; + EXPECT_EQ(a2, u.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(1, a2.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(1, it->num_copies()); +} + +TEST_F(PropagateOnAll, MoveAssignmentWithSameAlloc) { + auto it = t1.insert(0).first; + Table u(0, a1); + u = std::move(t1); + EXPECT_EQ(a1, u.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(NoPropagateOnMove, MoveAssignmentWithSameAlloc) { + auto it = t1.insert(0).first; + Table u(0, a1); + u = std::move(t1); + EXPECT_EQ(a1, u.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(PropagateOnAll, MoveAssignmentWithDifferentAlloc) { + auto it = t1.insert(0).first; + Table u(0, a2); + u = std::move(t1); + EXPECT_EQ(a1, u.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, a2.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(NoPropagateOnMove, MoveAssignmentWithDifferentAlloc) { + auto it = t1.insert(0).first; + Table u(0, a2); + u = std::move(t1); + it = u.find(0); + EXPECT_EQ(a2, u.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(1, a2.num_allocs()); + EXPECT_EQ(1, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +TEST_F(PropagateOnAll, Swap) { + auto it = t1.insert(0).first; + Table u(0, a2); + u.swap(t1); + EXPECT_EQ(a1, u.get_allocator()); + EXPECT_EQ(a2, t1.get_allocator()); + EXPECT_EQ(1, a1.num_allocs()); + EXPECT_EQ(0, a2.num_allocs()); + EXPECT_EQ(0, it->num_moves()); + EXPECT_EQ(0, it->num_copies()); +} + +} // namespace +} // namespace container_internal +} // inline namespace lts_2018_12_18 +} // namespace absl diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc new file mode 100644 index 00000000000..302f975818a --- /dev/null +++ b/absl/container/internal/raw_hash_set_test.cc @@ -0,0 +1,1830 @@ +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/raw_hash_set.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/internal/cycleclock.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/container/internal/container_memory.h" +#include "absl/container/internal/hash_function_defaults.h" +#include "absl/container/internal/hash_policy_testing.h" +#include "absl/container/internal/hashtable_debug.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2018_12_18 { +namespace container_internal { + +struct RawHashSetTestOnlyAccess { + template + static auto GetSlots(const C& c) -> decltype(c.slots_) { + return c.slots_; + } +}; + +namespace { + +using ::testing::DoubleNear; +using ::testing::ElementsAre; +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)); +} + +TEST(Util, probe_seq) { + probe_seq<16> seq(0, 127); + auto gen = [&]() { + size_t res = seq.offset(); + seq.next(); + return res; + }; + std::vector offsets(8); + std::generate_n(offsets.begin(), 8, gen); + EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64)); + seq = probe_seq<16>(128, 127); + std::generate_n(offsets.begin(), 8, gen); + EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64)); +} + +TEST(BitMask, Smoke) { + EXPECT_FALSE((BitMask(0))); + EXPECT_TRUE((BitMask(5))); + + EXPECT_THAT((BitMask(0)), ElementsAre()); + EXPECT_THAT((BitMask(0x1)), ElementsAre(0)); + EXPECT_THAT((BitMask(0x2)), ElementsAre(1)); + EXPECT_THAT((BitMask(0x3)), ElementsAre(0, 1)); + EXPECT_THAT((BitMask(0x4)), ElementsAre(2)); + EXPECT_THAT((BitMask(0x5)), ElementsAre(0, 2)); + EXPECT_THAT((BitMask(0x55)), ElementsAre(0, 2, 4, 6)); + EXPECT_THAT((BitMask(0xAA)), ElementsAre(1, 3, 5, 7)); +} + +TEST(BitMask, WithShift) { + // See the non-SSE version of Group for details on what this math is for. + uint64_t ctrl = 0x1716151413121110; + uint64_t hash = 0x12; + constexpr uint64_t msbs = 0x8080808080808080ULL; + constexpr uint64_t lsbs = 0x0101010101010101ULL; + auto x = ctrl ^ (lsbs * hash); + uint64_t mask = (x - lsbs) & ~x & msbs; + EXPECT_EQ(0x0000000080800000, mask); + + BitMask b(mask); + EXPECT_EQ(*b, 2); +} + +TEST(BitMask, LeadingTrailing) { + EXPECT_EQ((BitMask(0b0001101001000000).LeadingZeros()), 3); + EXPECT_EQ((BitMask(0b0001101001000000).TrailingZeros()), 6); + + EXPECT_EQ((BitMask(0b0000000000000001).LeadingZeros()), 15); + EXPECT_EQ((BitMask(0b0000000000000001).TrailingZeros()), 0); + + EXPECT_EQ((BitMask(0b1000000000000000).LeadingZeros()), 0); + EXPECT_EQ((BitMask(0b1000000000000000).TrailingZeros()), 15); + + EXPECT_EQ((BitMask(0x0000008080808000).LeadingZeros()), 3); + EXPECT_EQ((BitMask(0x0000008080808000).TrailingZeros()), 1); + + EXPECT_EQ((BitMask(0x0000000000000080).LeadingZeros()), 7); + EXPECT_EQ((BitMask(0x0000000000000080).TrailingZeros()), 0); + + EXPECT_EQ((BitMask(0x8000000000000000).LeadingZeros()), 0); + EXPECT_EQ((BitMask(0x8000000000000000).TrailingZeros()), 7); +} + +TEST(Group, EmptyGroup) { + for (h2_t h = 0; h != 128; ++h) EXPECT_FALSE(Group{EmptyGroup()}.Match(h)); +} + +TEST(Group, Match) { + if (Group::kWidth == 16) { + ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7, + 7, 5, 3, 1, 1, 1, 1, 1}; + EXPECT_THAT(Group{group}.Match(0), ElementsAre()); + EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15)); + EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10)); + EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9)); + EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8)); + } else if (Group::kWidth == 8) { + ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1}; + EXPECT_THAT(Group{group}.Match(0), ElementsAre()); + EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7)); + EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4)); + } else { + FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth; + } +} + +TEST(Group, MatchEmpty) { + if (Group::kWidth == 16) { + ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7, + 7, 5, 3, 1, 1, 1, 1, 1}; + EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4)); + } else if (Group::kWidth == 8) { + ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1}; + EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0)); + } else { + FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth; + } +} + +TEST(Group, MatchEmptyOrDeleted) { + if (Group::kWidth == 16) { + ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7, + 7, 5, 3, 1, 1, 1, 1, 1}; + EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4)); + } else if (Group::kWidth == 8) { + ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1}; + EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3)); + } else { + FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth; + } +} + +TEST(Batch, DropDeletes) { + constexpr size_t kCapacity = 63; + constexpr size_t kGroupWidth = container_internal::Group::kWidth; + std::vector ctrl(kCapacity + 1 + kGroupWidth); + ctrl[kCapacity] = kSentinel; + std::vector pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted}; + for (size_t i = 0; i != kCapacity; ++i) { + ctrl[i] = pattern[i % pattern.size()]; + if (i < kGroupWidth - 1) + ctrl[i + kCapacity + 1] = pattern[i % pattern.size()]; + } + ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity); + ASSERT_EQ(ctrl[kCapacity], kSentinel); + for (size_t i = 0; i < kCapacity + 1 + kGroupWidth; ++i) { + ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()]; + if (i == kCapacity) expected = kSentinel; + if (expected == kDeleted) expected = kEmpty; + if (IsFull(expected)) expected = kDeleted; + EXPECT_EQ(ctrl[i], expected) + << i << " " << int{pattern[i % pattern.size()]}; + } +} + +TEST(Group, CountLeadingEmptyOrDeleted) { + const std::vector empty_examples = {kEmpty, kDeleted}; + const std::vector full_examples = {0, 1, 2, 3, 5, 9, 127, kSentinel}; + + for (ctrl_t empty : empty_examples) { + std::vector e(Group::kWidth, empty); + EXPECT_EQ(Group::kWidth, Group{e.data()}.CountLeadingEmptyOrDeleted()); + for (ctrl_t full : full_examples) { + for (size_t i = 0; i != Group::kWidth; ++i) { + std::vector f(Group::kWidth, empty); + f[i] = full; + EXPECT_EQ(i, Group{f.data()}.CountLeadingEmptyOrDeleted()); + } + std::vector f(Group::kWidth, empty); + f[Group::kWidth * 2 / 3] = full; + f[Group::kWidth / 2] = full; + EXPECT_EQ( + Group::kWidth / 2, Group{f.data()}.CountLeadingEmptyOrDeleted()); + } + } +} + +struct IntPolicy { + using slot_type = int64_t; + using key_type = int64_t; + using init_type = int64_t; + + static void construct(void*, int64_t* slot, int64_t v) { *slot = v; } + static void destroy(void*, int64_t*) {} + static void transfer(void*, int64_t* new_slot, int64_t* old_slot) { + *new_slot = *old_slot; + } + + static int64_t& element(slot_type* slot) { return *slot; } + + template + static auto apply(F&& f, int64_t x) -> decltype(std::forward(f)(x, x)) { + return std::forward(f)(x, x); + } +}; + +class StringPolicy { + template ::value>::type> + decltype(std::declval()( + std::declval(), std::piecewise_construct, + std::declval>(), + std::declval())) static apply_impl(F&& f, + std::pair, V> p) { + const absl::string_view& key = std::get<0>(p.first); + return std::forward(f)(key, std::piecewise_construct, std::move(p.first), + std::move(p.second)); + } + + public: + struct slot_type { + struct ctor {}; + + template + slot_type(ctor, Ts&&... ts) : pair(std::forward(ts)...) {} + + std::pair pair; + }; + + using key_type = std::string; + using init_type = std::pair; + + template + static void construct(allocator_type* alloc, slot_type* slot, Args... args) { + std::allocator_traits::construct( + *alloc, slot, typename slot_type::ctor(), std::forward(args)...); + } + + template + static void destroy(allocator_type* alloc, slot_type* slot) { + std::allocator_traits::destroy(*alloc, slot); + } + + template + static void transfer(allocator_type* alloc, slot_type* new_slot, + slot_type* old_slot) { + construct(alloc, new_slot, std::move(old_slot->pair)); + destroy(alloc, old_slot); + } + + static std::pair& element(slot_type* slot) { + return slot->pair; + } + + template + static auto apply(F&& f, Args&&... args) + -> decltype(apply_impl(std::forward(f), + PairArgs(std::forward(args)...))) { + return apply_impl(std::forward(f), + PairArgs(std::forward(args)...)); + } +}; + +struct StringHash : absl::Hash { + using is_transparent = void; +}; +struct StringEq : std::equal_to { + using is_transparent = void; +}; + +struct StringTable + : raw_hash_set> { + using Base = typename StringTable::raw_hash_set; + StringTable() {} + using Base::Base; +}; + +struct IntTable + : raw_hash_set, + std::equal_to, std::allocator> { + using Base = typename IntTable::raw_hash_set; + IntTable() {} + using Base::Base; +}; + +struct BadFastHash { + template + size_t operator()(const T&) const { + return 0; + } +}; + +struct BadTable : raw_hash_set, + std::allocator> { + using Base = typename BadTable::raw_hash_set; + BadTable() {} + using Base::Base; +}; + +TEST(Table, EmptyFunctorOptimization) { + static_assert(std::is_empty>::value, ""); + static_assert(std::is_empty>::value, ""); + + struct MockTable { + void* ctrl; + void* slots; + size_t size; + size_t capacity; + size_t growth_left; + }; + struct StatelessHash { + size_t operator()(absl::string_view) const { return 0; } + }; + struct StatefulHash : StatelessHash { + size_t dummy; + }; + + EXPECT_EQ( + sizeof(MockTable), + sizeof( + raw_hash_set, std::allocator>)); + + EXPECT_EQ( + sizeof(MockTable) + sizeof(StatefulHash), + sizeof( + raw_hash_set, std::allocator>)); +} + +TEST(Table, Empty) { + IntTable t; + EXPECT_EQ(0, t.size()); + EXPECT_TRUE(t.empty()); +} + +#ifdef __GNUC__ +template +ABSL_ATTRIBUTE_ALWAYS_INLINE inline void DoNotOptimize(const T& v) { + asm volatile("" : : "r,m"(v) : "memory"); +} +#endif + +TEST(Table, Prefetch) { + IntTable t; + t.emplace(1); + // Works for both present and absent keys. + t.prefetch(1); + t.prefetch(2); + + // Do not run in debug mode, when prefetch is not implemented, or when + // sanitizers are enabled. +#if defined(NDEBUG) && defined(__GNUC__) && !defined(ADDRESS_SANITIZER) && \ + !defined(MEMORY_SANITIZER) && !defined(THREAD_SANITIZER) && \ + !defined(UNDEFINED_BEHAVIOR_SANITIZER) + const auto now = [] { return absl::base_internal::CycleClock::Now(); }; + + // Make size enough to not fit in L2 cache (16.7 Mb) + static constexpr int size = 1 << 22; + for (int i = 0; i < size; ++i) t.insert(i); + + int64_t no_prefetch = 0, prefetch = 0; + for (int iter = 0; iter < 10; ++iter) { + int64_t time = now(); + for (int i = 0; i < size; ++i) { + DoNotOptimize(t.find(i)); + } + no_prefetch += now() - time; + + time = now(); + for (int i = 0; i < size; ++i) { + t.prefetch(i + 20); + DoNotOptimize(t.find(i)); + } + prefetch += now() - time; + } + + // no_prefetch is at least 30% slower. + EXPECT_GE(1.0 * no_prefetch / prefetch, 1.3); +#endif +} + +TEST(Table, LookupEmpty) { + IntTable t; + auto it = t.find(0); + EXPECT_TRUE(it == t.end()); +} + +TEST(Table, Insert1) { + IntTable t; + EXPECT_TRUE(t.find(0) == t.end()); + auto res = t.emplace(0); + EXPECT_TRUE(res.second); + EXPECT_THAT(*res.first, 0); + EXPECT_EQ(1, t.size()); + EXPECT_THAT(*t.find(0), 0); +} + +TEST(Table, Insert2) { + IntTable t; + EXPECT_TRUE(t.find(0) == t.end()); + auto res = t.emplace(0); + EXPECT_TRUE(res.second); + EXPECT_THAT(*res.first, 0); + EXPECT_EQ(1, t.size()); + EXPECT_TRUE(t.find(1) == t.end()); + res = t.emplace(1); + EXPECT_TRUE(res.second); + EXPECT_THAT(*res.first, 1); + EXPECT_EQ(2, t.size()); + EXPECT_THAT(*t.find(0), 0); + EXPECT_THAT(*t.find(1), 1); +} + +TEST(Table, InsertCollision) { + BadTable t; + EXPECT_TRUE(t.find(1) == t.end()); + auto res = t.emplace(1); + EXPECT_TRUE(res.second); + EXPECT_THAT(*res.first, 1); + EXPECT_EQ(1, t.size()); + + EXPECT_TRUE(t.find(2) == t.end()); + res = t.emplace(2); + EXPECT_THAT(*res.first, 2); + EXPECT_TRUE(res.second); + EXPECT_EQ(2, t.size()); + + EXPECT_THAT(*t.find(1), 1); + EXPECT_THAT(*t.find(2), 2); +} + +// Test that we do not add existent element in case we need to search through +// many groups with deleted elements +TEST(Table, InsertCollisionAndFindAfterDelete) { + BadTable t; // all elements go to the same group. + // Have at least 2 groups with Group::kWidth collisions + // plus some extra collisions in the last group. + constexpr size_t kNumInserts = Group::kWidth * 2 + 5; + for (size_t i = 0; i < kNumInserts; ++i) { + auto res = t.emplace(i); + EXPECT_TRUE(res.second); + EXPECT_THAT(*res.first, i); + EXPECT_EQ(i + 1, t.size()); + } + + // Remove elements one by one and check + // that we still can find all other elements. + for (size_t i = 0; i < kNumInserts; ++i) { + EXPECT_EQ(1, t.erase(i)) << i; + for (size_t j = i + 1; j < kNumInserts; ++j) { + EXPECT_THAT(*t.find(j), j); + auto res = t.emplace(j); + EXPECT_FALSE(res.second) << i << " " << j; + EXPECT_THAT(*res.first, j); + EXPECT_EQ(kNumInserts - i - 1, t.size()); + } + } + EXPECT_TRUE(t.empty()); +} + +TEST(Table, LazyEmplace) { + StringTable t; + bool called = false; + auto it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) { + called = true; + f("abc", "ABC"); + }); + EXPECT_TRUE(called); + EXPECT_THAT(*it, Pair("abc", "ABC")); + called = false; + it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) { + called = true; + f("abc", "DEF"); + }); + EXPECT_FALSE(called); + EXPECT_THAT(*it, Pair("abc", "ABC")); +} + +TEST(Table, ContainsEmpty) { + IntTable t; + + EXPECT_FALSE(t.contains(0)); +} + +TEST(Table, Contains1) { + IntTable t; + + EXPECT_TRUE(t.insert(0).second); + EXPECT_TRUE(t.contains(0)); + EXPECT_FALSE(t.contains(1)); + + EXPECT_EQ(1, t.erase(0)); + EXPECT_FALSE(t.contains(0)); +} + +TEST(Table, Contains2) { + IntTable t; + + EXPECT_TRUE(t.insert(0).second); + EXPECT_TRUE(t.contains(0)); + EXPECT_FALSE(t.contains(1)); + + t.clear(); + EXPECT_FALSE(t.contains(0)); +} + +int decompose_constructed; +struct DecomposeType { + DecomposeType(int i) : i(i) { // NOLINT + ++decompose_constructed; + } + + explicit DecomposeType(const char* d) : DecomposeType(*d) {} + + int i; +}; + +struct DecomposeHash { + using is_transparent = void; + size_t operator()(DecomposeType a) const { return a.i; } + size_t operator()(int a) const { return a; } + size_t operator()(const char* a) const { return *a; } +}; + +struct DecomposeEq { + using is_transparent = void; + bool operator()(DecomposeType a, DecomposeType b) const { return a.i == b.i; } + bool operator()(DecomposeType a, int b) const { return a.i == b; } + bool operator()(DecomposeType a, const char* b) const { return a.i == *b; } +}; + +struct DecomposePolicy { + using slot_type = DecomposeType; + using key_type = DecomposeType; + using init_type = DecomposeType; + + template + static void construct(void*, DecomposeType* slot, T&& v) { + *slot = DecomposeType(std::forward(v)); + } + static void destroy(void*, DecomposeType*) {} + static DecomposeType& element(slot_type* slot) { return *slot; } + + template + static auto apply(F&& f, const T& x) -> decltype(std::forward(f)(x, x)) { + return std::forward(f)(x, x); + } +}; + +template +void TestDecompose(bool construct_three) { + DecomposeType elem{0}; + const int one = 1; + const char* three_p = "3"; + const auto& three = three_p; + + raw_hash_set> set1; + + decompose_constructed = 0; + int expected_constructed = 0; + EXPECT_EQ(expected_constructed, decompose_constructed); + set1.insert(elem); + EXPECT_EQ(expected_constructed, decompose_constructed); + set1.insert(1); + EXPECT_EQ(++expected_constructed, decompose_constructed); + set1.emplace("3"); + EXPECT_EQ(++expected_constructed, decompose_constructed); + EXPECT_EQ(expected_constructed, decompose_constructed); + + { // insert(T&&) + set1.insert(1); + EXPECT_EQ(expected_constructed, decompose_constructed); + } + + { // insert(const T&) + set1.insert(one); + EXPECT_EQ(expected_constructed, decompose_constructed); + } + + { // insert(hint, T&&) + set1.insert(set1.begin(), 1); + EXPECT_EQ(expected_constructed, decompose_constructed); + } + + { // insert(hint, const T&) + set1.insert(set1.begin(), one); + EXPECT_EQ(expected_constructed, decompose_constructed); + } + + { // emplace(...) + set1.emplace(1); + EXPECT_EQ(expected_constructed, decompose_constructed); + set1.emplace("3"); + expected_constructed += construct_three; + EXPECT_EQ(expected_constructed, decompose_constructed); + set1.emplace(one); + EXPECT_EQ(expected_constructed, decompose_constructed); + set1.emplace(three); + expected_constructed += construct_three; + EXPECT_EQ(expected_constructed, decompose_constructed); + } + + { // emplace_hint(...) + set1.emplace_hint(set1.begin(), 1); + EXPECT_EQ(expected_constructed, decompose_constructed); + set1.emplace_hint(set1.begin(), "3"); + expected_constructed += construct_three; + EXPECT_EQ(expected_constructed, decompose_constructed); + set1.emplace_hint(set1.begin(), one); + EXPECT_EQ(expected_constructed, decompose_constructed); + set1.emplace_hint(set1.begin(), three); + expected_constructed += construct_three; + EXPECT_EQ(expected_constructed, decompose_constructed); + } +} + +TEST(Table, Decompose) { + TestDecompose(false); + + struct TransparentHashIntOverload { + size_t operator()(DecomposeType a) const { return a.i; } + size_t operator()(int a) const { return a; } + }; + struct TransparentEqIntOverload { + bool operator()(DecomposeType a, DecomposeType b) const { + return a.i == b.i; + } + bool operator()(DecomposeType a, int b) const { return a.i == b; } + }; + TestDecompose(true); + TestDecompose(true); + TestDecompose(true); +} + +// Returns the largest m such that a table with m elements has the same number +// of buckets as a table with n elements. +size_t MaxDensitySize(size_t n) { + IntTable t; + t.reserve(n); + for (size_t i = 0; i != n; ++i) t.emplace(i); + const size_t c = t.bucket_count(); + while (c == t.bucket_count()) t.emplace(n++); + return t.size() - 1; +} + +struct Modulo1000Hash { + size_t operator()(int x) const { return x % 1000; } +}; + +struct Modulo1000HashTable + : public raw_hash_set, + std::allocator> {}; + +// Test that rehash with no resize happen in case of many deleted slots. +TEST(Table, RehashWithNoResize) { + Modulo1000HashTable t; + // Adding the same length (and the same hash) strings + // to have at least kMinFullGroups groups + // with Group::kWidth collisions. Then fill up to MaxDensitySize; + const size_t kMinFullGroups = 7; + std::vector keys; + for (size_t i = 0; i < MaxDensitySize(Group::kWidth * kMinFullGroups); ++i) { + int k = i * 1000; + t.emplace(k); + keys.push_back(k); + } + const size_t capacity = t.capacity(); + + // Remove elements from all groups except the first and the last one. + // All elements removed from full groups will be marked as kDeleted. + const size_t erase_begin = Group::kWidth / 2; + const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth; + for (size_t i = erase_begin; i < erase_end; ++i) { + EXPECT_EQ(1, t.erase(keys[i])) << i; + } + keys.erase(keys.begin() + erase_begin, keys.begin() + erase_end); + + auto last_key = keys.back(); + size_t last_key_num_probes = GetHashtableDebugNumProbes(t, last_key); + + // Make sure that we have to make a lot of probes for last key. + ASSERT_GT(last_key_num_probes, kMinFullGroups); + + int x = 1; + // Insert and erase one element, before inplace rehash happen. + while (last_key_num_probes == GetHashtableDebugNumProbes(t, last_key)) { + t.emplace(x); + ASSERT_EQ(capacity, t.capacity()); + // All elements should be there. + ASSERT_TRUE(t.find(x) != t.end()) << x; + for (const auto& k : keys) { + ASSERT_TRUE(t.find(k) != t.end()) << k; + } + t.erase(x); + ++x; + } +} + +TEST(Table, InsertEraseStressTest) { + IntTable t; + const size_t kMinElementCount = 250; + std::deque keys; + size_t i = 0; + for (; i < MaxDensitySize(kMinElementCount); ++i) { + t.emplace(i); + keys.push_back(i); + } + const size_t kNumIterations = 1000000; + for (; i < kNumIterations; ++i) { + ASSERT_EQ(1, t.erase(keys.front())); + keys.pop_front(); + t.emplace(i); + keys.push_back(i); + } +} + +TEST(Table, InsertOverloads) { + StringTable t; + // These should all trigger the insert(init_type) overload. + t.insert({{}, {}}); + t.insert({"ABC", {}}); + t.insert({"DEF", "!!!"}); + + EXPECT_THAT(t, UnorderedElementsAre(Pair("", ""), Pair("ABC", ""), + Pair("DEF", "!!!"))); +} + +TEST(Table, LargeTable) { + IntTable t; + for (int64_t i = 0; i != 100000; ++i) t.emplace(i << 40); + for (int64_t i = 0; i != 100000; ++i) ASSERT_EQ(i << 40, *t.find(i << 40)); +} + +// Timeout if copy is quadratic as it was in Rust. +TEST(Table, EnsureNonQuadraticAsInRust) { + static const size_t kLargeSize = 1 << 15; + + IntTable t; + for (size_t i = 0; i != kLargeSize; ++i) { + t.insert(i); + } + + // If this is quadratic, the test will timeout. + IntTable t2; + for (const auto& entry : t) t2.insert(entry); +} + +TEST(Table, ClearBug) { + IntTable t; + constexpr size_t capacity = container_internal::Group::kWidth - 1; + constexpr size_t max_size = capacity / 2; + for (size_t i = 0; i < max_size; ++i) { + t.insert(i); + } + ASSERT_EQ(capacity, t.capacity()); + intptr_t original = reinterpret_cast(&*t.find(2)); + t.clear(); + ASSERT_EQ(capacity, t.capacity()); + for (size_t i = 0; i < max_size; ++i) { + t.insert(i); + } + ASSERT_EQ(capacity, t.capacity()); + intptr_t second = reinterpret_cast(&*t.find(2)); + // We are checking that original and second are close enough to each other + // that they are probably still in the same group. This is not strictly + // guaranteed. + EXPECT_LT(std::abs(original - second), + capacity * sizeof(IntTable::value_type)); +} + +TEST(Table, Erase) { + IntTable t; + EXPECT_TRUE(t.find(0) == t.end()); + auto res = t.emplace(0); + EXPECT_TRUE(res.second); + EXPECT_EQ(1, t.size()); + t.erase(res.first); + EXPECT_EQ(0, t.size()); + EXPECT_TRUE(t.find(0) == t.end()); +} + +// Collect N bad keys by following algorithm: +// 1. Create an empty table and reserve it to 2 * N. +// 2. Insert N random elements. +// 3. Take first Group::kWidth - 1 to bad_keys array. +// 4. Clear the table without resize. +// 5. Go to point 2 while N keys not collected +std::vector CollectBadMergeKeys(size_t N) { + static constexpr int kGroupSize = Group::kWidth - 1; + + auto topk_range = [](size_t b, size_t e, IntTable* t) -> std::vector { + for (size_t i = b; i != e; ++i) { + t->emplace(i); + } + std::vector res; + res.reserve(kGroupSize); + auto it = t->begin(); + for (size_t i = b; i != e && i != b + kGroupSize; ++i, ++it) { + res.push_back(*it); + } + return res; + }; + + std::vector bad_keys; + bad_keys.reserve(N); + IntTable t; + t.reserve(N * 2); + + for (size_t b = 0; bad_keys.size() < N; b += N) { + auto keys = topk_range(b, b + N, &t); + bad_keys.insert(bad_keys.end(), keys.begin(), keys.end()); + t.erase(t.begin(), t.end()); + EXPECT_TRUE(t.empty()); + } + return bad_keys; +} + +struct ProbeStats { + // Number of elements with specific probe length over all tested tables. + std::vector all_probes_histogram; + // Ratios total_probe_length/size for every tested table. + std::vector single_table_ratios; + + friend ProbeStats operator+(const ProbeStats& a, const ProbeStats& b) { + ProbeStats res = a; + res.all_probes_histogram.resize(std::max(res.all_probes_histogram.size(), + b.all_probes_histogram.size())); + std::transform(b.all_probes_histogram.begin(), b.all_probes_histogram.end(), + res.all_probes_histogram.begin(), + res.all_probes_histogram.begin(), std::plus()); + res.single_table_ratios.insert(res.single_table_ratios.end(), + b.single_table_ratios.begin(), + b.single_table_ratios.end()); + return res; + } + + // Average ratio total_probe_length/size over tables. + double AvgRatio() const { + return std::accumulate(single_table_ratios.begin(), + single_table_ratios.end(), 0.0) / + single_table_ratios.size(); + } + + // Maximum ratio total_probe_length/size over tables. + double MaxRatio() const { + return *std::max_element(single_table_ratios.begin(), + single_table_ratios.end()); + } + + // Percentile ratio total_probe_length/size over tables. + double PercentileRatio(double Percentile = 0.95) const { + auto r = single_table_ratios; + auto mid = r.begin() + static_cast(r.size() * Percentile); + if (mid != r.end()) { + std::nth_element(r.begin(), mid, r.end()); + return *mid; + } else { + return MaxRatio(); + } + } + + // Maximum probe length over all elements and all tables. + size_t MaxProbe() const { return all_probes_histogram.size(); } + + // Fraction of elements with specified probe length. + std::vector ProbeNormalizedHistogram() const { + double total_elements = std::accumulate(all_probes_histogram.begin(), + all_probes_histogram.end(), 0ull); + std::vector res; + for (size_t p : all_probes_histogram) { + res.push_back(p / total_elements); + } + return res; + } + + size_t PercentileProbe(double Percentile = 0.99) const { + size_t idx = 0; + for (double p : ProbeNormalizedHistogram()) { + if (Percentile > p) { + Percentile -= p; + ++idx; + } else { + return idx; + } + } + return idx; + } + + friend std::ostream& operator<<(std::ostream& out, const ProbeStats& s) { + out << "{AvgRatio:" << s.AvgRatio() << ", MaxRatio:" << s.MaxRatio() + << ", PercentileRatio:" << s.PercentileRatio() + << ", MaxProbe:" << s.MaxProbe() << ", Probes=["; + for (double p : s.ProbeNormalizedHistogram()) { + out << p << ","; + } + out << "]}"; + + return out; + } +}; + +struct ExpectedStats { + double avg_ratio; + double max_ratio; + std::vector> pecentile_ratios; + std::vector> pecentile_probes; + + friend std::ostream& operator<<(std::ostream& out, const ExpectedStats& s) { + out << "{AvgRatio:" << s.avg_ratio << ", MaxRatio:" << s.max_ratio + << ", PercentileRatios: ["; + for (auto el : s.pecentile_ratios) { + out << el.first << ":" << el.second << ", "; + } + out << "], PercentileProbes: ["; + for (auto el : s.pecentile_probes) { + out << el.first << ":" << el.second << ", "; + } + out << "]}"; + + return out; + } +}; + +void VerifyStats(size_t size, const ExpectedStats& exp, + const ProbeStats& stats) { + EXPECT_LT(stats.AvgRatio(), exp.avg_ratio) << size << " " << stats; + EXPECT_LT(stats.MaxRatio(), exp.max_ratio) << size << " " << stats; + for (auto pr : exp.pecentile_ratios) { + EXPECT_LE(stats.PercentileRatio(pr.first), pr.second) + << size << " " << pr.first << " " << stats; + } + + for (auto pr : exp.pecentile_probes) { + EXPECT_LE(stats.PercentileProbe(pr.first), pr.second) + << size << " " << pr.first << " " << stats; + } +} + +using ProbeStatsPerSize = std::map; + +// Collect total ProbeStats on num_iters iterations of the following algorithm: +// 1. Create new table and reserve it to keys.size() * 2 +// 2. Insert all keys xored with seed +// 3. Collect ProbeStats from final table. +ProbeStats CollectProbeStatsOnKeysXoredWithSeed(const std::vector& keys, + size_t num_iters) { + const size_t reserve_size = keys.size() * 2; + + ProbeStats stats; + + int64_t seed = 0x71b1a19b907d6e33; + while (num_iters--) { + seed = static_cast(static_cast(seed) * 17 + 13); + IntTable t1; + t1.reserve(reserve_size); + for (const auto& key : keys) { + t1.emplace(key ^ seed); + } + + auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1); + stats.all_probes_histogram.resize( + std::max(stats.all_probes_histogram.size(), probe_histogram.size())); + std::transform(probe_histogram.begin(), probe_histogram.end(), + stats.all_probes_histogram.begin(), + stats.all_probes_histogram.begin(), std::plus()); + + size_t total_probe_seq_length = 0; + for (size_t i = 0; i < probe_histogram.size(); ++i) { + total_probe_seq_length += i * probe_histogram[i]; + } + stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 / + keys.size()); + t1.erase(t1.begin(), t1.end()); + } + return stats; +} + +ExpectedStats XorSeedExpectedStats() { + constexpr bool kRandomizesInserts = +#if NDEBUG + false; +#else // NDEBUG + true; +#endif // NDEBUG + + // The effective load factor is larger in non-opt mode because we insert + // elements out of order. + switch (container_internal::Group::kWidth) { + case 8: + if (kRandomizesInserts) { + return {0.05, + 1.0, + {{0.95, 0.5}}, + {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}}; + } else { + return {0.05, + 2.0, + {{0.95, 0.1}}, + {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}}; + } + case 16: + if (kRandomizesInserts) { + return {0.1, + 1.0, + {{0.95, 0.1}}, + {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}}; + } else { + return {0.05, + 1.0, + {{0.95, 0.05}}, + {{0.95, 0}, {0.99, 1}, {0.999, 4}, {0.9999, 10}}}; + } + } + ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width"); + return {}; +} +TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) { + ProbeStatsPerSize stats; + std::vector sizes = {Group::kWidth << 5, Group::kWidth << 10}; + for (size_t size : sizes) { + stats[size] = + CollectProbeStatsOnKeysXoredWithSeed(CollectBadMergeKeys(size), 200); + } + auto expected = XorSeedExpectedStats(); + for (size_t size : sizes) { + auto& stat = stats[size]; + VerifyStats(size, expected, stat); + } +} + +// Collect total ProbeStats on num_iters iterations of the following algorithm: +// 1. Create new table +// 2. Select 10% of keys and insert 10 elements key * 17 + j * 13 +// 3. Collect ProbeStats from final table +ProbeStats CollectProbeStatsOnLinearlyTransformedKeys( + const std::vector& keys, size_t num_iters) { + ProbeStats stats; + + std::random_device rd; + std::mt19937 rng(rd()); + auto linear_transform = [](size_t x, size_t y) { return x * 17 + y * 13; }; + std::uniform_int_distribution dist(0, keys.size()-1); + while (num_iters--) { + IntTable t1; + size_t num_keys = keys.size() / 10; + size_t start = dist(rng); + for (size_t i = 0; i != num_keys; ++i) { + for (size_t j = 0; j != 10; ++j) { + t1.emplace(linear_transform(keys[(i + start) % keys.size()], j)); + } + } + + auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1); + stats.all_probes_histogram.resize( + std::max(stats.all_probes_histogram.size(), probe_histogram.size())); + std::transform(probe_histogram.begin(), probe_histogram.end(), + stats.all_probes_histogram.begin(), + stats.all_probes_histogram.begin(), std::plus()); + + size_t total_probe_seq_length = 0; + for (size_t i = 0; i < probe_histogram.size(); ++i) { + total_probe_seq_length += i * probe_histogram[i]; + } + stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 / + t1.size()); + t1.erase(t1.begin(), t1.end()); + } + return stats; +} + +ExpectedStats LinearTransformExpectedStats() { + constexpr bool kRandomizesInserts = +#if NDEBUG + false; +#else // NDEBUG + true; +#endif // NDEBUG + + // The effective load factor is larger in non-opt mode because we insert + // elements out of order. + switch (container_internal::Group::kWidth) { + case 8: + if (kRandomizesInserts) { + return {0.1, + 0.5, + {{0.95, 0.3}}, + {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}}; + } else { + return {0.15, + 0.5, + {{0.95, 0.3}}, + {{0.95, 0}, {0.99, 3}, {0.999, 15}, {0.9999, 25}}}; + } + case 16: + if (kRandomizesInserts) { + return {0.1, + 0.4, + {{0.95, 0.3}}, + {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}}; + } else { + return {0.05, + 0.2, + {{0.95, 0.1}}, + {{0.95, 0}, {0.99, 1}, {0.999, 6}, {0.9999, 10}}}; + } + } + ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width"); + return {}; +} +TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) { + ProbeStatsPerSize stats; + std::vector sizes = {Group::kWidth << 5, Group::kWidth << 10}; + for (size_t size : sizes) { + stats[size] = CollectProbeStatsOnLinearlyTransformedKeys( + CollectBadMergeKeys(size), 300); + } + auto expected = LinearTransformExpectedStats(); + for (size_t size : sizes) { + auto& stat = stats[size]; + VerifyStats(size, expected, stat); + } +} + +TEST(Table, EraseCollision) { + BadTable t; + + // 1 2 3 + t.emplace(1); + t.emplace(2); + t.emplace(3); + EXPECT_THAT(*t.find(1), 1); + EXPECT_THAT(*t.find(2), 2); + EXPECT_THAT(*t.find(3), 3); + EXPECT_EQ(3, t.size()); + + // 1 DELETED 3 + t.erase(t.find(2)); + EXPECT_THAT(*t.find(1), 1); + EXPECT_TRUE(t.find(2) == t.end()); + EXPECT_THAT(*t.find(3), 3); + EXPECT_EQ(2, t.size()); + + // DELETED DELETED 3 + t.erase(t.find(1)); + EXPECT_TRUE(t.find(1) == t.end()); + EXPECT_TRUE(t.find(2) == t.end()); + EXPECT_THAT(*t.find(3), 3); + EXPECT_EQ(1, t.size()); + + // DELETED DELETED DELETED + t.erase(t.find(3)); + EXPECT_TRUE(t.find(1) == t.end()); + EXPECT_TRUE(t.find(2) == t.end()); + EXPECT_TRUE(t.find(3) == t.end()); + EXPECT_EQ(0, t.size()); +} + +TEST(Table, EraseInsertProbing) { + BadTable t(100); + + // 1 2 3 4 + t.emplace(1); + t.emplace(2); + t.emplace(3); + t.emplace(4); + + // 1 DELETED 3 DELETED + t.erase(t.find(2)); + t.erase(t.find(4)); + + // 1 10 3 11 12 + t.emplace(10); + t.emplace(11); + t.emplace(12); + + EXPECT_EQ(5, t.size()); + EXPECT_THAT(t, UnorderedElementsAre(1, 10, 3, 11, 12)); +} + +TEST(Table, Clear) { + IntTable t; + EXPECT_TRUE(t.find(0) == t.end()); + t.clear(); + EXPECT_TRUE(t.find(0) == t.end()); + auto res = t.emplace(0); + EXPECT_TRUE(res.second); + EXPECT_EQ(1, t.size()); + t.clear(); + EXPECT_EQ(0, t.size()); + EXPECT_TRUE(t.find(0) == t.end()); +} + +TEST(Table, Swap) { + IntTable t; + EXPECT_TRUE(t.find(0) == t.end()); + auto res = t.emplace(0); + EXPECT_TRUE(res.second); + EXPECT_EQ(1, t.size()); + IntTable u; + t.swap(u); + EXPECT_EQ(0, t.size()); + EXPECT_EQ(1, u.size()); + EXPECT_TRUE(t.find(0) == t.end()); + EXPECT_THAT(*u.find(0), 0); +} + +TEST(Table, Rehash) { + IntTable t; + EXPECT_TRUE(t.find(0) == t.end()); + t.emplace(0); + t.emplace(1); + EXPECT_EQ(2, t.size()); + t.rehash(128); + EXPECT_EQ(2, t.size()); + EXPECT_THAT(*t.find(0), 0); + EXPECT_THAT(*t.find(1), 1); +} + +TEST(Table, RehashDoesNotRehashWhenNotNecessary) { + IntTable t; + t.emplace(0); + t.emplace(1); + auto* p = &*t.find(0); + t.rehash(1); + EXPECT_EQ(p, &*t.find(0)); +} + +TEST(Table, RehashZeroDoesNotAllocateOnEmptyTable) { + IntTable t; + t.rehash(0); + EXPECT_EQ(0, t.bucket_count()); +} + +TEST(Table, RehashZeroDeallocatesEmptyTable) { + IntTable t; + t.emplace(0); + t.clear(); + EXPECT_NE(0, t.bucket_count()); + t.rehash(0); + EXPECT_EQ(0, t.bucket_count()); +} + +TEST(Table, RehashZeroForcesRehash) { + IntTable t; + t.emplace(0); + t.emplace(1); + auto* p = &*t.find(0); + t.rehash(0); + EXPECT_NE(p, &*t.find(0)); +} + +TEST(Table, ConstructFromInitList) { + using P = std::pair; + struct Q { + operator P() const { return {}; } + }; + StringTable t = {P(), Q(), {}, {{}, {}}}; +} + +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")); +} + +struct ExplicitAllocIntTable + : raw_hash_set, + std::equal_to, Alloc> { + ExplicitAllocIntTable() {} +}; + +TEST(Table, AllocWithExplicitCtor) { + ExplicitAllocIntTable t; + EXPECT_EQ(0, t.size()); +} + +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"}}; + t.insert(std::begin(v), std::end(v)); + StringTable u = t; + EXPECT_EQ(u, t); +} + +TEST(Table, Equality2) { + StringTable t; + std::vector> v1 = {{"a", "b"}, {"aa", "bb"}}; + t.insert(std::begin(v1), std::end(v1)); + StringTable u; + 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"}}; + t.insert(std::begin(v1), std::end(v1)); + StringTable u; + std::vector> v2 = {{"a", "a"}, {"aa", "aa"}}; + u.insert(std::begin(v2), std::end(v2)); + EXPECT_NE(u, t); +} + +TEST(Table, NumDeletedRegression) { + IntTable t; + t.emplace(0); + t.erase(t.find(0)); + // construct over a deleted slot. + t.emplace(0); + t.clear(); +} + +TEST(Table, FindFullDeletedRegression) { + IntTable t; + for (int i = 0; i < 1000; ++i) { + t.emplace(i); + t.erase(t.find(i)); + } + EXPECT_EQ(0, t.size()); +} + +TEST(Table, ReplacingDeletedSlotDoesNotRehash) { + size_t n; + { + // Compute n such that n is the maximum number of elements before rehash. + IntTable t; + t.emplace(0); + size_t c = t.bucket_count(); + for (n = 1; c == t.bucket_count(); ++n) t.emplace(n); + --n; + } + IntTable t; + t.rehash(n); + const size_t c = t.bucket_count(); + for (size_t i = 0; i != n; ++i) t.emplace(i); + EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n; + t.erase(0); + t.emplace(0); + EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n; +} + +TEST(Table, NoThrowMoveConstruct) { + ASSERT_TRUE( + std::is_nothrow_copy_constructible>::value); + ASSERT_TRUE(std::is_nothrow_copy_constructible< + std::equal_to>::value); + ASSERT_TRUE(std::is_nothrow_copy_constructible>::value); + EXPECT_TRUE(std::is_nothrow_move_constructible::value); +} + +TEST(Table, NoThrowMoveAssign) { + ASSERT_TRUE( + std::is_nothrow_move_assignable>::value); + ASSERT_TRUE( + std::is_nothrow_move_assignable>::value); + ASSERT_TRUE(std::is_nothrow_move_assignable>::value); + ASSERT_TRUE( + absl::allocator_traits>::is_always_equal::value); + EXPECT_TRUE(std::is_nothrow_move_assignable::value); +} + +TEST(Table, NoThrowSwappable) { + ASSERT_TRUE( + container_internal::IsNoThrowSwappable>()); + ASSERT_TRUE(container_internal::IsNoThrowSwappable< + std::equal_to>()); + ASSERT_TRUE(container_internal::IsNoThrowSwappable>()); + EXPECT_TRUE(container_internal::IsNoThrowSwappable()); +} + +TEST(Table, HeterogeneousLookup) { + struct Hash { + size_t operator()(int64_t i) const { return i; } + size_t operator()(double i) const { + ADD_FAILURE(); + return i; + } + }; + struct Eq { + bool operator()(int64_t a, int64_t b) const { return a == b; } + bool operator()(double a, int64_t b) const { + ADD_FAILURE(); + return a == b; + } + bool operator()(int64_t a, double b) const { + ADD_FAILURE(); + return a == b; + } + bool operator()(double a, double b) const { + ADD_FAILURE(); + return a == b; + } + }; + + struct THash { + using is_transparent = void; + size_t operator()(int64_t i) const { return i; } + size_t operator()(double i) const { return i; } + }; + struct TEq { + using is_transparent = void; + bool operator()(int64_t a, int64_t b) const { return a == b; } + bool operator()(double a, int64_t b) const { return a == b; } + bool operator()(int64_t a, double b) const { return a == b; } + bool operator()(double a, double b) const { return a == b; } + }; + + raw_hash_set> s{0, 1, 2}; + // It will convert to int64_t before the query. + EXPECT_EQ(1, *s.find(double{1.1})); + + raw_hash_set> ts{0, 1, 2}; + // It will try to use the double, and fail to find the object. + EXPECT_TRUE(ts.find(1.1) == ts.end()); +} + +template +using CallFind = decltype(std::declval().find(17)); + +template +using CallErase = decltype(std::declval().erase(17)); + +template +using CallExtract = decltype(std::declval().extract(17)); + +template +using CallPrefetch = decltype(std::declval().prefetch(17)); + +template +using CallCount = decltype(std::declval().count(17)); + +template