diff options
Diffstat (limited to 'absl')
1040 files changed, 46891 insertions, 6466 deletions
diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index edd0274c..853330d4 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -25,13 +25,18 @@ create_llvm_config( visibility = [":__subpackages__"], ) -# following configs are based on mapping defined in: https://git.io/v5Ijz +config_setting( + name = "osx", + constraint_values = [ + "@bazel_tools//platforms:osx", + ], +) + config_setting( name = "ios", - values = { - "cpu": "darwin", - }, - visibility = [":__subpackages__"], + constraint_values = [ + "@bazel_tools//platforms:ios", + ], ) config_setting( diff --git a/absl/CMakeLists.txt b/absl/CMakeLists.txt index 1d09b193..3e78397c 100644 --- a/absl/CMakeLists.txt +++ b/absl/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -20,10 +20,12 @@ add_subdirectory(base) add_subdirectory(algorithm) add_subdirectory(container) add_subdirectory(debugging) +add_subdirectory(flags) add_subdirectory(hash) add_subdirectory(memory) add_subdirectory(meta) add_subdirectory(numeric) +add_subdirectory(random) add_subdirectory(strings) add_subdirectory(synchronization) add_subdirectory(time) diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel index d04dc712..c506f3b9 100644 --- a/absl/algorithm/BUILD.bazel +++ b/absl/algorithm/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,9 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -28,6 +29,7 @@ cc_library( name = "algorithm", hdrs = ["algorithm.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_test( @@ -35,6 +37,7 @@ cc_test( size = "small", srcs = ["algorithm_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":algorithm", "@com_google_googletest//:gtest_main", @@ -45,6 +48,7 @@ cc_test( name = "algorithm_benchmark", srcs = ["equal_benchmark.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], deps = [ ":algorithm", @@ -59,6 +63,7 @@ cc_library( "container.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":algorithm", "//absl/base:core_headers", @@ -70,6 +75,7 @@ cc_test( name = "container_test", srcs = ["container_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container", "//absl/base", diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt index 87a165c0..9fbe36f6 100644 --- a/absl/algorithm/CMakeLists.txt +++ b/absl/algorithm/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -29,6 +29,8 @@ absl_cc_test( algorithm_test SRCS "algorithm_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::algorithm gmock_main @@ -53,6 +55,8 @@ absl_cc_test( container_test SRCS "container_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::algorithm_container absl::base diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h index 1eef16cb..7c2b787e 100644 --- a/absl/algorithm/algorithm.h +++ b/absl/algorithm/algorithm.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,7 @@ #include <type_traits> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace algorithm_internal { @@ -95,7 +95,7 @@ It RotateImpl(It first, It middle, It last, std::false_type) { // then the predicate is never invoked and the function returns false. // // This is a C++11-compatible implementation of C++14 `std::equal`. See -// http://en.cppreference.com/w/cpp/algorithm/equal for more information. +// https://en.cppreference.com/w/cpp/algorithm/equal for more information. template <typename InputIter1, typename InputIter2, typename Pred> bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, InputIter2 last2, Pred&& pred) { @@ -146,7 +146,7 @@ ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator>()); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_ALGORITHM_ALGORITHM_H_ diff --git a/absl/algorithm/algorithm_test.cc b/absl/algorithm/algorithm_test.cc index e4322bc4..81fccb61 100644 --- a/absl/algorithm/algorithm_test.cc +++ b/absl/algorithm/algorithm_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index b7718206..16389be7 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -36,7 +36,6 @@ // For template parameter and variable naming, `C` indicates the container type // to which the function is applied, `Pred` indicates the predicate object type // to be used by the function and `T` indicates the applicable element type. -// #ifndef ABSL_ALGORITHM_CONTAINER_H_ #define ABSL_ALGORITHM_CONTAINER_H_ @@ -56,7 +55,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_algorithm_internal { // NOTE: it is important to defer to ADL lookup for building with C++ modules, @@ -512,6 +511,16 @@ OutputIterator c_move(C&& src, OutputIterator dest) { container_algorithm_internal::c_end(src), dest); } +// c_move_backward() +// +// Container-based version of the <algorithm> `std::move_backward()` function to +// move a container's elements into an iterator in reverse order. +template <typename C, typename BidirectionalIterator> +BidirectionalIterator c_move_backward(C&& src, BidirectionalIterator dest) { + return std::move_backward(container_algorithm_internal::c_begin(src), + container_algorithm_internal::c_end(src), dest); +} + // c_swap_ranges() // // Container-based version of the <algorithm> `std::swap_ranges()` function to @@ -649,7 +658,6 @@ container_algorithm_internal::ContainerIter<C> c_generate_n(C& c, Size n, // and `unique()` are omitted, because it's not clear whether or not such // functions should call erase on their supplied sequences afterwards. Either // behavior would be surprising for a different set of users. -// // c_remove_copy() // @@ -1698,7 +1706,7 @@ OutputIt c_partial_sum(const InputSequence& input, OutputIt output_first, output_first, std::forward<BinaryOp>(op)); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_ALGORITHM_CONTAINER_H_ diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc index 1502b17f..86bf9d3e 100644 --- a/absl/algorithm/container_test.cc +++ b/absl/algorithm/container_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -636,6 +636,19 @@ TEST(MutatingTest, Move) { Pointee(5))); } +TEST(MutatingTest, MoveBackward) { + std::vector<std::unique_ptr<int>> actual; + actual.emplace_back(absl::make_unique<int>(1)); + actual.emplace_back(absl::make_unique<int>(2)); + actual.emplace_back(absl::make_unique<int>(3)); + actual.emplace_back(absl::make_unique<int>(4)); + actual.emplace_back(absl::make_unique<int>(5)); + auto subrange = absl::MakeSpan(actual.data(), 3); + absl::c_move_backward(subrange, actual.end()); + EXPECT_THAT(actual, ElementsAre(IsNull(), IsNull(), Pointee(1), Pointee(2), + Pointee(3))); +} + TEST(MutatingTest, MoveWithRvalue) { auto MakeRValueSrc = [] { std::vector<std::unique_ptr<int>> src; diff --git a/absl/algorithm/equal_benchmark.cc b/absl/algorithm/equal_benchmark.cc index 19c0780c..7bf62c9a 100644 --- a/absl/algorithm/equal_benchmark.cc +++ b/absl/algorithm/equal_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 4566c697..a512272a 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,11 +15,12 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", - "ABSL_TEST_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_EXCEPTIONS_FLAG", "ABSL_EXCEPTIONS_FLAG_LINKOPTS", + "ABSL_TEST_COPTS", ) package(default_visibility = ["//visibility:public"]) @@ -27,6 +28,34 @@ package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 cc_library( + name = "atomic_hook", + hdrs = ["internal/atomic_hook.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], +) + +cc_library( + name = "log_severity", + srcs = ["log_severity.cc"], + hdrs = ["log_severity.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [":core_headers"], +) + +cc_library( + name = "raw_logging_internal", + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], +) + +cc_library( name = "spinlock_wait", srcs = [ "internal/spinlock_akaros.inc", @@ -40,6 +69,7 @@ cc_library( "internal/spinlock_wait.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/base:__pkg__", ], @@ -53,6 +83,7 @@ cc_library( "policy_checks.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_library( @@ -61,18 +92,24 @@ cc_library( hdrs = ["dynamic_annotations.h"], copts = ABSL_DEFAULT_COPTS, defines = ["__CLANG_SUPPORT_DYN_ANNOTATION__"], + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_library( name = "core_headers", + srcs = [ + "internal/thread_annotations.h", + ], hdrs = [ "attributes.h", + "const_init.h", "macros.h", "optimization.h", "port.h", "thread_annotations.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", ], @@ -88,6 +125,10 @@ cc_library( "internal/low_level_alloc.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = select({ + "//absl:windows": [], + "//conditions:default": ["-pthread"], + }) + ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -110,9 +151,13 @@ cc_library( "internal/scheduling_mode.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], + deps = [ + "//absl/meta:type_traits", + ], ) cc_library( @@ -128,7 +173,6 @@ cc_library( hdrs = [ "call_once.h", "casts.h", - "internal/atomic_hook.h", "internal/cycleclock.h", "internal/low_level_scheduling.h", "internal/per_thread_tls.h", @@ -138,15 +182,21 @@ cc_library( "internal/thread_identity.h", "internal/tsan_mutex_interface.h", "internal/unscaledcycleclock.h", - "log_severity.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = select({ + "//absl:windows": [], + "//conditions:default": ["-pthread"], + }) + ABSL_DEFAULT_LINKOPTS, deps = [ + ":atomic_hook", ":base_internal", ":config", ":core_headers", ":dynamic_annotations", + ":log_severity", ":spinlock_wait", + "//absl/meta:type_traits", ], ) @@ -155,8 +205,9 @@ cc_test( size = "small", srcs = ["internal/atomic_hook_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":base", + ":atomic_hook", ":core_headers", "@com_google_googletest//:gtest_main", ], @@ -169,6 +220,7 @@ cc_test( "bit_cast_test.cc", ], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -181,7 +233,7 @@ cc_library( srcs = ["internal/throw_delegate.cc"], hdrs = ["internal/throw_delegate.h"], copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -195,7 +247,7 @@ cc_test( name = "throw_delegate_test", srcs = ["throw_delegate_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":throw_delegate", "@com_google_googletest//:gtest_main", @@ -207,6 +259,7 @@ cc_library( testonly = 1, hdrs = ["internal/exception_testing.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -219,6 +272,7 @@ cc_library( cc_library( name = "pretty_function", hdrs = ["internal/pretty_function.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//absl:__subpackages__"], ) @@ -228,9 +282,8 @@ cc_library( srcs = ["internal/exception_safety_testing.cc"], hdrs = ["internal/exception_safety_testing.h"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ - ":base", ":config", ":pretty_function", "//absl/memory", @@ -245,7 +298,7 @@ cc_test( name = "exception_safety_testing_test", srcs = ["exception_safety_testing_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":exception_safety_testing", "//absl/memory", @@ -263,6 +316,7 @@ cc_test( "internal/inline_variable_testing.h", ], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base_internal", "@com_google_googletest//:gtest_main", @@ -274,6 +328,7 @@ cc_test( size = "small", srcs = ["invoke_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base_internal", "//absl/memory", @@ -289,6 +344,7 @@ cc_library( testonly = 1, srcs = ["spinlock_test_common.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -304,7 +360,7 @@ cc_test( size = "medium", srcs = ["spinlock_test_common.cc"], copts = ABSL_TEST_COPTS, - tags = ["no_test_wasm"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -318,7 +374,8 @@ cc_library( name = "spinlock_benchmark_common", testonly = 1, srcs = ["internal/spinlock_benchmark.cc"], - copts = ABSL_DEFAULT_COPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/base:__pkg__", ], @@ -335,6 +392,7 @@ cc_binary( name = "spinlock_benchmark", testonly = 1, copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ ":spinlock_benchmark_common", @@ -348,6 +406,7 @@ cc_library( "internal/unaligned_access.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", ":core_headers", @@ -359,7 +418,6 @@ cc_test( srcs = ["internal/endian_test.cc"], copts = ABSL_TEST_COPTS, deps = [ - ":base", ":config", ":endian", "@com_google_googletest//:gtest_main", @@ -370,9 +428,7 @@ cc_test( name = "config_test", srcs = ["config_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_wasm", - ], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":config", "//absl/synchronization:thread_pool", @@ -384,9 +440,7 @@ cc_test( name = "call_once_test", srcs = ["call_once_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_wasm", - ], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -399,6 +453,7 @@ cc_test( name = "raw_logging_test", srcs = ["raw_logging_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", "//absl/strings", @@ -411,6 +466,7 @@ cc_test( size = "small", srcs = ["internal/sysinfo_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", "//absl/synchronization", @@ -420,13 +476,10 @@ cc_test( cc_test( name = "low_level_alloc_test", - size = "small", + size = "medium", srcs = ["internal/low_level_alloc_test.cc"], copts = ABSL_TEST_COPTS, - linkopts = select({ - "//absl:windows": [], - "//conditions:default": ["-pthread"], - }), + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["no_test_ios_x86_64"], deps = [":malloc_internal"], ) @@ -436,13 +489,7 @@ cc_test( size = "small", srcs = ["internal/thread_identity_test.cc"], copts = ABSL_TEST_COPTS, - linkopts = select({ - "//absl:windows": [], - "//conditions:default": ["-pthread"], - }), - tags = [ - "no_test_wasm", - ], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", ":core_headers", @@ -455,6 +502,7 @@ cc_test( name = "thread_identity_benchmark", srcs = ["internal/thread_identity_benchmark.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], visibility = ["//visibility:private"], deps = [ @@ -467,6 +515,7 @@ cc_test( cc_library( name = "bits", hdrs = ["internal/bits.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -478,8 +527,46 @@ cc_test( size = "small", srcs = ["internal/bits_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bits", "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "scoped_set_env", + testonly = 1, + srcs = ["internal/scoped_set_env.cc"], + hdrs = ["internal/scoped_set_env.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [":base"], +) + +cc_test( + name = "scoped_set_env_test", + size = "small", + srcs = ["internal/scoped_set_env_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":scoped_set_env", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "log_severity_test", + size = "small", + srcs = ["log_severity_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":base", + ":log_severity", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 212dd083..cc7960e3 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -16,6 +16,35 @@ absl_cc_library( NAME + atomic_hook + HDRS + "internal/atomic_hook.h" + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_library( + NAME + log_severity + HDRS + "log_severity.h" + SRCS + "log_severity.cc" + DEPS + absl::core_headers + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_library( + NAME + raw_logging_internal + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_library( + NAME spinlock_wait HDRS "internal/scheduling_mode.h" @@ -26,6 +55,8 @@ absl_cc_library( "internal/spinlock_posix.inc" "internal/spinlock_wait.cc" "internal/spinlock_win32.inc" + COPTS + ${ABSL_DEFAULT_COPTS} DEPS absl::core_headers ) @@ -60,10 +91,12 @@ absl_cc_library( core_headers HDRS "attributes.h" + "const_init.h" "macros.h" "optimization.h" "port.h" "thread_annotations.h" + "internal/thread_annotations.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -87,6 +120,7 @@ absl_cc_library( absl::core_headers absl::dynamic_annotations absl::spinlock_wait + Threads::Threads ) absl_cc_library( @@ -99,6 +133,8 @@ absl_cc_library( "internal/invoke.h" COPTS ${ABSL_DEFAULT_COPTS} + DEPS + absl::type_traits ) absl_cc_library( @@ -107,7 +143,6 @@ absl_cc_library( HDRS "call_once.h" "casts.h" - "internal/atomic_hook.h" "internal/cycleclock.h" "internal/low_level_scheduling.h" "internal/per_thread_tls.h" @@ -125,14 +160,19 @@ absl_cc_library( "internal/sysinfo.cc" "internal/thread_identity.cc" "internal/unscaledcycleclock.cc" + "log_severity.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::atomic_hook absl::base_internal absl::config absl::core_headers absl::dynamic_annotations + absl::log_severity absl::spinlock_wait + absl::type_traits + Threads::Threads PUBLIC ) @@ -180,10 +220,11 @@ absl_cc_library( SRCS "internal/exception_safety_testing.cc" COPTS - ${ABSL_DEFAULT_COPTS} + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS - absl::base absl::config absl::pretty_function absl::memory @@ -200,6 +241,7 @@ absl_cc_test( SRCS "exception_safety_testing_test.cc" COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} LINKOPTS ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} @@ -214,8 +256,10 @@ absl_cc_test( atomic_hook_test SRCS "internal/atomic_hook_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS - absl::base + absl::atomic_hook absl::core_headers gtest_main ) @@ -225,6 +269,8 @@ absl_cc_test( bit_cast_test SRCS "bit_cast_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::core_headers @@ -236,9 +282,11 @@ absl_cc_test( throw_delegate_test SRCS "throw_delegate_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base - absl_internal_throw_delegate + absl::throw_delegate gtest_main ) @@ -250,6 +298,8 @@ absl_cc_test( "inline_variable_test.cc" "inline_variable_test_a.cc" "inline_variable_test_b.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base_internal gtest_main @@ -260,6 +310,8 @@ absl_cc_test( invoke_test SRCS "invoke_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base_internal absl::memory @@ -290,6 +342,8 @@ absl_cc_test( spinlock_test SRCS "spinlock_test_common.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::core_headers @@ -317,6 +371,8 @@ absl_cc_test( endian_test SRCS "internal/endian_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::config @@ -329,6 +385,8 @@ absl_cc_test( config_test SRCS "config_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::config absl::synchronization @@ -340,6 +398,8 @@ absl_cc_test( call_once_test SRCS "call_once_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::core_headers @@ -352,6 +412,8 @@ absl_cc_test( raw_logging_test SRCS "raw_logging_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::strings @@ -363,6 +425,8 @@ absl_cc_test( sysinfo_test SRCS "internal/sysinfo_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::synchronization @@ -374,6 +438,8 @@ absl_cc_test( low_level_alloc_test SRCS "internal/low_level_alloc_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::malloc_internal Threads::Threads @@ -384,6 +450,8 @@ absl_cc_test( thread_identity_test SRCS "internal/thread_identity_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::base absl::core_headers @@ -408,7 +476,57 @@ absl_cc_test( bits_test SRCS "internal/bits_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::bits gtest_main ) + +absl_cc_library( + NAME + scoped_set_env + SRCS + "internal/scoped_set_env.cc" + HDRS + "internal/scoped_set_env.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base +) + +absl_cc_test( + NAME + scoped_set_env_test + SRCS + "internal/scoped_set_env_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::scoped_set_env + gtest_main +) + +absl_cc_test( + NAME + cmake_thread_test + SRCS + "internal/cmake_thread_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base +) + +absl_cc_test( + NAME + log_severity_test + SRCS + "log_severity_test.cc" + DEPS + absl::base + absl::log_severity + gmock + gtest_main +) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 291ad89e..a7da62af 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -80,7 +80,7 @@ // // A function-like feature checking macro that accepts C++11 style attributes. // It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6 -// (http://en.cppreference.com/w/cpp/experimental/feature_test). If we don't +// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't // find `__has_cpp_attribute`, will evaluate to 0. #if defined(__cplusplus) && defined(__has_cpp_attribute) // NOTE: requiring __cplusplus above should not be necessary, but @@ -102,7 +102,7 @@ // // Tells the compiler to perform `printf` format string checking if the // compiler supports it; see the 'format' attribute in -// <http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>. +// <https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>. // // Note: As the GCC manual states, "[s]ince non-static C++ methods // have an implicit 'this' argument, the arguments of such methods @@ -232,7 +232,7 @@ // out of bounds or does other scary things with memory. // NOTE: GCC supports AddressSanitizer(asan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) && defined(ADDRESS_SANITIZER) +#if defined(__GNUC__) #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS @@ -246,7 +246,7 @@ // This attribute is similar to the ADDRESS_SANITIZER attribute above, but deals // with initialized-ness rather than addressability issues. // NOTE: MemorySanitizer(msan) is supported by Clang but not GCC. -#if defined(__GNUC__) && defined(MEMORY_SANITIZER) +#if defined(__clang__) #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY @@ -257,7 +257,7 @@ // Tells the ThreadSanitizer to not instrument a given function. // NOTE: GCC supports ThreadSanitizer(tsan) since 4.8. // https://gcc.gnu.org/gcc-4.8/changes.html -#if defined(__GNUC__) && defined(THREAD_SANITIZER) +#if defined(__GNUC__) #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) #else #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD @@ -287,6 +287,17 @@ #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI #endif +// ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +// +// Tells the SafeStack to not instrument a given function. +// See https://clang.llvm.org/docs/SafeStack.html for details. +#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER) +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \ + __attribute__((no_sanitize("safe-stack"))) +#else +#define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK +#endif + // ABSL_ATTRIBUTE_RETURNS_NONNULL // // Tells the compiler that a particular function never returns a null pointer. diff --git a/absl/base/bit_cast_test.cc b/absl/base/bit_cast_test.cc index 5af036df..be201856 100644 --- a/absl/base/bit_cast_test.cc +++ b/absl/base/bit_cast_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/base/macros.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { template <int N> @@ -105,5 +105,5 @@ TEST(BitCast, Double) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/call_once.h b/absl/base/call_once.h index aea9197b..373f015f 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -29,6 +29,7 @@ #include <atomic> #include <cstdint> #include <type_traits> +#include <utility> #include "absl/base/internal/invoke.h" #include "absl/base/internal/low_level_scheduling.h" @@ -36,10 +37,11 @@ #include "absl/base/internal/scheduling_mode.h" #include "absl/base/internal/spinlock_wait.h" #include "absl/base/macros.h" +#include "absl/base/optimization.h" #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { class once_flag; @@ -208,7 +210,7 @@ void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) { } } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_CALL_ONCE_H_ diff --git a/absl/base/call_once_test.cc b/absl/base/call_once_test.cc index 4d98a405..0e89bd24 100644 --- a/absl/base/call_once_test.cc +++ b/absl/base/call_once_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,15 +18,18 @@ #include <vector> #include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { absl::once_flag once; -Mutex counters_mu; + +ABSL_CONST_INIT Mutex counters_mu(absl::kConstInit); int running_thread_count GUARDED_BY(counters_mu) = 0; int call_once_invoke_count GUARDED_BY(counters_mu) = 0; @@ -100,5 +103,5 @@ TEST(CallOnceTest, ExecutionCount) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/casts.h b/absl/base/casts.h index bba623b4..b67b047c 100644 --- a/absl/base/casts.h +++ b/absl/base/casts.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,32 +27,25 @@ #include <cstring> #include <memory> #include <type_traits> +#include <utility> #include "absl/base/internal/identity.h" #include "absl/base/macros.h" +#include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace internal_casts { -// NOTE: Not a fully compliant implementation of `std::is_trivially_copyable`. -// TODO(calabrese) Branch on implementations that directly provide -// `std::is_trivially_copyable`, create a more rigorous workaround, and publicly -// expose in meta/type_traits. -template <class T> -struct is_trivially_copyable - : std::integral_constant< - bool, std::is_destructible<T>::value&& __has_trivial_destructor(T) && - __has_trivial_copy(T) && __has_trivial_assign(T)> {}; - template <class Dest, class Source> struct is_bitcastable - : std::integral_constant<bool, - sizeof(Dest) == sizeof(Source) && - is_trivially_copyable<Source>::value && - is_trivially_copyable<Dest>::value && - std::is_default_constructible<Dest>::value> {}; + : std::integral_constant< + bool, + sizeof(Dest) == sizeof(Source) && + type_traits_internal::is_trivially_copyable<Source>::value && + type_traits_internal::is_trivially_copyable<Dest>::value && + std::is_default_constructible<Dest>::value> {}; } // namespace internal_casts @@ -185,7 +178,7 @@ inline Dest bit_cast(const Source& source) { return dest; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_CASTS_H_ diff --git a/absl/base/config.h b/absl/base/config.h index db4c4539..1c3cb08e 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -118,7 +118,7 @@ // Checks whether `std::is_trivially_copy_assignable<T>` is supported. // Notes: Clang with libc++ supports these features, as does gcc >= 5.1 with -// either libc++ or libstdc++, and Visual Studio. +// either libc++ or libstdc++, and Visual Studio (but not NVCC). #if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) #error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set #elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) @@ -127,7 +127,7 @@ (!defined(__clang__) && defined(__GNUC__) && \ (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \ (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \ - defined(_MSC_VER) + (defined(_MSC_VER) && !defined(__NVCC__)) #define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 #endif @@ -181,6 +181,13 @@ #endif #endif // defined(__ANDROID__) && defined(__clang__) +// Emscripten doesn't yet support `thread_local` or `__thread`. +// https://github.com/emscripten-core/emscripten/issues/3502 +#if defined(__EMSCRIPTEN__) +#undef ABSL_HAVE_TLS +#undef ABSL_HAVE_THREAD_LOCAL +#endif // defined(__EMSCRIPTEN__) + // ABSL_HAVE_INTRINSIC_INT128 // // Checks whether the __int128 compiler extension for a 128-bit integral type is @@ -191,15 +198,13 @@ // * On Clang: // * Building using Clang for Windows, where the Clang runtime library has // 128-bit support only on LP64 architectures, but Windows is LLP64. -// * Building for aarch64, where __int128 exists but has exhibits a sporadic -// compiler crashing bug. // * On Nvidia's nvcc: // * nvcc also defines __GNUC__ and __SIZEOF_INT128__, but not all versions // actually support __int128. #ifdef ABSL_HAVE_INTRINSIC_INT128 #error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set #elif defined(__SIZEOF_INT128__) -#if (defined(__clang__) && !defined(_WIN32) && !defined(__aarch64__)) || \ +#if (defined(__clang__) && !defined(_WIN32)) || \ (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \ (defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__)) #define ABSL_HAVE_INTRINSIC_INT128 1 @@ -255,7 +260,7 @@ // Linux and Linux-derived __linux__ // Android __ANDROID__ (implies __linux__) // Linux (non-Android) __linux__ && !__ANDROID__ -// Darwin (Mac OS X and iOS) __APPLE__ +// Darwin (macOS and iOS) __APPLE__ // Akaros (http://akaros.org) __ros__ // Windows _WIN32 // NaCL __native_client__ @@ -365,16 +370,25 @@ #error "absl endian detection needs to be set up for your compiler" #endif -// MacOS 10.13 doesn't let you use <any>, <optional>, or <variant> even though -// the headers exist and are publicly noted to work. See +// macOS 10.13 and iOS 10.11 don't let you use <any>, <optional>, or <variant> +// even though the headers exist and are publicly noted to work. See // https://github.com/abseil/abseil-cpp/issues/207 and // https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes +// libc++ spells out the availability requirements in the file +// llvm-project/libcxx/include/__config via the #define +// _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. #if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ - defined(__MAC_OS_X_VERSION_MIN_REQUIRED__) && \ - __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400 -#define ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES 1 + ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ + (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 120000) || \ + (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ + __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 50000)) +#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1 #else -#define ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES 0 +#define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0 #endif // ABSL_HAVE_STD_ANY @@ -386,7 +400,7 @@ #ifdef __has_include #if __has_include(<any>) && __cplusplus >= 201703L && \ - ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_ANY 1 #endif #endif @@ -400,7 +414,7 @@ #ifdef __has_include #if __has_include(<optional>) && __cplusplus >= 201703L && \ - ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_OPTIONAL 1 #endif #endif @@ -414,7 +428,7 @@ #ifdef __has_include #if __has_include(<variant>) && __cplusplus >= 201703L && \ - ABSL_INTERNAL_MACOS_HAS_CXX_17_TYPES + !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE #define ABSL_HAVE_STD_VARIANT 1 #endif #endif diff --git a/absl/base/config_test.cc b/absl/base/config_test.cc index c839712a..7e0c033d 100644 --- a/absl/base/config_test.cc +++ b/absl/base/config_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/const_init.h b/absl/base/const_init.h new file mode 100644 index 00000000..30d6a3ca --- /dev/null +++ b/absl/base/const_init.h @@ -0,0 +1,74 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// kConstInit +// ----------------------------------------------------------------------------- +// +// A constructor tag used to mark an object as safe for use as a global +// variable, avoiding the usual lifetime issues that can affect globals. + +#ifndef ABSL_BASE_CONST_INIT_H_ +#define ABSL_BASE_CONST_INIT_H_ + +// In general, objects with static storage duration (such as global variables) +// can trigger tricky object lifetime situations. Attempting to access them +// from the constructors or destructors of other global objects can result in +// undefined behavior, unless their constructors and destructors are designed +// with this issue in mind. +// +// The normal way to deal with this issue in C++11 is to use constant +// initialization and trivial destructors. +// +// Constant initialization is guaranteed to occur before any other code +// executes. Constructors that are declared 'constexpr' are eligible for +// constant initialization. You can annotate a variable declaration with the +// ABSL_CONST_INIT macro to express this intent. For compilers that support +// it, this annotation will cause a compilation error for declarations that +// aren't subject to constant initialization (perhaps because a runtime value +// was passed as a constructor argument). +// +// On program shutdown, lifetime issues can be avoided on global objects by +// ensuring that they contain trivial destructors. A class has a trivial +// destructor unless it has a user-defined destructor, a virtual method or base +// class, or a data member or base class with a non-trivial destructor of its +// own. Objects with static storage duration and a trivial destructor are not +// cleaned up on program shutdown, and are thus safe to access from other code +// running during shutdown. +// +// For a few core Abseil classes, we make a best effort to allow for safe global +// instances, even though these classes have non-trivial destructors. These +// objects can be created with the absl::kConstInit tag. For example: +// ABSL_CONST_INIT absl::Mutex global_mutex(absl::kConstInit); +// +// The line above declares a global variable of type absl::Mutex which can be +// accessed at any point during startup or shutdown. global_mutex's destructor +// will still run, but will not invalidate the object. Note that C++ specifies +// that accessing an object after its destructor has run results in undefined +// behavior, but this pattern works on the toolchains we support. +// +// The absl::kConstInit tag should only be used to define objects with static +// or thread_local storage duration. + +namespace absl { +inline namespace lts_2019_08_08 { + +enum ConstInitType { + kConstInit, +}; + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_BASE_CONST_INIT_H_ diff --git a/absl/base/dynamic_annotations.cc b/absl/base/dynamic_annotations.cc index 08c27e51..21e822e5 100644 --- a/absl/base/dynamic_annotations.cc +++ b/absl/base/dynamic_annotations.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/dynamic_annotations.h b/absl/base/dynamic_annotations.h index 7e328d96..65a54b44 100644 --- a/absl/base/dynamic_annotations.h +++ b/absl/base/dynamic_annotations.h @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -139,6 +139,7 @@ #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */ #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) /* empty */ #endif /* DYNAMIC_ANNOTATIONS_ENABLED || MEMORY_SANITIZER */ + /* TODO(delesley) -- Replace __CLANG_SUPPORT_DYN_ANNOTATION__ with the appropriate feature ID. */ #if defined(__clang__) && (!defined(SWIG)) \ @@ -376,7 +377,7 @@ inline T ANNOTATE_UNPROTECTED_READ(const volatile T &x) { /* NOLINT */ struct { char x[8] __attribute__ ((aligned (8))); } name #else #define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) -#define ADDRESS_SANITIZER_REDZONE(name) +#define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") #endif // ADDRESS_SANITIZER /* Undefine the macros intended only in this file. */ diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc index 7518264d..2ed38606 100644 --- a/absl/base/exception_safety_testing_test.cc +++ b/absl/base/exception_safety_testing_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/inline_variable_test.cc b/absl/base/inline_variable_test.cc index b968b10f..40e8b57b 100644 --- a/absl/base/inline_variable_test.cc +++ b/absl/base/inline_variable_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace inline_variable_testing_internal { namespace { @@ -60,5 +60,5 @@ TEST(InlineVariableTest, FunPtrType) { } // namespace } // namespace inline_variable_testing_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/inline_variable_test_a.cc b/absl/base/inline_variable_test_a.cc index a51b1d81..c19a6e55 100644 --- a/absl/base/inline_variable_test_a.cc +++ b/absl/base/inline_variable_test_a.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace inline_variable_testing_internal { const Foo& get_foo_a() { return inline_variable_foo; } @@ -23,5 +23,5 @@ const Foo& get_foo_a() { return inline_variable_foo; } const int& get_int_a() { return inline_variable_int; } } // namespace inline_variable_testing_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/inline_variable_test_b.cc b/absl/base/inline_variable_test_b.cc index 5041e20a..ae8da104 100644 --- a/absl/base/inline_variable_test_b.cc +++ b/absl/base/inline_variable_test_b.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,7 @@ #include "absl/base/internal/inline_variable_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace inline_variable_testing_internal { const Foo& get_foo_b() { return inline_variable_foo; } @@ -23,5 +23,5 @@ const Foo& get_foo_b() { return inline_variable_foo; } const int& get_int_b() { return inline_variable_int; } } // namespace inline_variable_testing_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h index 58ddf272..6757e75b 100644 --- a/absl/base/internal/atomic_hook.h +++ b/absl/base/internal/atomic_hook.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { template <typename T> @@ -161,7 +161,7 @@ class AtomicHook<ReturnType (*)(Args...)> { #undef ABSL_HAVE_WORKING_ATOMIC_POINTER } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ diff --git a/absl/base/internal/atomic_hook_test.cc b/absl/base/internal/atomic_hook_test.cc index cf740757..ecc80406 100644 --- a/absl/base/internal/atomic_hook_test.cc +++ b/absl/base/internal/atomic_hook_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h index 29657426..ef978e9b 100644 --- a/absl/base/internal/bits.h +++ b/absl/base/internal/bits.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -46,7 +46,7 @@ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { @@ -189,7 +189,7 @@ ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { #undef ABSL_BASE_INTERNAL_FORCEINLINE } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_BITS_H_ diff --git a/absl/base/internal/bits_test.cc b/absl/base/internal/bits_test.cc index e5d991d6..7855fa62 100644 --- a/absl/base/internal/bits_test.cc +++ b/absl/base/internal/bits_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/cmake_thread_test.cc b/absl/base/internal/cmake_thread_test.cc new file mode 100644 index 00000000..f70bb24e --- /dev/null +++ b/absl/base/internal/cmake_thread_test.cc @@ -0,0 +1,22 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <iostream> +#include "absl/base/internal/thread_identity.h" + +int main() { + auto* tid = absl::base_internal::CurrentThreadIdentityIfPresent(); + // Make sure the above call can't be optimized out + std::cout << (void*)tid << std::endl; +} diff --git a/absl/base/internal/cycleclock.cc b/absl/base/internal/cycleclock.cc index d99b63d3..9868e549 100644 --- a/absl/base/internal/cycleclock.cc +++ b/absl/base/internal/cycleclock.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,12 +22,13 @@ #include "absl/base/internal/cycleclock.h" +#include <atomic> #include <chrono> // NOLINT(build/c++11) #include "absl/base/internal/unscaledcycleclock.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { #if ABSL_USE_UNSCALED_CYCLECLOCK @@ -53,17 +54,40 @@ static constexpr int32_t kShift = 2; #endif static constexpr double kFrequencyScale = 1.0 / (1 << kShift); +static std::atomic<CycleClockSourceFunc> cycle_clock_source; + +CycleClockSourceFunc LoadCycleClockSource() { + // Optimize for the common case (no callback) by first doing a relaxed load; + // this is significantly faster on non-x86 platforms. + if (cycle_clock_source.load(std::memory_order_relaxed) == nullptr) { + return nullptr; + } + // This corresponds to the store(std::memory_order_release) in + // CycleClockSource::Register, and makes sure that any updates made prior to + // registering the callback are visible to this thread before the callback is + // invoked. + return cycle_clock_source.load(std::memory_order_acquire); +} } // namespace int64_t CycleClock::Now() { - return base_internal::UnscaledCycleClock::Now() >> kShift; + auto fn = LoadCycleClockSource(); + if (fn == nullptr) { + return base_internal::UnscaledCycleClock::Now() >> kShift; + } + return fn() >> kShift; } double CycleClock::Frequency() { return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency(); } +void CycleClockSource::Register(CycleClockSourceFunc source) { + // Corresponds to the load(std::memory_order_acquire) in LoadCycleClockSource. + cycle_clock_source.store(source, std::memory_order_release); +} + #else int64_t CycleClock::Now() { @@ -79,5 +103,5 @@ double CycleClock::Frequency() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/cycleclock.h b/absl/base/internal/cycleclock.h index ae5ede3e..39bce06a 100644 --- a/absl/base/internal/cycleclock.h +++ b/absl/base/internal/cycleclock.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,6 @@ // not necessarily "CPU cycles" and code should not rely on that behavior, even // if experimentally observed. // -// // An arbitrary offset may have been added to the counter at power on. // // On some platforms, the rate and offset of the counter may differ @@ -46,7 +45,7 @@ #include <cstdint> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // ----------------------------------------------------------------------------- @@ -72,8 +71,22 @@ class CycleClock { CycleClock& operator=(const CycleClock&) = delete; }; +using CycleClockSourceFunc = int64_t (*)(); + +class CycleClockSource { + private: + // CycleClockSource::Register() + // + // Register a function that provides an alternate source for the unscaled CPU + // cycle count value. The source function must be async signal safe, must not + // call CycleClock::Now(), and must have a frequency that matches that of the + // unscaled clock used by CycleClock. A nullptr value resets CycleClock to use + // the default source. + static void Register(CycleClockSourceFunc source); +}; + } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_CYCLECLOCK_H_ diff --git a/absl/base/internal/direct_mmap.h b/absl/base/internal/direct_mmap.h index f064e363..2e5e422c 100644 --- a/absl/base/internal/direct_mmap.h +++ b/absl/base/internal/direct_mmap.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -62,7 +62,7 @@ extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); #endif // __BIONIC__ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // Platform specific logic extracted from @@ -129,7 +129,7 @@ inline int DirectMunmap(void* start, size_t length) { } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else // !__linux__ @@ -138,7 +138,7 @@ inline int DirectMunmap(void* start, size_t length) { // actual mmap()/munmap() methods. namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, @@ -151,7 +151,7 @@ inline int DirectMunmap(void* start, size_t length) { } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // __linux__ diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index 52c09c5b..8643bffa 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #ifdef _MSC_VER #include <stdlib.h> // NOLINT(build/include) #elif defined(__APPLE__) -// Mac OS X / Darwin features +// macOS / Darwin features #include <libkern/OSByteOrder.h> #elif defined(__FreeBSD__) #include <sys/endian.h> @@ -34,7 +34,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Use compiler byte-swapping intrinsics if they are available. 32-bit // and 64-bit versions are available in Clang and GCC as of GCC 4.3.0. @@ -76,7 +76,7 @@ inline uint64_t gbswap_64(uint64_t host_int) { if (__builtin_constant_p(host_int)) { return __bswap_constant_64(host_int); } else { - register uint64_t result; + uint64_t result; __asm__("bswap %0" : "=r"(result) : "0"(host_int)); return result; } @@ -268,7 +268,7 @@ inline void Store64(void *p, uint64_t v) { } // namespace big_endian -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_ENDIAN_H_ diff --git a/absl/base/internal/endian_test.cc b/absl/base/internal/endian_test.cc index 14ac4765..0c683286 100644 --- a/absl/base/internal/endian_test.cc +++ b/absl/base/internal/endian_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { const uint64_t kInitialNumber{0x0123456789abcdef}; @@ -261,5 +261,5 @@ TEST(EndianessTest, big_endian) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc index 8207b7d7..6ef4325c 100644 --- a/absl/base/internal/exception_safety_testing.cc +++ b/absl/base/internal/exception_safety_testing.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h index d4d41a8a..be38ba54 100644 --- a/absl/base/internal/exception_safety_testing.h +++ b/absl/base/internal/exception_safety_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -169,8 +169,10 @@ class ConstructorTracker { return current_tracker_instance_ != nullptr; } - static std::string ErrorMessage(void* address, const std::string& address_description, - int countdown, const std::string& error_description) { + static std::string ErrorMessage(void* address, + const std::string& address_description, + int countdown, + const std::string& error_description) { return absl::Substitute( "With coundtown at $0:\n" " $1\n" @@ -556,8 +558,8 @@ class ThrowingValue : private exceptions_internal::TrackedObject { // We provide both regular and templated operator delete because if only the // templated version is provided as we did with operator new, the compiler has // no way of knowing which overload of operator delete to call. See - // http://en.cppreference.com/w/cpp/memory/new/operator_delete and - // http://en.cppreference.com/w/cpp/language/delete for the gory details. + // https://en.cppreference.com/w/cpp/memory/new/operator_delete and + // https://en.cppreference.com/w/cpp/language/delete for the gory details. void operator delete(void* p) noexcept { ::operator delete(p); } template <typename... Args> diff --git a/absl/base/internal/exception_testing.h b/absl/base/internal/exception_testing.h index 0cf7918e..01b54655 100644 --- a/absl/base/internal/exception_testing.h +++ b/absl/base/internal/exception_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/hide_ptr.h b/absl/base/internal/hide_ptr.h index ce390dc4..3720db18 100644 --- a/absl/base/internal/hide_ptr.h +++ b/absl/base/internal/hide_ptr.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include <cstdint> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // Arbitrary value with high bits set. Xor'ing with it is unlikely @@ -43,7 +43,7 @@ inline T* UnhidePtr(uintptr_t hidden) { } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_ diff --git a/absl/base/internal/identity.h b/absl/base/internal/identity.h index d57c83f4..b9d0f621 100644 --- a/absl/base/internal/identity.h +++ b/absl/base/internal/identity.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #define ABSL_BASE_INTERNAL_IDENTITY_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace internal { template <typename T> @@ -29,7 +29,7 @@ template <typename T> using identity_t = typename identity<T>::type; } // namespace internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_IDENTITY_H_ diff --git a/absl/base/internal/inline_variable.h b/absl/base/internal/inline_variable.h index f7bb8c56..130d8c24 100644 --- a/absl/base/internal/inline_variable.h +++ b/absl/base/internal/inline_variable.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/inline_variable_testing.h b/absl/base/internal/inline_variable_testing.h index be0b0b96..0ebdb9d8 100644 --- a/absl/base/internal/inline_variable_testing.h +++ b/absl/base/internal/inline_variable_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include "absl/base/internal/inline_variable.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace inline_variable_testing_internal { struct Foo { @@ -40,7 +40,7 @@ const int& get_int_a(); const int& get_int_b(); } // namespace inline_variable_testing_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_ diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index 1372ef50..030b98df 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -39,11 +39,13 @@ #include <type_traits> #include <utility> +#include "absl/meta/type_traits.h" + // The following code is internal implementation detail. See the comment at the // top of this file for the API documentation. namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // The five classes below each implement one of the clauses from the definition @@ -68,15 +70,11 @@ struct MemFunAndRef : StrippedAccept<MemFunAndRef> { template <typename... Args> struct AcceptImpl : std::false_type {}; - template <typename R, typename C, typename... Params, typename Obj, - typename... Args> - struct AcceptImpl<R (C::*)(Params...), Obj, Args...> - : std::is_base_of<C, Obj> {}; - - template <typename R, typename C, typename... Params, typename Obj, - typename... Args> - struct AcceptImpl<R (C::*)(Params...) const, Obj, Args...> - : std::is_base_of<C, Obj> {}; + template <typename MemFunType, typename C, typename Obj, typename... Args> + struct AcceptImpl<MemFunType C::*, Obj, Args...> + : std::integral_constant<bool, std::is_base_of<C, Obj>::value && + absl::is_function<MemFunType>::value> { + }; template <typename MemFun, typename Obj, typename... Args> static decltype((std::declval<Obj>().* @@ -93,15 +91,11 @@ struct MemFunAndPtr : StrippedAccept<MemFunAndPtr> { template <typename... Args> struct AcceptImpl : std::false_type {}; - template <typename R, typename C, typename... Params, typename Ptr, - typename... Args> - struct AcceptImpl<R (C::*)(Params...), Ptr, Args...> - : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value> {}; - - template <typename R, typename C, typename... Params, typename Ptr, - typename... Args> - struct AcceptImpl<R (C::*)(Params...) const, Ptr, Args...> - : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value> {}; + template <typename MemFunType, typename C, typename Ptr, typename... Args> + struct AcceptImpl<MemFunType C::*, Ptr, Args...> + : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value && + absl::is_function<MemFunType>::value> { + }; template <typename MemFun, typename Ptr, typename... Args> static decltype(((*std::declval<Ptr>()).* @@ -120,7 +114,9 @@ struct DataMemAndRef : StrippedAccept<DataMemAndRef> { struct AcceptImpl : std::false_type {}; template <typename R, typename C, typename Obj> - struct AcceptImpl<R C::*, Obj> : std::is_base_of<C, Obj> {}; + struct AcceptImpl<R C::*, Obj> + : std::integral_constant<bool, std::is_base_of<C, Obj>::value && + !absl::is_function<R>::value> {}; template <typename DataMem, typename Ref> static decltype(std::declval<Ref>().*std::declval<DataMem>()) Invoke( @@ -137,7 +133,8 @@ struct DataMemAndPtr : StrippedAccept<DataMemAndPtr> { template <typename R, typename C, typename Ptr> struct AcceptImpl<R C::*, Ptr> - : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value> {}; + : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value && + !absl::is_function<R>::value> {}; template <typename DataMem, typename Ptr> static decltype((*std::declval<Ptr>()).*std::declval<DataMem>()) Invoke( @@ -184,7 +181,7 @@ InvokeT<F, Args...> Invoke(F&& f, Args&&... args) { std::forward<Args>(args)...); } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_INVOKE_H_ diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc index 10d805cc..419c0e45 100644 --- a/absl/base/internal/low_level_alloc.cc +++ b/absl/base/internal/low_level_alloc.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -63,7 +63,7 @@ #endif // __APPLE__ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // A first-fit allocator with amortized logarithmic free() time. @@ -295,7 +295,10 @@ class SCOPED_LOCKABLE ArenaLock { arena_->mu.Unlock(); #ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING if (mask_valid_) { - pthread_sigmask(SIG_SETMASK, &mask_, nullptr); + const int err = pthread_sigmask(SIG_SETMASK, &mask_, nullptr); + if (err != 0) { + ABSL_RAW_LOG(FATAL, "pthread_sigmask failed: %d", err); + } } #endif left_ = true; @@ -612,7 +615,7 @@ void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) { } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_LOW_LEVEL_ALLOC_MISSING diff --git a/absl/base/internal/low_level_alloc.h b/absl/base/internal/low_level_alloc.h index 87cfc934..32b6ec17 100644 --- a/absl/base/internal/low_level_alloc.h +++ b/absl/base/internal/low_level_alloc.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,6 +25,7 @@ // IWYU pragma: private, include "base/low_level_alloc.h" #include <sys/types.h> + #include <cstdint> #include "absl/base/attributes.h" @@ -54,7 +55,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { class LowLevelAlloc { @@ -119,6 +120,7 @@ class LowLevelAlloc { }; } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ diff --git a/absl/base/internal/low_level_alloc_test.cc b/absl/base/internal/low_level_alloc_test.cc index 65bb519d..b6eb8b30 100644 --- a/absl/base/internal/low_level_alloc_test.cc +++ b/absl/base/internal/low_level_alloc_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include <utility> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { namespace { @@ -138,6 +138,7 @@ static void Test(bool use_new_arena, bool call_malloc_hook, int n) { TEST_ASSERT(LowLevelAlloc::DeleteArena(arena)); } } + // LowLevelAlloc is designed to be safe to call before main(). static struct BeforeMain { BeforeMain() { @@ -149,7 +150,7 @@ static struct BeforeMain { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl int main(int argc, char *argv[]) { diff --git a/absl/base/internal/low_level_scheduling.h b/absl/base/internal/low_level_scheduling.h index 7cb6117e..b762279d 100644 --- a/absl/base/internal/low_level_scheduling.h +++ b/absl/base/internal/low_level_scheduling.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ extern "C" bool __google_disable_rescheduling(void); extern "C" void __google_enable_rescheduling(bool disable_result); namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { class SchedulingHelper; // To allow use of SchedulingGuard. @@ -87,6 +87,7 @@ class SchedulingGuard { //------------------------------------------------------------------------------ // End of public interfaces. //------------------------------------------------------------------------------ + inline bool SchedulingGuard::ReschedulingIsAllowed() { return false; } @@ -99,8 +100,8 @@ inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) { return; } - } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ diff --git a/absl/base/internal/per_thread_tls.h b/absl/base/internal/per_thread_tls.h index 2428bdc1..cf5e97a0 100644 --- a/absl/base/internal/per_thread_tls.h +++ b/absl/base/internal/per_thread_tls.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,13 +16,17 @@ #define ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ // This header defines two macros: +// // If the platform supports thread-local storage: -// ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a -// thread-local variable ABSL_PER_THREAD_TLS is 1 +// +// * ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a +// thread-local variable +// * ABSL_PER_THREAD_TLS is 1 // // Otherwise: -// ABSL_PER_THREAD_TLS_KEYWORD is empty -// ABSL_PER_THREAD_TLS is 0 +// +// * ABSL_PER_THREAD_TLS_KEYWORD is empty +// * ABSL_PER_THREAD_TLS is 0 // // Microsoft C supports thread-local storage. // GCC supports it if the appropriate version of glibc is available, diff --git a/absl/base/internal/pretty_function.h b/absl/base/internal/pretty_function.h index 01b0547b..35d51676 100644 --- a/absl/base/internal/pretty_function.h +++ b/absl/base/internal/pretty_function.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index ed8b8d7c..1a36d5b6 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -36,7 +36,8 @@ // This preprocessor token is also defined in raw_io.cc. If you need to copy // this, consider moving both to config.h instead. #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__Fuchsia__) || defined(__native_client__) + defined(__Fuchsia__) || defined(__native_client__) || \ + defined(__EMSCRIPTEN__) #include <unistd.h> @@ -181,7 +182,7 @@ void RawLogVA(absl::LogSeverity severity, const char* file, int line, } // namespace namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace raw_logging_internal { void SafeWriteToStderr(const char *s, size_t len) { #if defined(ABSL_HAVE_SYSCALL_WRITE) @@ -232,5 +233,5 @@ void RegisterInternalLogFunction(InternalLogFunction func) { } } // namespace raw_logging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index 2786a3de..8e5a34ef 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,6 +38,7 @@ // ABSL_RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); // This will print an almost standard log line like this to stderr only: // E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file + #define ABSL_RAW_LOG(severity, ...) \ do { \ constexpr const char* absl_raw_logging_internal_basename = \ @@ -79,13 +80,13 @@ absl_raw_logging_internal_basename, __LINE__, message); \ } while (0) -#define ABSL_INTERNAL_CHECK(condition, message) \ - do { \ - if (ABSL_PREDICT_FALSE(!(condition))) { \ +#define ABSL_INTERNAL_CHECK(condition, message) \ + do { \ + if (ABSL_PREDICT_FALSE(!(condition))) { \ std::string death_message = "Check " #condition " failed: "; \ death_message += std::string(message); \ - ABSL_INTERNAL_LOG(FATAL, death_message); \ - } \ + ABSL_INTERNAL_LOG(FATAL, death_message); \ + } \ } while (0) #define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo @@ -96,7 +97,7 @@ ::absl::NormalizeLogSeverity(severity) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace raw_logging_internal { // Helper function to implement ABSL_RAW_LOG @@ -176,7 +177,7 @@ extern base_internal::AtomicHook<InternalLogFunction> internal_log_function; void RegisterInternalLogFunction(InternalLogFunction func); } // namespace raw_logging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_RAW_LOGGING_H_ diff --git a/absl/base/internal/scheduling_mode.h b/absl/base/internal/scheduling_mode.h index 19a7514c..48767920 100644 --- a/absl/base/internal/scheduling_mode.h +++ b/absl/base/internal/scheduling_mode.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,7 +19,7 @@ #define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // Used to describe how a thread may be scheduled. Typically associated with @@ -50,7 +50,7 @@ enum SchedulingMode { }; } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ diff --git a/absl/base/internal/scoped_set_env.cc b/absl/base/internal/scoped_set_env.cc new file mode 100644 index 00000000..eb7a0116 --- /dev/null +++ b/absl/base/internal/scoped_set_env.cc @@ -0,0 +1,81 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/scoped_set_env.h" + +#ifdef _WIN32 +#include <windows.h> +#endif + +#include <cstdlib> + +#include "absl/base/internal/raw_logging.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace base_internal { + +namespace { + +#ifdef _WIN32 +const int kMaxEnvVarValueSize = 1024; +#endif + +void SetEnvVar(const char* name, const char* value) { +#ifdef _WIN32 + SetEnvironmentVariableA(name, value); +#else + if (value == nullptr) { + ::unsetenv(name); + } else { + ::setenv(name, value, 1); + } +#endif +} + +} // namespace + +ScopedSetEnv::ScopedSetEnv(const char* var_name, const char* new_value) + : var_name_(var_name), was_unset_(false) { +#ifdef _WIN32 + char buf[kMaxEnvVarValueSize]; + auto get_res = GetEnvironmentVariableA(var_name_.c_str(), buf, sizeof(buf)); + ABSL_INTERNAL_CHECK(get_res < sizeof(buf), "value exceeds buffer size"); + + if (get_res == 0) { + was_unset_ = (GetLastError() == ERROR_ENVVAR_NOT_FOUND); + } else { + old_value_.assign(buf, get_res); + } + + SetEnvironmentVariableA(var_name_.c_str(), new_value); +#else + const char* val = ::getenv(var_name_.c_str()); + if (val == nullptr) { + was_unset_ = true; + } else { + old_value_ = val; + } +#endif + + SetEnvVar(var_name_.c_str(), new_value); +} + +ScopedSetEnv::~ScopedSetEnv() { + SetEnvVar(var_name_.c_str(), was_unset_ ? nullptr : old_value_.c_str()); +} + +} // namespace base_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/base/internal/scoped_set_env.h b/absl/base/internal/scoped_set_env.h new file mode 100644 index 00000000..709843bc --- /dev/null +++ b/absl/base/internal/scoped_set_env.h @@ -0,0 +1,43 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ +#define ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ + +#include <string> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace base_internal { + +class ScopedSetEnv { + public: + ScopedSetEnv(const char* var_name, const char* new_value); + ~ScopedSetEnv(); + + private: + std::string var_name_; + std::string old_value_; + + // True if the environment variable was initially not set. + bool was_unset_; +}; + +} // namespace base_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ diff --git a/absl/base/internal/scoped_set_env_test.cc b/absl/base/internal/scoped_set_env_test.cc new file mode 100644 index 00000000..5cbad246 --- /dev/null +++ b/absl/base/internal/scoped_set_env_test.cc @@ -0,0 +1,99 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifdef _WIN32 +#include <windows.h> +#endif + +#include "gtest/gtest.h" +#include "absl/base/internal/scoped_set_env.h" + +namespace { + +using absl::base_internal::ScopedSetEnv; + +std::string GetEnvVar(const char* name) { +#ifdef _WIN32 + char buf[1024]; + auto get_res = GetEnvironmentVariableA(name, buf, sizeof(buf)); + if (get_res >= sizeof(buf)) { + return "TOO_BIG"; + } + + if (get_res == 0) { + return "UNSET"; + } + + return std::string(buf, get_res); +#else + const char* val = ::getenv(name); + if (val == nullptr) { + return "UNSET"; + } + + return val; +#endif +} + +TEST(ScopedSetEnvTest, SetNonExistingVarToString) { + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); + + { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value"); + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); + } + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); +} + +TEST(ScopedSetEnvTest, SetNonExistingVarToNull) { + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); + + { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", nullptr); + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); + } + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); +} + +TEST(ScopedSetEnvTest, SetExistingVarToString) { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value"); + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); + + { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "new_value"); + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "new_value"); + } + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); +} + +TEST(ScopedSetEnvTest, SetExistingVarToNull) { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", "value"); + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); + + { + ScopedSetEnv scoped_set("SCOPED_SET_ENV_TEST_VAR", nullptr); + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "UNSET"); + } + + EXPECT_EQ(GetEnvVar("SCOPED_SET_ENV_TEST_VAR"), "value"); +} + +} // namespace diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc index 8f8eef82..5f443bfa 100644 --- a/absl/base/internal/spinlock.cc +++ b/absl/base/internal/spinlock.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -54,7 +54,7 @@ // holder to acquire the lock. There may be outstanding waiter(s). namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { ABSL_CONST_INIT static base_internal::AtomicHook<void (*)(const void *lock, @@ -229,5 +229,5 @@ uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) { } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h index d53878b2..df947661 100644 --- a/absl/base/internal/spinlock.h +++ b/absl/base/internal/spinlock.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -32,6 +32,7 @@ #include <stdint.h> #include <sys/types.h> + #include <atomic> #include "absl/base/attributes.h" @@ -45,7 +46,7 @@ #include "absl/base/thread_annotations.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { class LOCKABLE SpinLock { @@ -58,8 +59,10 @@ class LOCKABLE SpinLock { // // static SpinLock lock(base_internal::kLinkerInitialized); // - // When intialized using this constructor, we depend on the fact - // that the linker has already initialized the memory appropriately. + // When initialized using this constructor, we depend on the fact + // that the linker has already initialized the memory appropriately. The lock + // is initialized in non-cooperative mode. + // // A SpinLock constructed like this can be freely used from global // initializers without worrying about the order in which global // initializers run. @@ -235,7 +238,7 @@ inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_SPINLOCK_H_ diff --git a/absl/base/internal/spinlock_akaros.inc b/absl/base/internal/spinlock_akaros.inc index 051c8cf8..bc468940 100644 --- a/absl/base/internal/spinlock_akaros.inc +++ b/absl/base/internal/spinlock_akaros.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/spinlock_benchmark.cc b/absl/base/internal/spinlock_benchmark.cc index 907d3e27..0451c65f 100644 --- a/absl/base/internal/spinlock_benchmark.cc +++ b/absl/base/internal/spinlock_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/spinlock_linux.inc b/absl/base/internal/spinlock_linux.inc index 94c861dc..28e29d19 100644 --- a/absl/base/internal/spinlock_linux.inc +++ b/absl/base/internal/spinlock_linux.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -51,17 +51,12 @@ extern "C" { ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockDelay( std::atomic<uint32_t> *w, uint32_t value, int loop, absl::base_internal::SchedulingMode) { - if (loop != 0) { - int save_errno = errno; - struct timespec tm; - tm.tv_sec = 0; - // Increase the delay; we expect (but do not rely on) explicit wakeups. - // We don't rely on explicit wakeups because we intentionally allow for - // a race on the kSpinLockSleeper bit. - tm.tv_nsec = 16 * absl::base_internal::SpinLockSuggestedDelayNS(loop); - syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); - errno = save_errno; - } + int save_errno = errno; + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); + syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, &tm); + errno = save_errno; } ABSL_ATTRIBUTE_WEAK void AbslInternalSpinLockWake(std::atomic<uint32_t> *w, diff --git a/absl/base/internal/spinlock_posix.inc b/absl/base/internal/spinlock_posix.inc index 0098c1c7..f025b5f8 100644 --- a/absl/base/internal/spinlock_posix.inc +++ b/absl/base/internal/spinlock_posix.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/spinlock_wait.cc b/absl/base/internal/spinlock_wait.cc index 3f8e43f0..60a85196 100644 --- a/absl/base/internal/spinlock_wait.cc +++ b/absl/base/internal/spinlock_wait.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -32,7 +32,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // See spinlock_wait.h for spec. @@ -66,19 +66,16 @@ int SpinLockSuggestedDelayNS(int loop) { r = 0x5deece66dLL * r + 0xb; // numbers from nrand48() delay_rand.store(r, std::memory_order_relaxed); - r <<= 16; // 48-bit random number now in top 48-bits. if (loop < 0 || loop > 32) { // limit loop to 0..32 loop = 32; } - // loop>>3 cannot exceed 4 because loop cannot exceed 32. - // Select top 20..24 bits of lower 48 bits, - // giving approximately 0ms to 16ms. - // Mean is exponential in loop for first 32 iterations, then 8ms. - // The futex path multiplies this by 16, since we expect explicit wakeups - // almost always on that path. - return static_cast<int>(r >> (44 - (loop >> 3))); + const int kMinDelay = 128 << 10; // 128us + // Double delay every 8 iterations, up to 16x (2ms). + int delay = kMinDelay << (loop / 8); + // Randomize in delay..2*delay range, for resulting 128us..4ms range. + return delay | ((delay - 1) & static_cast<int>(r)); } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/spinlock_wait.h b/absl/base/internal/spinlock_wait.h index 5eb727f1..7e139de0 100644 --- a/absl/base/internal/spinlock_wait.h +++ b/absl/base/internal/spinlock_wait.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include "absl/base/internal/scheduling_mode.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // SpinLockWait() waits until it can perform one of several transitions from @@ -63,7 +63,7 @@ void SpinLockDelay(std::atomic<uint32_t> *w, uint32_t value, int loop, int SpinLockSuggestedDelayNS(int loop); } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // In some build configurations we pass --detect-odr-violations to the diff --git a/absl/base/internal/spinlock_win32.inc b/absl/base/internal/spinlock_win32.inc index 32c8fc0b..78654b5b 100644 --- a/absl/base/internal/spinlock_win32.inc +++ b/absl/base/internal/spinlock_win32.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index ce14fc0f..0d5cf821 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -56,7 +56,7 @@ #include "absl/base/internal/unscaledcycleclock.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { static once_flag init_system_info_once; @@ -402,5 +402,5 @@ pid_t GetTID() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/sysinfo.h b/absl/base/internal/sysinfo.h index 79100f61..22739aeb 100644 --- a/absl/base/internal/sysinfo.h +++ b/absl/base/internal/sysinfo.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,7 +33,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // Nominal core processor cycles per second of each processor. This is _not_ @@ -59,7 +59,7 @@ using pid_t = DWORD; pid_t GetTID(); } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_SYSINFO_H_ diff --git a/absl/base/internal/sysinfo_test.cc b/absl/base/internal/sysinfo_test.cc index c072ebc2..c8323e52 100644 --- a/absl/base/internal/sysinfo_test.cc +++ b/absl/base/internal/sysinfo_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { namespace { @@ -38,12 +38,12 @@ TEST(SysinfoTest, NumCPUs) { } TEST(SysinfoTest, NominalCPUFrequency) { -#if !(defined(__aarch64__) && defined(__linux__)) +#if !(defined(__aarch64__) && defined(__linux__)) && !defined(__EMSCRIPTEN__) EXPECT_GE(NominalCPUFrequency(), 1000.0) << "NominalCPUFrequency() did not return a reasonable value"; #else - // TODO(absl-team): Aarch64 cannot read the CPU frequency from sysfs, so we - // get back 1.0. Fix once the value is available. + // Aarch64 cannot read the CPU frequency from sysfs, so we get back 1.0. + // Emscripten does not have a sysfs to read from at all. EXPECT_EQ(NominalCPUFrequency(), 1.0) << "CPU frequency detection was fixed! Please update unittest."; #endif @@ -96,5 +96,5 @@ TEST(SysinfoTest, LinuxGetTID) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/thread_annotations.h b/absl/base/internal/thread_annotations.h new file mode 100644 index 00000000..4dab6a9c --- /dev/null +++ b/absl/base/internal/thread_annotations.h @@ -0,0 +1,271 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: thread_annotations.h +// ----------------------------------------------------------------------------- +// +// WARNING: This is a backwards compatible header and it will be removed after +// the migration to prefixed thread annotations is finished; please include +// "absl/base/thread_annotations.h". +// +// This header file contains macro definitions for thread safety annotations +// that allow developers to document the locking policies of multi-threaded +// code. The annotations can also help program analysis tools to identify +// potential thread safety issues. +// +// These annotations are implemented using compiler attributes. Using the macros +// defined here instead of raw attributes allow for portability and future +// compatibility. +// +// When referring to mutexes in the arguments of the attributes, you should +// use variable names or more complex expressions (e.g. my_object->mutex_) +// that evaluate to a concrete mutex object whenever possible. If the mutex +// you want to refer to is not in scope, you may use a member pointer +// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object. + +#ifndef ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ +#define ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ + +#if defined(__clang__) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +// GUARDED_BY() +// +// Documents if a shared field or global variable needs to be protected by a +// mutex. GUARDED_BY() allows the user to specify a particular mutex that +// should be held when accessing the annotated variable. +// +// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to +// local variables, a local variable and its associated mutex can often be +// combined into a small class or struct, thereby allowing the annotation. +// +// Example: +// +// class Foo { +// Mutex mu_; +// int p1_ GUARDED_BY(mu_); +// ... +// }; +#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +// PT_GUARDED_BY() +// +// Documents if the memory location pointed to by a pointer should be guarded +// by a mutex when dereferencing the pointer. +// +// Example: +// class Foo { +// Mutex mu_; +// int *p1_ PT_GUARDED_BY(mu_); +// ... +// }; +// +// Note that a pointer variable to a shared memory location could itself be a +// shared variable. +// +// Example: +// +// // `q_`, guarded by `mu1_`, points to a shared memory location that is +// // guarded by `mu2_`: +// int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_); +#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +// ACQUIRED_AFTER() / ACQUIRED_BEFORE() +// +// Documents the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER +// and ACQUIRED_BEFORE.) +// +// As with GUARDED_BY, this is only applicable to mutexes that are shared +// fields or global variables. +// +// Example: +// +// Mutex m1_; +// Mutex m2_ ACQUIRED_AFTER(m1_); +#define ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED() +// +// Documents a function that expects a mutex to be held prior to entry. +// The mutex is expected to be held both on entry to, and exit from, the +// function. +// +// An exclusive lock allows read-write access to the guarded data member(s), and +// only one thread can acquire a lock exclusively at any one time. A shared lock +// allows read-only access, and any number of threads can acquire a shared lock +// concurrently. +// +// Generally, non-const methods should be annotated with +// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with +// SHARED_LOCKS_REQUIRED. +// +// Example: +// +// Mutex mu1, mu2; +// int a GUARDED_BY(mu1); +// int b GUARDED_BY(mu2); +// +// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } +// void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) + +#define SHARED_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) + +// LOCKS_EXCLUDED() +// +// Documents the locks acquired in the body of the function. These locks +// cannot be held when calling this function (as Abseil's `Mutex` locks are +// non-reentrant). +#define LOCKS_EXCLUDED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +// LOCK_RETURNED() +// +// Documents a function that returns a mutex without acquiring it. For example, +// a public getter method that returns a pointer to a private mutex should +// be annotated with LOCK_RETURNED. +#define LOCK_RETURNED(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +// LOCKABLE +// +// Documents if a class/type is a lockable type (such as the `Mutex` class). +#define LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(lockable) + +// SCOPED_LOCKABLE +// +// Documents if a class does RAII locking (such as the `MutexLock` class). +// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is +// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no +// arguments; the analysis will assume that the destructor unlocks whatever the +// constructor locked. +#define SCOPED_LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +// EXCLUSIVE_LOCK_FUNCTION() +// +// Documents functions that acquire a lock in the body of a function, and do +// not release it. +#define EXCLUSIVE_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) + +// SHARED_LOCK_FUNCTION() +// +// Documents functions that acquire a shared (reader) lock in the body of a +// function, and do not release it. +#define SHARED_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) + +// UNLOCK_FUNCTION() +// +// Documents functions that expect a lock to be held on entry to the function, +// and release it in the body of the function. +#define UNLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) + +// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION() +// +// Documents functions that try to acquire a lock, and return success or failure +// (or a non-boolean value that can be interpreted as a boolean). +// The first argument should be `true` for functions that return `true` on +// success, or `false` for functions that return `false` on success. The second +// argument specifies the mutex that is locked on success. If unspecified, this +// mutex is assumed to be `this`. +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) + +#define SHARED_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) + +// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK() +// +// Documents functions that dynamically check to see if a lock is held, and fail +// if it is not held. +#define ASSERT_EXCLUSIVE_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) + +#define ASSERT_SHARED_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) + +// NO_THREAD_SAFETY_ANALYSIS +// +// Turns off thread safety checking within the body of a particular function. +// This annotation is used to mark functions that are known to be correct, but +// the locking behavior is more complicated than the analyzer can handle. +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +//------------------------------------------------------------------------------ +// Tool-Supplied Annotations +//------------------------------------------------------------------------------ + +// TS_UNCHECKED should be placed around lock expressions that are not valid +// C++ syntax, but which are present for documentation purposes. These +// annotations will be ignored by the analysis. +#define TS_UNCHECKED(x) "" + +// TS_FIXME is used to mark lock expressions that are not valid C++ syntax. +// It is used by automated tools to mark and disable invalid expressions. +// The annotation should either be fixed, or changed to TS_UNCHECKED. +#define TS_FIXME(x) "" + +// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of +// a particular function. However, this attribute is used to mark functions +// that are incorrect and need to be fixed. It is used by automated tools to +// avoid breaking the build when the analysis is updated. +// Code owners are expected to eventually fix the routine. +#define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS + +// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY +// annotation that needs to be fixed, because it is producing thread safety +// warning. It disables the GUARDED_BY. +#define GUARDED_BY_FIXME(x) + +// Disables warnings for a single read operation. This can be used to avoid +// warnings when it is known that the read is not actually involved in a race, +// but the compiler cannot confirm that. +#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x) + + +namespace thread_safety_analysis { + +// Takes a reference to a guarded data member, and returns an unguarded +// reference. +template <typename T> +inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +template <typename T> +inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +} // namespace thread_safety_analysis + +#endif // ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc index b35bee34..dbec326a 100644 --- a/absl/base/internal/thread_identity.cc +++ b/absl/base/internal/thread_identity.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/base/internal/spinlock.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { #if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11 @@ -131,5 +131,5 @@ ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index 17ac2a7c..bb5aa074 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,7 +33,7 @@ #include "absl/base/internal/per_thread_tls.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { struct SynchLocksHeld; struct SynchWaitParams; @@ -43,9 +43,9 @@ namespace base_internal { class SpinLock; struct ThreadIdentity; -// Used by the implementation of base::Mutex and base::CondVar. +// Used by the implementation of absl::Mutex and absl::CondVar. struct PerThreadSynch { - // The internal representation of base::Mutex and base::CondVar rely + // The internal representation of absl::Mutex and absl::CondVar rely // on the alignment of PerThreadSynch. Both store the address of the // PerThreadSynch in the high-order bits of their internal state, // which means the low kLowZeroBits of the address of PerThreadSynch @@ -237,6 +237,7 @@ inline ThreadIdentity* CurrentThreadIdentityIfPresent() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ diff --git a/absl/base/internal/thread_identity_benchmark.cc b/absl/base/internal/thread_identity_benchmark.cc index 242522b4..0ae10f2b 100644 --- a/absl/base/internal/thread_identity_benchmark.cc +++ b/absl/base/internal/thread_identity_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc index ec93fc27..8de6d9eb 100644 --- a/absl/base/internal/thread_identity_test.cc +++ b/absl/base/internal/thread_identity_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { namespace { @@ -124,5 +124,5 @@ TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/throw_delegate.cc b/absl/base/internal/throw_delegate.cc index 0f73c3eb..bee404d4 100644 --- a/absl/base/internal/throw_delegate.cc +++ b/absl/base/internal/throw_delegate.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { namespace { @@ -104,5 +104,5 @@ void ThrowStdBadFunctionCall() { Throw(std::bad_function_call()); } void ThrowStdBadAlloc() { Throw(std::bad_alloc()); } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/internal/throw_delegate.h b/absl/base/internal/throw_delegate.h index 7e5510c0..60ed4f32 100644 --- a/absl/base/internal/throw_delegate.h +++ b/absl/base/internal/throw_delegate.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // Helper functions that allow throwing exceptions consistently from anywhere. @@ -67,7 +67,7 @@ namespace base_internal { // [[noreturn]] void ThrowStdBadArrayNewLength(); } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ diff --git a/absl/base/internal/tsan_mutex_interface.h b/absl/base/internal/tsan_mutex_interface.h index 6bb4faed..2a510603 100644 --- a/absl/base/internal/tsan_mutex_interface.h +++ b/absl/base/internal/tsan_mutex_interface.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h index 07a64bba..a709a446 100644 --- a/absl/base/internal/unaligned_access.h +++ b/absl/base/internal/unaligned_access.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,15 +25,6 @@ // unaligned APIs // Portable handling of unaligned loads, stores, and copies. -// On some platforms, like ARM, the copy functions can be more efficient -// then a load and a store. -// -// It is possible to implement all of these these using constant-length memcpy -// calls, which is portable and will usually be inlined into simple loads and -// stores if the architecture supports it. However, such inlining usually -// happens in a pass that's quite late in compilation, which means the resulting -// loads and stores cannot participate in many other optimizations, leading to -// overall worse code. // The unaligned API is C++ only. The declarations use C++ features // (namespaces, inline) which are absent or incompatible in C. @@ -65,7 +56,7 @@ void __sanitizer_unaligned_store64(void *p, uint64_t v); } // extern "C" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { inline uint16_t UnalignedLoad16(const void *p) { @@ -93,7 +84,7 @@ inline void UnalignedStore64(void *p, uint64_t v) { } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ @@ -110,172 +101,10 @@ inline void UnalignedStore64(void *p, uint64_t v) { #define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ (absl::base_internal::UnalignedStore64(_p, _val)) -#elif defined(UNDEFINED_BEHAVIOR_SANITIZER) - -namespace absl { -inline namespace lts_2018_12_18 { -namespace base_internal { - -inline uint16_t UnalignedLoad16(const void *p) { - uint16_t t; - memcpy(&t, p, sizeof t); - return t; -} - -inline uint32_t UnalignedLoad32(const void *p) { - uint32_t t; - memcpy(&t, p, sizeof t); - return t; -} - -inline uint64_t UnalignedLoad64(const void *p) { - uint64_t t; - memcpy(&t, p, sizeof t); - return t; -} - -inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); } - -inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); } - -inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } - -} // namespace base_internal -} // inline namespace lts_2018_12_18 -} // namespace absl - -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ - (absl::base_internal::UnalignedLoad16(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ - (absl::base_internal::UnalignedLoad32(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ - (absl::base_internal::UnalignedLoad64(_p)) - -#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ - (absl::base_internal::UnalignedStore16(_p, _val)) -#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - (absl::base_internal::UnalignedStore32(_p, _val)) -#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (absl::base_internal::UnalignedStore64(_p, _val)) - -#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \ - defined(_M_IX86) || defined(__ppc__) || defined(__PPC__) || \ - defined(__ppc64__) || defined(__PPC64__) - -// x86 and x86-64 can perform unaligned loads/stores directly; -// modern PowerPC hardware can also do unaligned integer loads and stores; -// but note: the FPU still sends unaligned loads and stores to a trap handler! - -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ - (*reinterpret_cast<const uint16_t *>(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ - (*reinterpret_cast<const uint32_t *>(_p)) -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ - (*reinterpret_cast<const uint64_t *>(_p)) - -#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ - (*reinterpret_cast<uint16_t *>(_p) = (_val)) -#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - (*reinterpret_cast<uint32_t *>(_p) = (_val)) -#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (*reinterpret_cast<uint64_t *>(_p) = (_val)) - -#elif defined(__arm__) && \ - !defined(__ARM_ARCH_5__) && \ - !defined(__ARM_ARCH_5T__) && \ - !defined(__ARM_ARCH_5TE__) && \ - !defined(__ARM_ARCH_5TEJ__) && \ - !defined(__ARM_ARCH_6__) && \ - !defined(__ARM_ARCH_6J__) && \ - !defined(__ARM_ARCH_6K__) && \ - !defined(__ARM_ARCH_6Z__) && \ - !defined(__ARM_ARCH_6ZK__) && \ - !defined(__ARM_ARCH_6T2__) - - -// ARMv7 and newer support native unaligned accesses, but only of 16-bit -// and 32-bit values (not 64-bit); older versions either raise a fatal signal, -// do an unaligned read and rotate the words around a bit, or do the reads very -// slowly (trip through kernel mode). There's no simple #define that says just -// "ARMv7 or higher", so we have to filter away all ARMv5 and ARMv6 -// sub-architectures. Newer gcc (>= 4.6) set an __ARM_FEATURE_ALIGNED #define, -// so in time, maybe we can move on to that. -// -// This is a mess, but there's not much we can do about it. -// -// To further complicate matters, only LDR instructions (single reads) are -// allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we -// explicitly tell the compiler that these accesses can be unaligned, it can and -// will combine accesses. On armcc, the way to signal this is done by accessing -// through the type (uint32_t __packed *), but GCC has no such attribute -// (it ignores __attribute__((packed)) on individual variables). However, -// we can tell it that a _struct_ is unaligned, which has the same effect, -// so we do that. - -namespace absl { -inline namespace lts_2018_12_18 { -namespace base_internal { - -struct Unaligned16Struct { - uint16_t value; - uint8_t dummy; // To make the size non-power-of-two. -} ABSL_ATTRIBUTE_PACKED; - -struct Unaligned32Struct { - uint32_t value; - uint8_t dummy; // To make the size non-power-of-two. -} ABSL_ATTRIBUTE_PACKED; - -} // namespace base_internal -} // inline namespace lts_2018_12_18 -} // namespace absl - -#define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ - ((reinterpret_cast<const ::absl::base_internal::Unaligned16Struct *>(_p)) \ - ->value) -#define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ - ((reinterpret_cast<const ::absl::base_internal::Unaligned32Struct *>(_p)) \ - ->value) - -#define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ - ((reinterpret_cast< ::absl::base_internal::Unaligned16Struct *>(_p)) \ - ->value = (_val)) -#define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ - ((reinterpret_cast< ::absl::base_internal::Unaligned32Struct *>(_p)) \ - ->value = (_val)) - -namespace absl { -inline namespace lts_2018_12_18 { -namespace base_internal { - -inline uint64_t UnalignedLoad64(const void *p) { - uint64_t t; - memcpy(&t, p, sizeof t); - return t; -} - -inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } - -} // namespace base_internal -} // inline namespace lts_2018_12_18 -} // namespace absl - -#define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ - (absl::base_internal::UnalignedLoad64(_p)) -#define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ - (absl::base_internal::UnalignedStore64(_p, _val)) - #else -// ABSL_INTERNAL_NEED_ALIGNED_LOADS is defined when the underlying platform -// doesn't support unaligned access. -#define ABSL_INTERNAL_NEED_ALIGNED_LOADS - -// These functions are provided for architectures that don't support -// unaligned loads and stores. - namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { inline uint16_t UnalignedLoad16(const void *p) { @@ -303,7 +132,7 @@ inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); } inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ diff --git a/absl/base/internal/unscaledcycleclock.cc b/absl/base/internal/unscaledcycleclock.cc index 888caf1e..29a927d3 100644 --- a/absl/base/internal/unscaledcycleclock.cc +++ b/absl/base/internal/unscaledcycleclock.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,7 @@ #include "absl/base/internal/sysinfo.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { #if defined(__i386__) @@ -97,7 +97,7 @@ double UnscaledCycleClock::Frequency() { #endif } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_USE_UNSCALED_CYCLECLOCK diff --git a/absl/base/internal/unscaledcycleclock.h b/absl/base/internal/unscaledcycleclock.h index c71674f3..e6fc9103 100644 --- a/absl/base/internal/unscaledcycleclock.h +++ b/absl/base/internal/unscaledcycleclock.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -59,7 +59,8 @@ // CycleClock that runs at atleast 1 MHz. We've found some Android // ARM64 devices where this is not the case, so we disable it by // default on Android ARM64. -#if defined(__native_client__) || TARGET_OS_IPHONE || \ +#if defined(__native_client__) || \ + (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \ (defined(__ANDROID__) && defined(__aarch64__)) #define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 #else @@ -83,8 +84,9 @@ defined(_M_IX86) || defined(_M_X64)) #define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY #endif + namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { class UnscaledCycleClockWrapperForGetCurrentTime; } // namespace time_internal @@ -114,8 +116,9 @@ class UnscaledCycleClock { }; } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_USE_UNSCALED_CYCLECLOCK #endif // ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc index 4df637ac..685501a4 100644 --- a/absl/base/invoke_test.cc +++ b/absl/base/invoke_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { namespace { @@ -71,6 +71,10 @@ struct OverloadedFunctor { struct Class { int Method(int a, int b) { return a - b; } int ConstMethod(int a, int b) const { return a - b; } + int RefMethod(int a, int b) & { return a - b; } + int RefRefMethod(int a, int b) && { return a - b; } + int NoExceptMethod(int a, int b) noexcept { return a - b; } + int VolatileMethod(int a, int b) volatile { return a - b; } int member; }; @@ -152,8 +156,18 @@ TEST(InvokeTest, ReferenceWrapper) { TEST(InvokeTest, MemberFunction) { std::unique_ptr<Class> p(new Class); std::unique_ptr<const Class> cp(new Class); + std::unique_ptr<volatile Class> vp(new Class); + EXPECT_EQ(1, Invoke(&Class::Method, p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::Method, p.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::Method, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefMethod, p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefMethod, p.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefMethod, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefRefMethod, std::move(*p), 3, 2)); // NOLINT + EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, *p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, p.get(), 3, 2)); @@ -163,6 +177,13 @@ TEST(InvokeTest, MemberFunction) { EXPECT_EQ(1, Invoke(&Class::ConstMethod, cp.get(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, *cp, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp.get(), 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *vp, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::Method, make_unique<Class>(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, make_unique<Class>(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, make_unique<const Class>(), 3, 2)); @@ -198,5 +219,5 @@ TEST(InvokeTest, SfinaeFriendly) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/types/optional.cc b/absl/base/log_severity.cc index 5c77f154..8109da19 100644 --- a/absl/types/optional.cc +++ b/absl/base/log_severity.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,15 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/types/optional.h" +#include "absl/base/log_severity.h" -#ifndef ABSL_HAVE_STD_OPTIONAL -namespace absl { -inline namespace lts_2018_12_18 { +#include <ostream> -nullopt_t::init_t nullopt_t::init; -extern const nullopt_t nullopt{nullopt_t::init}; +namespace absl { +inline namespace lts_2019_08_08 { -} // inline namespace lts_2018_12_18 +std::ostream& operator<<(std::ostream& os, absl::LogSeverity s) { + if (s == absl::NormalizeLogSeverity(s)) return os << absl::LogSeverityName(s); + return os << "absl::LogSeverity(" << static_cast<int>(s) << ")"; +} +} // inline namespace lts_2019_08_08 } // namespace absl -#endif // ABSL_HAVE_STD_OPTIONAL diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h index c24fad79..45f79cca 100644 --- a/absl/base/log_severity.h +++ b/absl/base/log_severity.h @@ -4,24 +4,24 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// #ifndef ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ #define ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ #include <array> +#include <ostream> #include "absl/base/attributes.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Four severity levels are defined. Logging APIs should terminate the program // when a message is logged at severity `kFatal`; the other levels have no @@ -63,7 +63,11 @@ constexpr absl::LogSeverity NormalizeLogSeverity(int s) { return NormalizeLogSeverity(static_cast<absl::LogSeverity>(s)); } -} // inline namespace lts_2018_12_18 +// The exact representation of a streamed `absl::LogSeverity` is deliberately +// unspecified; do not rely on it. +std::ostream& operator<<(std::ostream& os, absl::LogSeverity s); + +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_BASE_INTERNAL_LOG_SEVERITY_H_ diff --git a/absl/base/log_severity_test.cc b/absl/base/log_severity_test.cc new file mode 100644 index 00000000..1de2d101 --- /dev/null +++ b/absl/base/log_severity_test.cc @@ -0,0 +1,43 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/log_severity.h" + +#include <sstream> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace { +using testing::Eq; + +std::string StreamHelper(absl::LogSeverity value) { + std::ostringstream stream; + stream << value; + return stream.str(); +} + +TEST(StreamTest, Works) { + EXPECT_THAT(StreamHelper(static_cast<absl::LogSeverity>(-100)), + Eq("absl::LogSeverity(-100)")); + EXPECT_THAT(StreamHelper(absl::LogSeverity::kInfo), Eq("INFO")); + EXPECT_THAT(StreamHelper(absl::LogSeverity::kWarning), Eq("WARNING")); + EXPECT_THAT(StreamHelper(absl::LogSeverity::kError), Eq("ERROR")); + EXPECT_THAT(StreamHelper(absl::LogSeverity::kFatal), Eq("FATAL")); + EXPECT_THAT(StreamHelper(static_cast<absl::LogSeverity>(4)), + Eq("absl::LogSeverity(4)")); +} + +} // namespace diff --git a/absl/base/macros.h b/absl/base/macros.h index 14c4b0a3..3121088a 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,6 @@ // This code is compiled directly on many platforms, including client // platforms like Windows, Mac, and embedded systems. Before making // any changes here, make sure that you're not breaking any platforms. -// #ifndef ABSL_BASE_MACROS_H_ #define ABSL_BASE_MACROS_H_ @@ -32,6 +31,7 @@ #include <cassert> #include <cstddef> +#include "absl/base/optimization.h" #include "absl/base/port.h" // ABSL_ARRAYSIZE() @@ -43,14 +43,14 @@ (sizeof(::absl::macros_internal::ArraySizeHelper(array))) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace macros_internal { // Note: this internal template function declaration is used by ABSL_ARRAYSIZE. // The function doesn't need a definition, as we only use its type. template <typename T, size_t N> auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; } // namespace macros_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // kLinkerInitialized @@ -74,13 +74,13 @@ auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; // // Invocation // static MyClass my_global(absl::base_internal::kLinkerInitialized); namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { enum LinkerInitialized { kLinkerInitialized = 0, }; } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // ABSL_FALLTHROUGH_INTENDED @@ -196,10 +196,11 @@ enum LinkerInitialized { // This macro is inspired by // https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ #if defined(NDEBUG) -#define ABSL_ASSERT(expr) (false ? (void)(expr) : (void)0) +#define ABSL_ASSERT(expr) \ + (false ? static_cast<void>(expr) : static_cast<void>(0)) #else -#define ABSL_ASSERT(expr) \ - (ABSL_PREDICT_TRUE((expr)) ? (void)0 \ +#define ABSL_ASSERT(expr) \ + (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \ : [] { assert(false && #expr); }()) // NOLINT #endif diff --git a/absl/base/optimization.h b/absl/base/optimization.h index 2fddfc80..0dcbef32 100644 --- a/absl/base/optimization.h +++ b/absl/base/optimization.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -111,9 +111,9 @@ // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html // for more information. // -// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to -// `__attribute__((aligned(ABSL_CACHELINE_SIZE)))`. For compilers where this is -// not known to work, the macro expands to nothing. +// On some compilers, `ABSL_CACHELINE_ALIGNED` expands to an `__attribute__` +// or `__declspec` attribute. For compilers where this is not known to work, +// the macro expands to nothing. // // No further guarantees are made here. The result of applying the macro // to variables and types is always implementation-defined. @@ -122,6 +122,14 @@ // of causing bugs that are difficult to diagnose, crash, etc. It does not // of itself guarantee that objects are aligned to a cache line. // +// NOTE: Some compilers are picky about the locations of annotations such as +// this attribute, so prefer to put it at the beginning of your declaration. +// For example, +// +// ABSL_CACHELINE_ALIGNED static Foo* foo = ... +// +// class ABSL_CACHELINE_ALIGNED Bar { ... +// // Recommendations: // // 1) Consult compiler documentation; this comment is not kept in sync as @@ -131,8 +139,10 @@ // 3) Prefer applying this attribute to individual variables. Avoid // applying it to types. This tends to localize the effect. #define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE))) - -#else // not GCC +#elif defined(_MSC_VER) +#define ABSL_CACHELINE_SIZE 64 +#define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE)) +#else #define ABSL_CACHELINE_SIZE 64 #define ABSL_CACHELINE_ALIGNED #endif @@ -153,6 +163,12 @@ // Compilers can use the information that a certain branch is not likely to be // taken (for instance, a CHECK failure) to optimize for the common case in // the absence of better information (ie. compiling gcc with `-fprofile-arcs`). +// +// Recommendation: Modern CPUs dynamically predict branch execution paths, +// typically with accuracy greater than 97%. As a result, annotating every +// branch in a codebase is likely counterproductive; however, annotating +// specific branches that are both hot and consistently mispredicted is likely +// to yield performance improvements. #if ABSL_HAVE_BUILTIN(__builtin_expect) || \ (defined(__GNUC__) && !defined(__clang__)) #define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0)) diff --git a/absl/base/policy_checks.h b/absl/base/policy_checks.h index 0a07fc03..699fb1a2 100644 --- a/absl/base/policy_checks.h +++ b/absl/base/policy_checks.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/port.h b/absl/base/port.h index 1c67257f..6c28068d 100644 --- a/absl/base/port.h +++ b/absl/base/port.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/raw_logging_test.cc b/absl/base/raw_logging_test.cc index b21cf651..3d30bd38 100644 --- a/absl/base/raw_logging_test.cc +++ b/absl/base/raw_logging_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc index 95382977..06860e71 100644 --- a/absl/base/spinlock_test_common.cc +++ b/absl/base/spinlock_test_common.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -36,7 +36,7 @@ constexpr int32_t kNumThreads = 10; constexpr int32_t kIters = 1000; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace base_internal { // This is defined outside of anonymous namespace so that it can be @@ -55,6 +55,7 @@ namespace { static constexpr int kArrayLength = 10; static uint32_t values[kArrayLength]; + static SpinLock static_spinlock(base_internal::kLinkerInitialized); static SpinLock static_cooperative_spinlock( base_internal::kLinkerInitialized, @@ -62,7 +63,6 @@ static SpinLock static_cooperative_spinlock( static SpinLock static_noncooperative_spinlock( base_internal::kLinkerInitialized, base_internal::SCHEDULE_KERNEL_ONLY); - // Simple integer hash function based on the public domain lookup2 hash. // http://burtleburtle.net/bob/c/lookup2.c static uint32_t Hash32(uint32_t a, uint32_t c) { @@ -190,9 +190,11 @@ TEST(SpinLock, WaitCyclesEncoding) { SpinLockTest::DecodeWaitCycles(before_max_value); EXPECT_GT(expected_max_value_decoded, before_max_value_decoded); } + TEST(SpinLockWithThreads, StaticSpinLock) { ThreadedTest(&static_spinlock); } + TEST(SpinLockWithThreads, StackSpinLock) { SpinLock spinlock; ThreadedTest(&spinlock); @@ -265,5 +267,5 @@ TEST(SpinLockWithThreads, DoesNotDeadlock) { } // namespace } // namespace base_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/base/thread_annotations.h b/absl/base/thread_annotations.h index 2241ace4..f3e96589 100644 --- a/absl/base/thread_annotations.h +++ b/absl/base/thread_annotations.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,6 @@ // code. The annotations can also help program analysis tools to identify // potential thread safety issues. // -// // These annotations are implemented using compiler attributes. Using the macros // defined here instead of raw attributes allow for portability and future // compatibility. @@ -34,19 +33,23 @@ #ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_ #define ABSL_BASE_THREAD_ANNOTATIONS_H_ + +// TODO(mbonadei): Remove after the backward compatibility period. +#include "absl/base/internal/thread_annotations.h" // IWYU pragma: export + #if defined(__clang__) -#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#define ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(x) __attribute__((x)) #else -#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#define ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(x) // no-op #endif -// GUARDED_BY() +// ABSL_GUARDED_BY() // // Documents if a shared field or global variable needs to be protected by a -// mutex. GUARDED_BY() allows the user to specify a particular mutex that +// mutex. ABSL_GUARDED_BY() allows the user to specify a particular mutex that // should be held when accessing the annotated variable. // -// Although this annotation (and PT_GUARDED_BY, below) cannot be applied to +// Although this annotation (and ABSL_PT_GUARDED_BY, below) cannot be applied to // local variables, a local variable and its associated mutex can often be // combined into a small class or struct, thereby allowing the annotation. // @@ -54,12 +57,13 @@ // // class Foo { // Mutex mu_; -// int p1_ GUARDED_BY(mu_); +// int p1_ ABSL_GUARDED_BY(mu_); // ... // }; -#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) +#define ABSL_GUARDED_BY(x) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(guarded_by(x)) -// PT_GUARDED_BY() +// ABSL_PT_GUARDED_BY() // // Documents if the memory location pointed to by a pointer should be guarded // by a mutex when dereferencing the pointer. @@ -67,7 +71,7 @@ // Example: // class Foo { // Mutex mu_; -// int *p1_ PT_GUARDED_BY(mu_); +// int *p1_ ABSL_PT_GUARDED_BY(mu_); // ... // }; // @@ -78,31 +82,32 @@ // // // `q_`, guarded by `mu1_`, points to a shared memory location that is // // guarded by `mu2_`: -// int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_); -#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) +// int *q_ ABSL_GUARDED_BY(mu1_) ABSL_PT_GUARDED_BY(mu2_); +#define ABSL_PT_GUARDED_BY(x) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(pt_guarded_by(x)) -// ACQUIRED_AFTER() / ACQUIRED_BEFORE() +// ABSL_ACQUIRED_AFTER() / ABSL_ACQUIRED_BEFORE() // // Documents the acquisition order between locks that can be held // simultaneously by a thread. For any two locks that need to be annotated // to establish an acquisition order, only one of them needs the annotation. -// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER -// and ACQUIRED_BEFORE.) +// (i.e. You don't have to annotate both locks with both ABSL_ACQUIRED_AFTER +// and ABSL_ACQUIRED_BEFORE.) // -// As with GUARDED_BY, this is only applicable to mutexes that are shared +// As with ABSL_GUARDED_BY, this is only applicable to mutexes that are shared // fields or global variables. // // Example: // // Mutex m1_; -// Mutex m2_ ACQUIRED_AFTER(m1_); -#define ACQUIRED_AFTER(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) +// Mutex m2_ ABSL_ACQUIRED_AFTER(m1_); +#define ABSL_ACQUIRED_AFTER(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(acquired_after(__VA_ARGS__)) -#define ACQUIRED_BEFORE(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) +#define ABSL_ACQUIRED_BEFORE(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(acquired_before(__VA_ARGS__)) -// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED() +// ABSL_EXCLUSIVE_LOCKS_REQUIRED() / ABSL_SHARED_LOCKS_REQUIRED() // // Documents a function that expects a mutex to be held prior to entry. // The mutex is expected to be held both on entry to, and exit from, the @@ -114,77 +119,78 @@ // concurrently. // // Generally, non-const methods should be annotated with -// EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with -// SHARED_LOCKS_REQUIRED. +// ABSL_EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with +// ABSL_SHARED_LOCKS_REQUIRED. // // Example: // // Mutex mu1, mu2; -// int a GUARDED_BY(mu1); -// int b GUARDED_BY(mu2); -// -// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } -// void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } -#define EXCLUSIVE_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) +// int a ABSL_GUARDED_BY(mu1); +// int b ABSL_GUARDED_BY(mu2); +// +// void foo() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } +// void bar() const ABSL_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } +#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + exclusive_locks_required(__VA_ARGS__)) -#define SHARED_LOCKS_REQUIRED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) +#define ABSL_SHARED_LOCKS_REQUIRED(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(shared_locks_required(__VA_ARGS__)) -// LOCKS_EXCLUDED() +// ABSL_LOCKS_EXCLUDED() // // Documents the locks acquired in the body of the function. These locks // cannot be held when calling this function (as Abseil's `Mutex` locks are // non-reentrant). -#define LOCKS_EXCLUDED(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) +#define ABSL_LOCKS_EXCLUDED(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(locks_excluded(__VA_ARGS__)) -// LOCK_RETURNED() +// ABSL_LOCK_RETURNED() // // Documents a function that returns a mutex without acquiring it. For example, // a public getter method that returns a pointer to a private mutex should -// be annotated with LOCK_RETURNED. -#define LOCK_RETURNED(x) \ - THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) +// be annotated with ABSL_LOCK_RETURNED. +#define ABSL_LOCK_RETURNED(x) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(lock_returned(x)) -// LOCKABLE +// ABSL_LOCKABLE // // Documents if a class/type is a lockable type (such as the `Mutex` class). -#define LOCKABLE \ - THREAD_ANNOTATION_ATTRIBUTE__(lockable) +#define ABSL_LOCKABLE ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(lockable) -// SCOPED_LOCKABLE +// ABSL_SCOPED_LOCKABLE // // Documents if a class does RAII locking (such as the `MutexLock` class). // The constructor should use `LOCK_FUNCTION()` to specify the mutex that is // acquired, and the destructor should use `UNLOCK_FUNCTION()` with no // arguments; the analysis will assume that the destructor unlocks whatever the // constructor locked. -#define SCOPED_LOCKABLE \ - THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) +#define ABSL_SCOPED_LOCKABLE \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(scoped_lockable) -// EXCLUSIVE_LOCK_FUNCTION() +// ABSL_EXCLUSIVE_LOCK_FUNCTION() // // Documents functions that acquire a lock in the body of a function, and do // not release it. -#define EXCLUSIVE_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) +#define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + exclusive_lock_function(__VA_ARGS__)) -// SHARED_LOCK_FUNCTION() +// ABSL_SHARED_LOCK_FUNCTION() // // Documents functions that acquire a shared (reader) lock in the body of a // function, and do not release it. -#define SHARED_LOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) +#define ABSL_SHARED_LOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(shared_lock_function(__VA_ARGS__)) -// UNLOCK_FUNCTION() +// ABSL_UNLOCK_FUNCTION() // // Documents functions that expect a lock to be held on entry to the function, // and release it in the body of the function. -#define UNLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) +#define ABSL_UNLOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(unlock_function(__VA_ARGS__)) -// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION() +// ABSL_EXCLUSIVE_TRYLOCK_FUNCTION() / ABSL_SHARED_TRYLOCK_FUNCTION() // // Documents functions that try to acquire a lock, and return success or failure // (or a non-boolean value that can be interpreted as a boolean). @@ -192,76 +198,82 @@ // success, or `false` for functions that return `false` on success. The second // argument specifies the mutex that is locked on success. If unspecified, this // mutex is assumed to be `this`. -#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) +#define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + exclusive_trylock_function(__VA_ARGS__)) -#define SHARED_TRYLOCK_FUNCTION(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) +#define ABSL_SHARED_TRYLOCK_FUNCTION(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE( \ + shared_trylock_function(__VA_ARGS__)) -// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK() +// ABSL_ASSERT_EXCLUSIVE_LOCK() / ABSL_ASSERT_SHARED_LOCK() // // Documents functions that dynamically check to see if a lock is held, and fail // if it is not held. -#define ASSERT_EXCLUSIVE_LOCK(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) +#define ABSL_ASSERT_EXCLUSIVE_LOCK(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(assert_exclusive_lock(__VA_ARGS__)) -#define ASSERT_SHARED_LOCK(...) \ - THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) +#define ABSL_ASSERT_SHARED_LOCK(...) \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(assert_shared_lock(__VA_ARGS__)) -// NO_THREAD_SAFETY_ANALYSIS +// ABSL_NO_THREAD_SAFETY_ANALYSIS // // Turns off thread safety checking within the body of a particular function. // This annotation is used to mark functions that are known to be correct, but // the locking behavior is more complicated than the analyzer can handle. -#define NO_THREAD_SAFETY_ANALYSIS \ - THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) +#define ABSL_NO_THREAD_SAFETY_ANALYSIS \ + ABSL_INTERNAL_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis) //------------------------------------------------------------------------------ // Tool-Supplied Annotations //------------------------------------------------------------------------------ -// TS_UNCHECKED should be placed around lock expressions that are not valid +// ABSL_TS_UNCHECKED should be placed around lock expressions that are not valid // C++ syntax, but which are present for documentation purposes. These // annotations will be ignored by the analysis. -#define TS_UNCHECKED(x) "" +#define ABSL_TS_UNCHECKED(x) "" -// TS_FIXME is used to mark lock expressions that are not valid C++ syntax. +// ABSL_TS_FIXME is used to mark lock expressions that are not valid C++ syntax. // It is used by automated tools to mark and disable invalid expressions. -// The annotation should either be fixed, or changed to TS_UNCHECKED. -#define TS_FIXME(x) "" +// The annotation should either be fixed, or changed to ABSL_TS_UNCHECKED. +#define ABSL_TS_FIXME(x) "" -// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of -// a particular function. However, this attribute is used to mark functions +// Like ABSL_NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body +// of a particular function. However, this attribute is used to mark functions // that are incorrect and need to be fixed. It is used by automated tools to // avoid breaking the build when the analysis is updated. // Code owners are expected to eventually fix the routine. -#define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS +#define ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME ABSL_NO_THREAD_SAFETY_ANALYSIS -// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY -// annotation that needs to be fixed, because it is producing thread safety -// warning. It disables the GUARDED_BY. -#define GUARDED_BY_FIXME(x) +// Similar to ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a +// ABSL_GUARDED_BY annotation that needs to be fixed, because it is producing +// thread safety warning. It disables the ABSL_GUARDED_BY. +#define ABSL_GUARDED_BY_FIXME(x) // Disables warnings for a single read operation. This can be used to avoid // warnings when it is known that the read is not actually involved in a race, // but the compiler cannot confirm that. -#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x) - +#define ABSL_TS_UNCHECKED_READ(x) absl::base_internal::ts_unchecked_read(x) -namespace thread_safety_analysis { +namespace absl { +inline namespace lts_2019_08_08 { +namespace base_internal { // Takes a reference to a guarded data member, and returns an unguarded // reference. +// Do not used this function directly, use ABSL_TS_UNCHECKED_READ instead. template <typename T> -inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS { +inline const T& ts_unchecked_read(const T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { return v; } template <typename T> -inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS { +inline T& ts_unchecked_read(T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS { return v; } -} // namespace thread_safety_analysis +} // namespace base_internal +} // inline namespace lts_2019_08_08 +} // namespace absl #endif // ABSL_BASE_THREAD_ANNOTATIONS_H_ diff --git a/absl/base/throw_delegate_test.cc b/absl/base/throw_delegate_test.cc index 0f15df04..a74dd3cd 100644 --- a/absl/base/throw_delegate_test.cc +++ b/absl/base/throw_delegate_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/compiler_config_setting.bzl b/absl/compiler_config_setting.bzl index b77c4f56..66962294 100644 --- a/absl/compiler_config_setting.bzl +++ b/absl/compiler_config_setting.bzl @@ -5,35 +5,34 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# """Creates config_setting that allows selecting based on 'compiler' value.""" def create_llvm_config(name, visibility): - # The "do_not_use_tools_cpp_compiler_present" attribute exists to - # distinguish between older versions of Bazel that do not support - # "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do. - # In the future, the only way to select on the compiler will be through - # flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can - # be removed. - if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"): - native.config_setting( - name = name, - flag_values = { - "@bazel_tools//tools/cpp:compiler": "llvm", - }, - visibility = visibility, - ) - else: - native.config_setting( - name = name, - values = {"compiler": "llvm"}, - visibility = visibility, - ) + # The "do_not_use_tools_cpp_compiler_present" attribute exists to + # distinguish between older versions of Bazel that do not support + # "@bazel_tools//tools/cpp:compiler" flag_value, and newer ones that do. + # In the future, the only way to select on the compiler will be through + # flag_values{"@bazel_tools//tools/cpp:compiler"} and the else branch can + # be removed. + if hasattr(cc_common, "do_not_use_tools_cpp_compiler_present"): + native.config_setting( + name = name, + flag_values = { + "@bazel_tools//tools/cpp:compiler": "llvm", + }, + visibility = visibility, + ) + else: + native.config_setting( + name = name, + values = {"compiler": "llvm"}, + visibility = visibility, + ) diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index afc869f4..9e2a5b1e 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,11 +15,12 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", - "ABSL_TEST_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_EXCEPTIONS_FLAG", "ABSL_EXCEPTIONS_FLAG_LINKOPTS", + "ABSL_TEST_COPTS", ) package(default_visibility = ["//visibility:public"]) @@ -30,6 +31,7 @@ cc_library( name = "compressed_tuple", hdrs = ["internal/compressed_tuple.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/utility", ], @@ -39,8 +41,14 @@ cc_test( name = "compressed_tuple_test", srcs = ["internal/compressed_tuple_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":compressed_tuple", + ":test_instance_tracker", + "//absl/memory", + "//absl/types:any", + "//absl/types:optional", + "//absl/utility", "@com_google_googletest//:gtest_main", ], ) @@ -49,6 +57,7 @@ cc_library( name = "fixed_array", hdrs = ["fixed_array.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":compressed_tuple", "//absl/algorithm", @@ -63,7 +72,7 @@ cc_test( name = "fixed_array_test", srcs = ["fixed_array_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":fixed_array", "//absl/base:exception_testing", @@ -77,6 +86,7 @@ cc_test( name = "fixed_array_test_noexceptions", srcs = ["fixed_array_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":fixed_array", "//absl/base:exception_testing", @@ -90,7 +100,7 @@ cc_test( name = "fixed_array_exception_safety_test", srcs = ["fixed_array_exception_safety_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":fixed_array", "//absl/base:exception_safety_testing", @@ -102,6 +112,7 @@ cc_test( name = "fixed_array_benchmark", srcs = ["fixed_array_benchmark.cc"], copts = ABSL_TEST_COPTS + ["$(STACK_FRAME_UNLIMITED)"], + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], deps = [ ":fixed_array", @@ -110,10 +121,26 @@ cc_test( ) cc_library( + name = "inlined_vector_internal", + hdrs = ["internal/inlined_vector.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":compressed_tuple", + "//absl/base:core_headers", + "//absl/memory", + "//absl/meta:type_traits", + "//absl/types:span", + ], +) + +cc_library( name = "inlined_vector", hdrs = ["inlined_vector.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":inlined_vector_internal", "//absl/algorithm", "//absl/base:core_headers", "//absl/base:throw_delegate", @@ -121,12 +148,22 @@ cc_library( ], ) +cc_library( + name = "counting_allocator", + testonly = 1, + hdrs = ["internal/counting_allocator.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], +) + cc_test( name = "inlined_vector_test", srcs = ["inlined_vector_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ + ":counting_allocator", ":inlined_vector", ":test_instance_tracker", "//absl/base", @@ -143,7 +180,9 @@ cc_test( name = "inlined_vector_test_noexceptions", srcs = ["inlined_vector_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":counting_allocator", ":inlined_vector", ":test_instance_tracker", "//absl/base", @@ -160,30 +199,46 @@ cc_test( name = "inlined_vector_benchmark", srcs = ["inlined_vector_benchmark.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], deps = [ ":inlined_vector", "//absl/base", + "//absl/base:core_headers", "//absl/strings", "@com_github_google_benchmark//:benchmark_main", ], ) +cc_test( + name = "inlined_vector_exception_safety_test", + srcs = ["inlined_vector_exception_safety_test.cc"], + copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, + deps = [ + ":inlined_vector", + "//absl/base:exception_safety_testing", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "test_instance_tracker", testonly = 1, srcs = ["internal/test_instance_tracker.cc"], hdrs = ["internal/test_instance_tracker.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], + deps = ["//absl/types:compare"], ) cc_test( name = "test_instance_tracker_test", srcs = ["internal/test_instance_tracker_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":test_instance_tracker", "@com_google_googletest//:gtest_main", @@ -208,6 +263,7 @@ cc_library( name = "flat_hash_map", hdrs = ["flat_hash_map.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container_memory", ":hash_function_defaults", @@ -220,13 +276,15 @@ cc_library( cc_test( name = "flat_hash_map_test", srcs = ["flat_hash_map_test.cc"], - copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":flat_hash_map", ":hash_generator_testing", ":unordered_map_constructor_test", ":unordered_map_lookup_test", + ":unordered_map_members_test", ":unordered_map_modifiers_test", "//absl/types:any", "@com_google_googletest//:gtest_main", @@ -237,6 +295,7 @@ cc_library( name = "flat_hash_set", hdrs = ["flat_hash_set.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container_memory", ":hash_function_defaults", @@ -251,12 +310,14 @@ cc_test( name = "flat_hash_set_test", srcs = ["flat_hash_set_test.cc"], copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"], + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":flat_hash_set", ":hash_generator_testing", ":unordered_set_constructor_test", ":unordered_set_lookup_test", + ":unordered_set_members_test", ":unordered_set_modifiers_test", "//absl/memory", "//absl/strings", @@ -268,6 +329,7 @@ cc_library( name = "node_hash_map", hdrs = ["node_hash_map.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container_memory", ":hash_function_defaults", @@ -281,7 +343,8 @@ cc_library( cc_test( name = "node_hash_map_test", srcs = ["node_hash_map_test.cc"], - copts = ABSL_TEST_COPTS + ["-DUNORDERED_MAP_CXX17"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":hash_generator_testing", @@ -289,6 +352,7 @@ cc_test( ":tracked", ":unordered_map_constructor_test", ":unordered_map_lookup_test", + ":unordered_map_members_test", ":unordered_map_modifiers_test", "@com_google_googletest//:gtest_main", ], @@ -298,6 +362,7 @@ cc_library( name = "node_hash_set", hdrs = ["node_hash_set.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_function_defaults", ":node_hash_policy", @@ -311,12 +376,13 @@ cc_test( name = "node_hash_set_test", srcs = ["node_hash_set_test.cc"], copts = ABSL_TEST_COPTS + ["-DUNORDERED_SET_CXX17"], + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ - ":hash_generator_testing", ":node_hash_set", ":unordered_set_constructor_test", ":unordered_set_lookup_test", + ":unordered_set_members_test", ":unordered_set_modifiers_test", "@com_google_googletest//:gtest_main", ], @@ -326,6 +392,7 @@ cc_library( name = "container_memory", hdrs = ["internal/container_memory.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/memory", "//absl/utility", @@ -336,6 +403,7 @@ cc_test( name = "container_memory_test", srcs = ["internal/container_memory_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":container_memory", @@ -348,6 +416,7 @@ cc_library( name = "hash_function_defaults", hdrs = ["internal/hash_function_defaults.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", "//absl/hash", @@ -359,6 +428,7 @@ cc_test( name = "hash_function_defaults_test", srcs = ["internal/hash_function_defaults_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS, deps = [ ":hash_function_defaults", @@ -374,6 +444,7 @@ cc_library( srcs = ["internal/hash_generator_testing.cc"], hdrs = ["internal/hash_generator_testing.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_testing", "//absl/meta:type_traits", @@ -386,6 +457,7 @@ cc_library( testonly = 1, hdrs = ["internal/hash_policy_testing.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/hash", "//absl/strings", @@ -396,6 +468,7 @@ cc_test( name = "hash_policy_testing_test", srcs = ["internal/hash_policy_testing_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_testing", "@com_google_googletest//:gtest_main", @@ -406,6 +479,7 @@ cc_library( name = "hash_policy_traits", hdrs = ["internal/hash_policy_traits.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = ["//absl/meta:type_traits"], ) @@ -413,6 +487,7 @@ cc_test( name = "hash_policy_traits_test", srcs = ["internal/hash_policy_traits_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_traits", "@com_google_googletest//:gtest_main", @@ -423,6 +498,7 @@ cc_library( name = "hashtable_debug", hdrs = ["internal/hashtable_debug.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hashtable_debug_hooks", ], @@ -432,18 +508,56 @@ cc_library( name = "hashtable_debug_hooks", hdrs = ["internal/hashtable_debug_hooks.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, +) + +cc_library( + name = "hashtablez_sampler", + srcs = [ + "internal/hashtablez_sampler.cc", + "internal/hashtablez_sampler_force_weak_definition.cc", + ], + hdrs = ["internal/hashtablez_sampler.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":have_sse", + "//absl/base", + "//absl/base:core_headers", + "//absl/debugging:stacktrace", + "//absl/memory", + "//absl/synchronization", + "//absl/utility", + ], +) + +cc_test( + name = "hashtablez_sampler_test", + srcs = ["internal/hashtablez_sampler_test.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":hashtablez_sampler", + ":have_sse", + "//absl/base:core_headers", + "//absl/synchronization", + "//absl/synchronization:thread_pool", + "//absl/time", + "@com_google_googletest//:gtest_main", + ], ) cc_library( name = "node_hash_policy", hdrs = ["internal/node_hash_policy.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_test( name = "node_hash_policy_test", srcs = ["internal/node_hash_policy_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_policy_traits", ":node_hash_policy", @@ -455,9 +569,30 @@ cc_library( name = "raw_hash_map", hdrs = ["internal/raw_hash_map.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":container_memory", ":raw_hash_set", + "//absl/base:throw_delegate", + ], +) + +cc_library( + name = "have_sse", + hdrs = ["internal/have_sse.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], +) + +cc_library( + name = "common", + hdrs = ["internal/common.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/meta:type_traits", + "//absl/types:optional", ], ) @@ -466,11 +601,15 @@ cc_library( srcs = ["internal/raw_hash_set.cc"], hdrs = ["internal/raw_hash_set.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":common", ":compressed_tuple", ":container_memory", ":hash_policy_traits", ":hashtable_debug_hooks", + ":hashtablez_sampler", + ":have_sse", ":layout", "//absl/base:bits", "//absl/base:config", @@ -478,7 +617,6 @@ cc_library( "//absl/base:endian", "//absl/memory", "//absl/meta:type_traits", - "//absl/types:optional", "//absl/utility", ], ) @@ -507,6 +645,7 @@ cc_test( size = "small", srcs = ["internal/raw_hash_set_allocator_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":raw_hash_set", ":tracked", @@ -519,6 +658,7 @@ cc_library( name = "layout", hdrs = ["internal/layout.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:core_headers", "//absl/meta:type_traits", @@ -533,6 +673,7 @@ cc_test( size = "small", srcs = ["internal/layout_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS, visibility = ["//visibility:private"], deps = [ @@ -549,6 +690,7 @@ cc_library( testonly = 1, hdrs = ["internal/tracked.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, ) cc_library( @@ -556,6 +698,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_map_constructor_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -568,6 +711,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_map_lookup_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -580,6 +724,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_map_modifiers_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -592,9 +737,35 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_set_constructor_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", + "//absl/meta:type_traits", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "unordered_set_members_test", + testonly = 1, + hdrs = ["internal/unordered_set_members_test.h"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/meta:type_traits", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "unordered_map_members_test", + testonly = 1, + hdrs = ["internal/unordered_map_members_test.h"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/meta:type_traits", "@com_google_googletest//:gtest", ], ) @@ -604,6 +775,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_set_lookup_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -616,6 +788,7 @@ cc_library( testonly = 1, hdrs = ["internal/unordered_set_modifiers_test.h"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash_generator_testing", ":hash_policy_testing", @@ -627,10 +800,12 @@ cc_test( name = "unordered_set_test", srcs = ["internal/unordered_set_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":unordered_set_constructor_test", ":unordered_set_lookup_test", + ":unordered_set_members_test", ":unordered_set_modifiers_test", "@com_google_googletest//:gtest_main", ], @@ -640,10 +815,12 @@ cc_test( name = "unordered_map_test", srcs = ["internal/unordered_map_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = NOTEST_TAGS_NONMOBILE, deps = [ ":unordered_map_constructor_test", ":unordered_map_lookup_test", + ":unordered_map_members_test", ":unordered_map_modifiers_test", "@com_google_googletest//:gtest_main", ], diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 8605facc..7988b12f 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -20,10 +20,6 @@ absl_cc_library( NAME container - SRCS - "internal/raw_hash_set.cc" - COPTS - ${ABSL_DEFAULT_COPTS} PUBLIC ) @@ -31,7 +27,9 @@ absl_cc_library( NAME compressed_tuple HDRS - "internal/compressed_tuple.h" + "internal/compressed_tuple.h" + COPTS + ${ABSL_DEFAULT_COPTS} DEPS absl::utility PUBLIC @@ -42,8 +40,15 @@ absl_cc_test( compressed_tuple_test SRCS "internal/compressed_tuple_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS + absl::any absl::compressed_tuple + absl::memory + absl::optional + absl::test_instance_tracker + absl::utility gmock_main ) @@ -70,6 +75,7 @@ absl_cc_test( SRCS "fixed_array_test.cc" COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} LINKOPTS ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} @@ -86,6 +92,8 @@ absl_cc_test( fixed_array_test_noexceptions SRCS "fixed_array_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::fixed_array absl::exception_testing @@ -100,6 +108,7 @@ absl_cc_test( SRCS "fixed_array_exception_safety_test.cc" COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} LINKOPTS ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} @@ -111,6 +120,22 @@ absl_cc_test( absl_cc_library( NAME + inlined_vector_internal + HDRS + "internal/inlined_vector.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::compressed_tuple + absl::core_headers + absl::memory + absl::span + absl::type_traits + PUBLIC +) + +absl_cc_library( + NAME inlined_vector HDRS "inlined_vector.h" @@ -119,21 +144,33 @@ absl_cc_library( DEPS absl::algorithm absl::core_headers + absl::inlined_vector_internal absl::throw_delegate absl::memory PUBLIC ) +absl_cc_library( + NAME + counting_allocator + HDRS + "internal/counting_allocator.h" + COPTS + ${ABSL_DEFAULT_COPTS} +) + absl_cc_test( NAME inlined_vector_test SRCS "inlined_vector_test.cc" COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} LINKOPTS ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} DEPS + absl::counting_allocator absl::inlined_vector absl::test_instance_tracker absl::base @@ -150,6 +187,8 @@ absl_cc_test( inlined_vector_test_noexceptions SRCS "inlined_vector_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::inlined_vector absl::test_instance_tracker @@ -162,6 +201,22 @@ absl_cc_test( gmock_main ) +absl_cc_test( + NAME + inlined_vector_exception_safety_test + SRCS + "inlined_vector_exception_safety_test.cc" + COPTS + ${ABSL_TEST_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::inlined_vector + absl::exception_safety_testing + gmock_main +) + absl_cc_library( NAME test_instance_tracker @@ -171,6 +226,8 @@ absl_cc_library( "internal/test_instance_tracker.cc" COPTS ${ABSL_DEFAULT_COPTS} + DEPS + absl::compare TESTONLY ) @@ -179,6 +236,8 @@ absl_cc_test( test_instance_tracker_test SRCS "internal/test_instance_tracker_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::test_instance_tracker gmock_main @@ -206,12 +265,13 @@ absl_cc_test( SRCS "flat_hash_map_test.cc" COPTS - "-DUNORDERED_MAP_CXX17" + ${ABSL_TEST_COPTS} DEPS absl::flat_hash_map absl::hash_generator_testing absl::unordered_map_constructor_test absl::unordered_map_lookup_test + absl::unordered_map_members_test absl::unordered_map_modifiers_test absl::any gmock_main @@ -240,12 +300,14 @@ absl_cc_test( SRCS "flat_hash_set_test.cc" COPTS + ${ABSL_TEST_COPTS} "-DUNORDERED_SET_CXX17" DEPS absl::flat_hash_set absl::hash_generator_testing absl::unordered_set_constructor_test absl::unordered_set_lookup_test + absl::unordered_set_members_test absl::unordered_set_modifiers_test absl::memory absl::strings @@ -275,13 +337,14 @@ absl_cc_test( SRCS "node_hash_map_test.cc" COPTS - "-DUNORDERED_MAP_CXX17" + ${ABSL_TEST_COPTS} DEPS absl::hash_generator_testing absl::node_hash_map absl::tracked absl::unordered_map_constructor_test absl::unordered_map_lookup_test + absl::unordered_map_members_test absl::unordered_map_modifiers_test gmock_main ) @@ -308,12 +371,14 @@ absl_cc_test( SRCS "node_hash_set_test.cc" COPTS + ${ABSL_TEST_COPTS} "-DUNORDERED_SET_CXX17" DEPS absl::hash_generator_testing absl::node_hash_set absl::unordered_set_constructor_test absl::unordered_set_lookup_test + absl::unordered_set_members_test absl::unordered_set_modifiers_test gmock_main ) @@ -336,6 +401,8 @@ absl_cc_test( container_memory_test SRCS "internal/container_memory_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::container_memory absl::strings @@ -361,6 +428,8 @@ absl_cc_test( hash_function_defaults_test SRCS "internal/hash_function_defaults_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash_function_defaults absl::hash @@ -402,6 +471,8 @@ absl_cc_test( hash_policy_testing_test SRCS "internal/hash_policy_testing_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash_policy_testing gmock_main @@ -424,6 +495,8 @@ absl_cc_test( hash_policy_traits_test SRCS "internal/hash_policy_traits_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash_policy_traits gmock_main @@ -431,6 +504,35 @@ absl_cc_test( absl_cc_library( NAME + hashtablez_sampler + HDRS + "internal/hashtablez_sampler.h" + SRCS + "internal/hashtablez_sampler.cc" + "internal/hashtablez_sampler_force_weak_definition.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::have_sse + absl::synchronization +) + +absl_cc_test( + NAME + hashtablez_sampler_test + SRCS + "internal/hashtablez_sampler_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::hashtablez_sampler + absl::have_sse + gmock_main +) + +absl_cc_library( + NAME hashtable_debug HDRS "internal/hashtable_debug.h" @@ -452,6 +554,15 @@ absl_cc_library( absl_cc_library( NAME + have_sse + HDRS + "internal/have_sse.h" + COPTS + ${ABSL_DEFAULT_COPTS} +) + +absl_cc_library( + NAME node_hash_policy HDRS "internal/node_hash_policy.h" @@ -465,6 +576,8 @@ absl_cc_test( node_hash_policy_test SRCS "internal/node_hash_policy_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash_policy_traits absl::node_hash_policy @@ -481,11 +594,23 @@ absl_cc_library( DEPS absl::container_memory absl::raw_hash_set + absl::throw_delegate PUBLIC ) absl_cc_library( NAME + container_common + HDRS + "internal/commom.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::type_traits +) + +absl_cc_library( + NAME raw_hash_set HDRS "internal/raw_hash_set.h" @@ -494,19 +619,22 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::bits absl::compressed_tuple + absl::config + absl::container_common absl::container_memory + absl::core_headers + absl::endian absl::hash_policy_traits absl::hashtable_debug_hooks + absl::have_sse absl::layout - absl::bits - absl::config - absl::core_headers - absl::endian absl::memory absl::meta absl::optional absl::utility + absl::hashtablez_sampler PUBLIC ) @@ -515,6 +643,8 @@ absl_cc_test( raw_hash_set_test SRCS "internal/raw_hash_set_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::container_memory absl::hash_function_defaults @@ -532,6 +662,8 @@ absl_cc_test( raw_hash_set_allocator_test SRCS "internal/raw_hash_set_allocator_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::raw_hash_set absl::tracked @@ -560,6 +692,8 @@ absl_cc_test( layout_test SRCS "internal/layout_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::layout absl::base @@ -608,6 +742,19 @@ absl_cc_library( absl_cc_library( NAME + unordered_map_members_test + HDRS + "internal/unordered_map_members_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::type_traits + gmock + TESTONLY +) + +absl_cc_library( + NAME unordered_map_modifiers_test HDRS "internal/unordered_map_modifiers_test.h" @@ -650,6 +797,19 @@ absl_cc_library( absl_cc_library( NAME + unordered_set_members_test + HDRS + "internal/unordered_set_members_test.h" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::type_traits + gmock + TESTONLY +) + +absl_cc_library( + NAME unordered_set_modifiers_test HDRS "internal/unordered_set_modifiers_test.h" @@ -667,9 +827,12 @@ absl_cc_test( unordered_set_test SRCS "internal/unordered_set_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::unordered_set_constructor_test absl::unordered_set_lookup_test + absl::unordered_set_members_test absl::unordered_set_modifiers_test gmock_main ) @@ -679,9 +842,12 @@ absl_cc_test( unordered_map_test SRCS "internal/unordered_map_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::unordered_map_constructor_test absl::unordered_map_lookup_test + absl::unordered_map_members_test absl::unordered_map_modifiers_test gmock_main ) diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h index 7f6a3afd..1e0da5eb 100644 --- a/absl/container/fixed_array.h +++ b/absl/container/fixed_array.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -51,7 +51,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1); @@ -515,6 +515,7 @@ void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct( #endif // ADDRESS_SANITIZER static_cast<void>(n); // Mark used when not in asan mode } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_CONTAINER_FIXED_ARRAY_H_ diff --git a/absl/container/fixed_array_benchmark.cc b/absl/container/fixed_array_benchmark.cc index b4f0cf2a..3c7a5a72 100644 --- a/absl/container/fixed_array_benchmark.cc +++ b/absl/container/fixed_array_benchmark.cc @@ -1,10 +1,10 @@ -// Copyright 2017 The Abseil Authors. +// Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/container/fixed_array.h" - #include <stddef.h> + #include <string> #include "benchmark/benchmark.h" +#include "absl/container/fixed_array.h" namespace { @@ -25,8 +25,9 @@ namespace { // set an int to a constant.. class SimpleClass { public: - SimpleClass() : i(3) { } + SimpleClass() : i(3) {} ~SimpleClass() { i = 0; } + private: int i; }; diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc index 4d0430b3..4a67bb46 100644 --- a/absl/container/fixed_array_exception_safety_test.cc +++ b/absl/container/fixed_array_exception_safety_test.cc @@ -1,10 +1,10 @@ -// Copyright 2017 The Abseil Authors. +// Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -14,13 +14,12 @@ #include <initializer_list> -#include "absl/container/fixed_array.h" - #include "gtest/gtest.h" #include "absl/base/internal/exception_safety_testing.h" +#include "absl/container/fixed_array.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { @@ -115,5 +114,5 @@ TEST(FixedArrayExceptionSafety, Fill) { } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc index 205ff41f..2b1cf47e 100644 --- a/absl/container/fixed_array_test.cc +++ b/absl/container/fixed_array_test.cc @@ -1,10 +1,10 @@ -// Copyright 2017 The Abseil Authors. +// Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,6 +15,7 @@ #include "absl/container/fixed_array.h" #include <stdio.h> + #include <cstring> #include <list> #include <memory> @@ -42,11 +43,7 @@ static bool IsOnStack(const ArrayType& a) { class ConstructionTester { public: - ConstructionTester() - : self_ptr_(this), - value_(0) { - constructions++; - } + ConstructionTester() : self_ptr_(this), value_(0) { constructions++; } ~ConstructionTester() { assert(self_ptr_ == this); self_ptr_ = nullptr; @@ -58,9 +55,7 @@ class ConstructionTester { static int constructions; static int destructions; - void CheckConstructed() { - assert(self_ptr_ == this); - } + void CheckConstructed() { assert(self_ptr_ == this); } void set(int value) { value_ = value; } int get() { return value_; } @@ -150,7 +145,7 @@ TEST(FixedArrayTest, SmallObjects) { } { - // Arrays of > default size should be on the stack + // Arrays of > default size should be on the heap absl::FixedArray<int, 100> array(101); EXPECT_FALSE(IsOnStack(array)); } @@ -160,13 +155,13 @@ TEST(FixedArrayTest, SmallObjects) { // same amount of stack space absl::FixedArray<int> array1(0); absl::FixedArray<char> array2(0); - EXPECT_LE(sizeof(array1), sizeof(array2)+100); - EXPECT_LE(sizeof(array2), sizeof(array1)+100); + EXPECT_LE(sizeof(array1), sizeof(array2) + 100); + EXPECT_LE(sizeof(array2), sizeof(array1) + 100); } { // Ensure that vectors are properly constructed inside a fixed array. - absl::FixedArray<std::vector<int> > array(2); + absl::FixedArray<std::vector<int>> array(2); EXPECT_EQ(0, array[0].size()); EXPECT_EQ(0, array[1].size()); } @@ -270,8 +265,8 @@ static void TestArray(int n) { array.data()[i].set(i + 1); } for (int i = 0; i < n; i++) { - EXPECT_THAT(array[i].get(), i+1); - EXPECT_THAT(array.data()[i].get(), i+1); + EXPECT_THAT(array[i].get(), i + 1); + EXPECT_THAT(array.data()[i].get(), i + 1); } } // Close scope containing 'array'. @@ -296,7 +291,7 @@ static void TestArrayOfArrays(int n) { ASSERT_EQ(array.size(), n); ASSERT_EQ(array.memsize(), - sizeof(ConstructionTester) * elements_per_inner_array * n); + sizeof(ConstructionTester) * elements_per_inner_array * n); ASSERT_EQ(array.begin() + n, array.end()); // Check that all elements were constructed @@ -316,7 +311,7 @@ static void TestArrayOfArrays(int n) { } for (int i = 0; i < n; i++) { for (int j = 0; j < elements_per_inner_array; j++) { - ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j); + ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j); ASSERT_EQ((array.data()[i])[j].get(), i * elements_per_inner_array + j); } } @@ -329,8 +324,7 @@ static void TestArrayOfArrays(int n) { } for (int i = 0; i < n; i++) { for (int j = 0; j < elements_per_inner_array; j++) { - ASSERT_EQ((array[i])[j].get(), - (i + 1) * elements_per_inner_array + j); + ASSERT_EQ((array[i])[j].get(), (i + 1) * elements_per_inner_array + j); ASSERT_EQ((array.data()[i])[j].get(), (i + 1) * elements_per_inner_array + j); } @@ -343,7 +337,7 @@ static void TestArrayOfArrays(int n) { } TEST(IteratorConstructorTest, NonInline) { - int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; absl::FixedArray<int, ABSL_ARRAYSIZE(kInput) - 1> const fixed( kInput, kInput + ABSL_ARRAYSIZE(kInput)); ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size()); @@ -353,7 +347,7 @@ TEST(IteratorConstructorTest, NonInline) { } TEST(IteratorConstructorTest, Inline) { - int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; absl::FixedArray<int, ABSL_ARRAYSIZE(kInput)> const fixed( kInput, kInput + ABSL_ARRAYSIZE(kInput)); ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size()); @@ -363,9 +357,10 @@ TEST(IteratorConstructorTest, Inline) { } TEST(IteratorConstructorTest, NonPod) { - char const* kInput[] = - { "red", "orange", "yellow", "green", "blue", "indigo", "violet" }; - absl::FixedArray<std::string> const fixed(kInput, kInput + ABSL_ARRAYSIZE(kInput)); + char const* kInput[] = {"red", "orange", "yellow", "green", + "blue", "indigo", "violet"}; + absl::FixedArray<std::string> const fixed(kInput, + kInput + ABSL_ARRAYSIZE(kInput)); ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size()); for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) { ASSERT_EQ(kInput[i], fixed[i]); @@ -380,7 +375,7 @@ TEST(IteratorConstructorTest, FromEmptyVector) { } TEST(IteratorConstructorTest, FromNonEmptyVector) { - int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; std::vector<int> const items(kInput, kInput + ABSL_ARRAYSIZE(kInput)); absl::FixedArray<int> const fixed(items.begin(), items.end()); ASSERT_EQ(items.size(), fixed.size()); @@ -390,7 +385,7 @@ TEST(IteratorConstructorTest, FromNonEmptyVector) { } TEST(IteratorConstructorTest, FromBidirectionalIteratorRange) { - int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + int const kInput[] = {2, 3, 5, 7, 11, 13, 17}; std::list<int> const items(kInput, kInput + ABSL_ARRAYSIZE(kInput)); absl::FixedArray<int> const fixed(items.begin(), items.end()); EXPECT_THAT(fixed, testing::ElementsAreArray(kInput)); @@ -507,9 +502,8 @@ struct PickyDelete { TEST(FixedArrayTest, UsesGlobalAlloc) { absl::FixedArray<PickyDelete, 0> a(5); } - TEST(FixedArrayTest, Data) { - static const int kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; + static const int kInput[] = {2, 3, 5, 7, 11, 13, 17}; absl::FixedArray<int> fa(std::begin(kInput), std::end(kInput)); EXPECT_EQ(fa.data(), &*fa.begin()); EXPECT_EQ(fa.data(), &fa[0]); @@ -823,7 +817,7 @@ TEST(AllocatorSupportTest, SizeValAllocConstructor) { #ifdef ADDRESS_SANITIZER TEST(FixedArrayTest, AddressSanitizerAnnotations1) { absl::FixedArray<int, 32> a(10); - int *raw = a.data(); + int* raw = a.data(); raw[0] = 0; raw[9] = 0; EXPECT_DEATH(raw[-2] = 0, "container-overflow"); @@ -834,7 +828,7 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations1) { TEST(FixedArrayTest, AddressSanitizerAnnotations2) { absl::FixedArray<char, 17> a(12); - char *raw = a.data(); + char* raw = a.data(); raw[0] = 0; raw[11] = 0; EXPECT_DEATH(raw[-7] = 0, "container-overflow"); @@ -845,7 +839,7 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations2) { TEST(FixedArrayTest, AddressSanitizerAnnotations3) { absl::FixedArray<uint64_t, 20> a(20); - uint64_t *raw = a.data(); + uint64_t* raw = a.data(); raw[0] = 0; raw[19] = 0; EXPECT_DEATH(raw[-1] = 0, "container-overflow"); @@ -854,7 +848,7 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations3) { TEST(FixedArrayTest, AddressSanitizerAnnotations4) { absl::FixedArray<ThreeInts> a(10); - ThreeInts *raw = a.data(); + ThreeInts* raw = a.data(); raw[0] = ThreeInts(); raw[9] = ThreeInts(); // Note: raw[-1] is pointing to 12 bytes before the container range. However, @@ -869,4 +863,21 @@ TEST(FixedArrayTest, AddressSanitizerAnnotations4) { } #endif // ADDRESS_SANITIZER +TEST(FixedArrayTest, AbslHashValueWorks) { + using V = absl::FixedArray<int>; + std::vector<V> cases; + + // Generate a variety of vectors some of these are small enough for the inline + // space but are stored out of line. + for (int i = 0; i < 10; ++i) { + V v(i); + for (int j = 0; j < i; ++j) { + v[j] = j; + } + cases.push_back(v); + } + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases)); +} + } // namespace diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index ed453348..a711398e 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -42,7 +42,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class K, class V> struct FlatHashMapPolicy; @@ -78,7 +78,7 @@ struct FlatHashMapPolicy; // NOTE: A `flat_hash_map` stores its value types directly inside its // implementation array to avoid memory indirection. Because a `flat_hash_map` // is designed to move data when rehashed, map values will not retain pointer -// stability. If you require pointer stability, or your values are large, +// stability. If you require pointer stability, or if your values are large, // consider using `absl::flat_hash_map<Key, std::unique_ptr<Value>>` instead. // If your types are not moveable or you require pointer stability for keys, // consider `absl::node_hash_map`. @@ -220,8 +220,12 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< // Erases the element at `position` of the `flat_hash_map`, returning // `void`. // - // NOTE: this return behavior is different than that of STL containers in - // general and `std::unordered_map` in particular. + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_map` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // map.erase(it++); // // iterator erase(const_iterator first, const_iterator last): // @@ -528,25 +532,26 @@ namespace container_internal { template <class K, class V> struct FlatHashMapPolicy { - using slot_type = container_internal::slot_type<K, V>; + using slot_policy = container_internal::map_slot_policy<K, V>; + using slot_type = typename slot_policy::slot_type; using key_type = K; using mapped_type = V; using init_type = std::pair</*non const*/ key_type, mapped_type>; template <class Allocator, class... Args> static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { - slot_type::construct(alloc, slot, std::forward<Args>(args)...); + slot_policy::construct(alloc, slot, std::forward<Args>(args)...); } template <class Allocator> static void destroy(Allocator* alloc, slot_type* slot) { - slot_type::destroy(alloc, slot); + slot_policy::destroy(alloc, slot); } template <class Allocator> static void transfer(Allocator* alloc, slot_type* new_slot, slot_type* old_slot) { - slot_type::transfer(alloc, new_slot, old_slot); + slot_policy::transfer(alloc, new_slot, old_slot); } template <class F, class... Args> @@ -576,7 +581,7 @@ struct IsUnorderedContainer< } // namespace container_algorithm_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_ diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc index 02d2fa81..3f11a52c 100644 --- a/absl/container/flat_hash_map_test.cc +++ b/absl/container/flat_hash_map_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,11 +17,12 @@ #include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/unordered_map_constructor_test.h" #include "absl/container/internal/unordered_map_lookup_test.h" +#include "absl/container/internal/unordered_map_members_test.h" #include "absl/container/internal/unordered_map_modifiers_test.h" #include "absl/types/any.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { using ::absl::container_internal::hash_internal::Enum; @@ -31,19 +32,20 @@ using ::testing::Pair; using ::testing::UnorderedElementsAre; template <class K, class V> -using Map = - flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual, Alloc<>>; +using Map = flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual, + Alloc<std::pair<const K, V>>>; static_assert(!std::is_standard_layout<NonStandardLayout>(), ""); using MapTypes = - ::testing::Types<Map<int, int>, Map<std::string, int>, Map<Enum, std::string>, - Map<EnumClass, int>, Map<int, NonStandardLayout>, - Map<NonStandardLayout, int>>; + ::testing::Types<Map<int, int>, Map<std::string, int>, + Map<Enum, std::string>, Map<EnumClass, int>, + Map<int, NonStandardLayout>, Map<NonStandardLayout, int>>; -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ConstructorTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, LookupTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashMap, ModifiersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ConstructorTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, LookupTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, MembersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ModifiersTest, MapTypes); TEST(FlatHashMap, StandardLayout) { struct Int { @@ -140,6 +142,7 @@ TEST(FlatHashMap, LazyKeyPattern) { int conversions = 0; int hashes = 0; flat_hash_map<size_t, size_t, Hash, Eq> m(0, Hash{&hashes}); + m.reserve(3); m[LazyInt(1, &conversions)] = 1; EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1))); @@ -204,7 +207,9 @@ TEST(FlatHashMap, MergeExtractInsert) { m.insert(std::move(node)); EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9))); } -#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__) + +#if (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) && \ + !defined(__EMSCRIPTEN__) TEST(FlatHashMap, Any) { absl::flat_hash_map<int, absl::any> m; m.emplace(1, 7); @@ -235,9 +240,10 @@ TEST(FlatHashMap, Any) { ASSERT_NE(it2, m2.end()); EXPECT_EQ(7, it2->second); } -#endif // __ANDROID__ +#endif // (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) && + // !defined(__EMSCRIPTEN__) } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/flat_hash_set.h b/absl/container/flat_hash_set.h index b175b1bf..8adbbcd5 100644 --- a/absl/container/flat_hash_set.h +++ b/absl/container/flat_hash_set.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -40,7 +40,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <typename T> struct FlatHashSetPolicy; @@ -56,9 +56,9 @@ struct FlatHashSetPolicy; // following notable differences: // // * Requires keys that are CopyConstructible -// * Supports heterogeneous lookup, through `find()`, `operator[]()` and -// `insert()`, provided that the set is provided a compatible heterogeneous -// hashing function and equality operator. +// * Supports heterogeneous lookup, through `find()` and `insert()`, provided +// that the set is provided a compatible heterogeneous hashing function and +// equality operator. // * Invalidates any references and pointers to elements within the table after // `rehash()`. // * Contains a `capacity()` member function indicating the number of element @@ -213,8 +213,12 @@ class flat_hash_set // Erases the element at `position` of the `flat_hash_set`, returning // `void`. // - // NOTE: this return behavior is different than that of STL containers in - // general and `std::unordered_map` in particular. + // NOTE: returning `void` in this case is different than that of STL + // containers in general and `std::unordered_set` in particular (which + // return an iterator to the element following the erased element). If that + // iterator is needed, simply post increment the iterator: + // + // set.erase(it++); // // iterator erase(const_iterator first, const_iterator last): // @@ -485,7 +489,7 @@ struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>> } // namespace container_algorithm_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_FLAT_HASH_SET_H_ diff --git a/absl/container/flat_hash_set_test.cc b/absl/container/flat_hash_set_test.cc index cabc2b59..56140bbe 100644 --- a/absl/container/flat_hash_set_test.cc +++ b/absl/container/flat_hash_set_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,12 +19,13 @@ #include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/unordered_set_constructor_test.h" #include "absl/container/internal/unordered_set_lookup_test.h" +#include "absl/container/internal/unordered_set_members_test.h" #include "absl/container/internal/unordered_set_modifiers_test.h" #include "absl/memory/memory.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -41,9 +42,10 @@ using Set = using SetTypes = ::testing::Types<Set<int>, Set<std::string>, Set<Enum>, Set<EnumClass>>; -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ConstructorTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, LookupTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(FlatHashSet, ModifiersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, ConstructorTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, LookupTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, MembersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashSet, ModifiersTest, SetTypes); TEST(FlatHashSet, EmplaceString) { std::vector<std::string> v = {"a", "b"}; @@ -124,5 +126,5 @@ TEST(FlatHashSet, MergeExtractInsert) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 37714baf..27186b15 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -1,10 +1,10 @@ -// Copyright 2018 The Abseil Authors. +// Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -50,11 +50,11 @@ #include "absl/base/internal/throw_delegate.h" #include "absl/base/optimization.h" #include "absl/base/port.h" +#include "absl/container/internal/inlined_vector.h" #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { - +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // InlinedVector // ----------------------------------------------------------------------------- @@ -67,119 +67,181 @@ inline namespace lts_2018_12_18 { // designed to cover the same API footprint as covered by `std::vector`. template <typename T, size_t N, typename A = std::allocator<T>> class InlinedVector { - static_assert(N > 0, "InlinedVector requires inline capacity greater than 0"); - constexpr static typename A::size_type inlined_capacity() { - return static_cast<typename A::size_type>(N); - } + static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity."); - template <typename Iterator> - using DisableIfIntegral = - absl::enable_if_t<!std::is_integral<Iterator>::value>; + using Storage = inlined_vector_internal::Storage<T, N, A>; + using rvalue_reference = typename Storage::rvalue_reference; + using MoveIterator = typename Storage::MoveIterator; + using AllocatorTraits = typename Storage::AllocatorTraits; + using IsMemcpyOk = typename Storage::IsMemcpyOk; template <typename Iterator> - using EnableIfInputIterator = absl::enable_if_t<std::is_convertible< - typename std::iterator_traits<Iterator>::iterator_category, - std::input_iterator_tag>::value>; + using IteratorValueAdapter = + typename Storage::template IteratorValueAdapter<Iterator>; + using CopyValueAdapter = typename Storage::CopyValueAdapter; + using DefaultValueAdapter = typename Storage::DefaultValueAdapter; template <typename Iterator> - using IteratorCategory = - typename std::iterator_traits<Iterator>::iterator_category; - - using rvalue_reference = typename A::value_type&&; + using EnableIfAtLeastForwardIterator = absl::enable_if_t< + inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>; + template <typename Iterator> + using DisableIfAtLeastForwardIterator = absl::enable_if_t< + !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>; public: - using allocator_type = A; - using value_type = typename allocator_type::value_type; - using pointer = typename allocator_type::pointer; - using const_pointer = typename allocator_type::const_pointer; - using reference = typename allocator_type::reference; - using const_reference = typename allocator_type::const_reference; - using size_type = typename allocator_type::size_type; - using difference_type = typename allocator_type::difference_type; - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator<iterator>; - using const_reverse_iterator = std::reverse_iterator<const_iterator>; + using allocator_type = typename Storage::allocator_type; + using value_type = typename Storage::value_type; + using pointer = typename Storage::pointer; + using const_pointer = typename Storage::const_pointer; + using reference = typename Storage::reference; + using const_reference = typename Storage::const_reference; + using size_type = typename Storage::size_type; + using difference_type = typename Storage::difference_type; + using iterator = typename Storage::iterator; + using const_iterator = typename Storage::const_iterator; + using reverse_iterator = typename Storage::reverse_iterator; + using const_reverse_iterator = typename Storage::const_reverse_iterator; // --------------------------------------------------------------------------- // InlinedVector Constructors and Destructor // --------------------------------------------------------------------------- - // Creates an empty inlined vector with a default initialized allocator. - InlinedVector() noexcept(noexcept(allocator_type())) - : allocator_and_tag_(allocator_type()) {} + // Creates an empty inlined vector with a value-initialized allocator. + InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {} - // Creates an empty inlined vector with a specified allocator. + // Creates an empty inlined vector with a copy of `alloc`. explicit InlinedVector(const allocator_type& alloc) noexcept - : allocator_and_tag_(alloc) {} + : storage_(alloc) {} // Creates an inlined vector with `n` copies of `value_type()`. explicit InlinedVector(size_type n, const allocator_type& alloc = allocator_type()) - : allocator_and_tag_(alloc) { - InitAssign(n); + : storage_(alloc) { + storage_.Initialize(DefaultValueAdapter(), n); } // Creates an inlined vector with `n` copies of `v`. InlinedVector(size_type n, const_reference v, const allocator_type& alloc = allocator_type()) - : allocator_and_tag_(alloc) { - InitAssign(n, v); + : storage_(alloc) { + storage_.Initialize(CopyValueAdapter(v), n); } - // Creates an inlined vector of copies of the values in `init_list`. - InlinedVector(std::initializer_list<value_type> init_list, + // Creates an inlined vector with copies of the elements of `list`. + InlinedVector(std::initializer_list<value_type> list, const allocator_type& alloc = allocator_type()) - : allocator_and_tag_(alloc) { - AppendRange(init_list.begin(), init_list.end(), - IteratorCategory<decltype(init_list.begin())>{}); - } + : InlinedVector(list.begin(), list.end(), alloc) {} // Creates an inlined vector with elements constructed from the provided - // Iterator range [`first`, `last`). + // forward iterator range [`first`, `last`). // - // NOTE: The `enable_if` prevents ambiguous interpretation between a call to + // NOTE: the `enable_if` prevents ambiguous interpretation between a call to // this constructor with two integral arguments and a call to the above // `InlinedVector(size_type, const_reference)` constructor. - template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr> + template <typename ForwardIterator, + EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> + InlinedVector(ForwardIterator first, ForwardIterator last, + const allocator_type& alloc = allocator_type()) + : storage_(alloc) { + storage_.Initialize(IteratorValueAdapter<ForwardIterator>(first), + std::distance(first, last)); + } + + // Creates an inlined vector with elements constructed from the provided input + // iterator range [`first`, `last`). + template <typename InputIterator, + DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> InlinedVector(InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()) - : allocator_and_tag_(alloc) { - AppendRange(first, last, IteratorCategory<InputIterator>{}); + : storage_(alloc) { + std::copy(first, last, std::back_inserter(*this)); } - // Creates a copy of `other` using `other`'s allocator. - InlinedVector(const InlinedVector& other); + // Creates an inlined vector by copying the contents of `other` using + // `other`'s allocator. + InlinedVector(const InlinedVector& other) + : InlinedVector(other, *other.storage_.GetAllocPtr()) {} - // Creates a copy of `other` but with a specified allocator. - InlinedVector(const InlinedVector& other, const allocator_type& alloc); + // Creates an inlined vector by copying the contents of `other` using `alloc`. + InlinedVector(const InlinedVector& other, const allocator_type& alloc) + : storage_(alloc) { + if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) { + storage_.MemcpyFrom(other.storage_); + } else { + storage_.Initialize(IteratorValueAdapter<const_pointer>(other.data()), + other.size()); + } + } - // Creates an inlined vector by moving in the contents of `other`. + // Creates an inlined vector by moving in the contents of `other` without + // allocating. If `other` contains allocated memory, the newly-created inlined + // vector will take ownership of that memory. However, if `other` does not + // contain allocated memory, the newly-created inlined vector will perform + // element-wise move construction of the contents of `other`. // - // NOTE: This move constructor does not allocate and only moves the underlying - // objects, so its `noexcept` specification depends on whether moving the - // underlying objects can throw or not. We assume: - // a) move constructors should only throw due to allocation failure and + // NOTE: since no allocation is performed for the inlined vector in either + // case, the `noexcept(...)` specification depends on whether moving the + // underlying objects can throw. It is assumed assumed that... + // a) move constructors should only throw due to allocation failure. // b) if `value_type`'s move constructor allocates, it uses the same - // allocation function as the `InlinedVector`'s allocator, so the move - // constructor is non-throwing if the allocator is non-throwing or - // `value_type`'s move constructor is specified as `noexcept`. - InlinedVector(InlinedVector&& v) noexcept( + // allocation function as the inlined vector's allocator. + // Thus, the move constructor is non-throwing if the allocator is non-throwing + // or `value_type`'s move constructor is specified as `noexcept`. + InlinedVector(InlinedVector&& other) noexcept( absl::allocator_is_nothrow<allocator_type>::value || - std::is_nothrow_move_constructible<value_type>::value); + std::is_nothrow_move_constructible<value_type>::value) + : storage_(*other.storage_.GetAllocPtr()) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if (other.storage_.GetIsAllocated()) { + storage_.SetAllocatedData(other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + } else { + IteratorValueAdapter<MoveIterator> other_values( + MoveIterator(other.storage_.GetInlinedData())); + + inlined_vector_internal::ConstructElements( + storage_.GetAllocPtr(), storage_.GetInlinedData(), &other_values, + other.storage_.GetSize()); - // Creates an inlined vector by moving in the contents of `other`. + storage_.SetInlinedSize(other.storage_.GetSize()); + } + } + + // Creates an inlined vector by moving in the contents of `other` with a copy + // of `alloc`. // - // NOTE: This move constructor allocates and subsequently moves the underlying - // objects, so its `noexcept` specification depends on whether the allocation - // can throw and whether moving the underlying objects can throw. Based on the - // same assumptions as above, the `noexcept` specification is dominated by - // whether the allocation can throw regardless of whether `value_type`'s move - // constructor is specified as `noexcept`. - InlinedVector(InlinedVector&& v, const allocator_type& alloc) noexcept( - absl::allocator_is_nothrow<allocator_type>::value); + // NOTE: if `other`'s allocator is not equal to `alloc`, even if `other` + // contains allocated memory, this move constructor will still allocate. Since + // allocation is performed, this constructor can only be `noexcept` if the + // specified allocator is also `noexcept`. + InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept( + absl::allocator_is_nothrow<allocator_type>::value) + : storage_(alloc) { + if (IsMemcpyOk::value) { + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); + } else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) && + other.storage_.GetIsAllocated()) { + storage_.SetAllocatedData(other.storage_.GetAllocatedData(), + other.storage_.GetAllocatedCapacity()); + storage_.SetAllocatedSize(other.storage_.GetSize()); + + other.storage_.SetInlinedSize(0); + } else { + storage_.Initialize( + IteratorValueAdapter<MoveIterator>(MoveIterator(other.data())), + other.size()); + } + } - ~InlinedVector() { clear(); } + ~InlinedVector() {} // --------------------------------------------------------------------------- // InlinedVector Member Accessors @@ -187,87 +249,102 @@ class InlinedVector { // `InlinedVector::empty()` // - // Checks if the inlined vector has no elements. + // Returns whether the inlined vector contains no elements. bool empty() const noexcept { return !size(); } // `InlinedVector::size()` // // Returns the number of elements in the inlined vector. - size_type size() const noexcept { return tag().size(); } + size_type size() const noexcept { return storage_.GetSize(); } // `InlinedVector::max_size()` // - // Returns the maximum number of elements the vector can hold. + // Returns the maximum number of elements the inlined vector can hold. size_type max_size() const noexcept { // One bit of the size storage is used to indicate whether the inlined - // vector is allocated. As a result, the maximum size of the container that - // we can express is half of the max for `size_type`. + // vector contains allocated memory. As a result, the maximum size that the + // inlined vector can express is half of the max for `size_type`. return (std::numeric_limits<size_type>::max)() / 2; } // `InlinedVector::capacity()` // - // Returns the number of elements that can be stored in the inlined vector - // without requiring a reallocation of underlying memory. + // Returns the number of elements that could be stored in the inlined vector + // without requiring a reallocation. // - // NOTE: For most inlined vectors, `capacity()` should equal - // `inlined_capacity()`. For inlined vectors which exceed this capacity, they - // will no longer be inlined and `capacity()` will equal its capacity on the - // allocated heap. + // NOTE: for most inlined vectors, `capacity()` should be equal to the + // template parameter `N`. For inlined vectors which exceed this capacity, + // they will no longer be inlined and `capacity()` will equal the capactity of + // the allocated memory. size_type capacity() const noexcept { - return allocated() ? allocation().capacity() : inlined_capacity(); + return storage_.GetIsAllocated() ? storage_.GetAllocatedCapacity() + : storage_.GetInlinedCapacity(); } // `InlinedVector::data()` // - // Returns a `pointer` to elements of the inlined vector. This pointer can be - // used to access and modify the contained elements. - // Only results within the range [`0`, `size()`) are defined. + // Returns a `pointer` to the elements of the inlined vector. This pointer + // can be used to access and modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. pointer data() noexcept { - return allocated() ? allocated_space() : inlined_space(); + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); } - // Overload of `InlinedVector::data()` to return a `const_pointer` to elements - // of the inlined vector. This pointer can be used to access (but not modify) - // the contained elements. + // Overload of `InlinedVector::data()` that returns a `const_pointer` to the + // elements of the inlined vector. This pointer can be used to access but not + // modify the contained elements. + // + // NOTE: only elements within [`data()`, `data() + size()`) are valid. const_pointer data() const noexcept { - return allocated() ? allocated_space() : inlined_space(); + return storage_.GetIsAllocated() ? storage_.GetAllocatedData() + : storage_.GetInlinedData(); } - // `InlinedVector::operator[]()` + // `InlinedVector::operator[](...)` // - // Returns a `reference` to the `i`th element of the inlined vector using the - // array operator. + // Returns a `reference` to the `i`th element of the inlined vector. reference operator[](size_type i) { assert(i < size()); + return data()[i]; } - // Overload of `InlinedVector::operator[]()` to return a `const_reference` to - // the `i`th element of the inlined vector. + // Overload of `InlinedVector::operator[](...)` that returns a + // `const_reference` to the `i`th element of the inlined vector. const_reference operator[](size_type i) const { assert(i < size()); + return data()[i]; } - // `InlinedVector::at()` + // `InlinedVector::at(...)` // // Returns a `reference` to the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. reference at(size_type i) { if (ABSL_PREDICT_FALSE(i >= size())) { base_internal::ThrowStdOutOfRange( - "InlinedVector::at() failed bounds check"); + "`InlinedVector::at(size_type)` failed bounds check"); } + return data()[i]; } - // Overload of `InlinedVector::at()` to return a `const_reference` to the - // `i`th element of the inlined vector. + // Overload of `InlinedVector::at(...)` that returns a `const_reference` to + // the `i`th element of the inlined vector. + // + // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, + // in both debug and non-debug builds, `std::out_of_range` will be thrown. const_reference at(size_type i) const { if (ABSL_PREDICT_FALSE(i >= size())) { base_internal::ThrowStdOutOfRange( - "InlinedVector::at() failed bounds check"); + "`InlinedVector::at(size_type) const` failed bounds check"); } + return data()[i]; } @@ -276,13 +353,15 @@ class InlinedVector { // Returns a `reference` to the first element of the inlined vector. reference front() { assert(!empty()); + return at(0); } - // Overload of `InlinedVector::front()` returns a `const_reference` to the - // first element of the inlined vector. + // Overload of `InlinedVector::front()` that returns a `const_reference` to + // the first element of the inlined vector. const_reference front() const { assert(!empty()); + return at(0); } @@ -291,13 +370,15 @@ class InlinedVector { // Returns a `reference` to the last element of the inlined vector. reference back() { assert(!empty()); + return at(size() - 1); } - // Overload of `InlinedVector::back()` to return a `const_reference` to the + // Overload of `InlinedVector::back()` that returns a `const_reference` to the // last element of the inlined vector. const_reference back() const { assert(!empty()); + return at(size() - 1); } @@ -306,7 +387,7 @@ class InlinedVector { // Returns an `iterator` to the beginning of the inlined vector. iterator begin() noexcept { return data(); } - // Overload of `InlinedVector::begin()` to return a `const_iterator` to + // Overload of `InlinedVector::begin()` that returns a `const_iterator` to // the beginning of the inlined vector. const_iterator begin() const noexcept { return data(); } @@ -315,7 +396,7 @@ class InlinedVector { // Returns an `iterator` to the end of the inlined vector. iterator end() noexcept { return data() + size(); } - // Overload of `InlinedVector::end()` to return a `const_iterator` to the + // Overload of `InlinedVector::end()` that returns a `const_iterator` to the // end of the inlined vector. const_iterator end() const noexcept { return data() + size(); } @@ -334,7 +415,7 @@ class InlinedVector { // Returns a `reverse_iterator` from the end of the inlined vector. reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - // Overload of `InlinedVector::rbegin()` to return a + // Overload of `InlinedVector::rbegin()` that returns a // `const_reverse_iterator` from the end of the inlined vector. const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); @@ -345,7 +426,7 @@ class InlinedVector { // Returns a `reverse_iterator` from the beginning of the inlined vector. reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - // Overload of `InlinedVector::rend()` to return a `const_reverse_iterator` + // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator` // from the beginning of the inlined vector. const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); @@ -364,1086 +445,403 @@ class InlinedVector { // `InlinedVector::get_allocator()` // - // Returns a copy of the allocator of the inlined vector. - allocator_type get_allocator() const { return allocator(); } + // Returns a copy of the inlined vector's allocator. + allocator_type get_allocator() const { return *storage_.GetAllocPtr(); } // --------------------------------------------------------------------------- // InlinedVector Member Mutators // --------------------------------------------------------------------------- - // `InlinedVector::operator=()` + // `InlinedVector::operator=(...)` // - // Replaces the contents of the inlined vector with copies of the elements in - // the provided `std::initializer_list`. - InlinedVector& operator=(std::initializer_list<value_type> init_list) { - AssignRange(init_list.begin(), init_list.end(), - IteratorCategory<decltype(init_list.begin())>{}); + // Replaces the elements of the inlined vector with copies of the elements of + // `list`. + InlinedVector& operator=(std::initializer_list<value_type> list) { + assign(list.begin(), list.end()); + return *this; } - // Overload of `InlinedVector::operator=()` to replace the contents of the - // inlined vector with the contents of `other`. + // Overload of `InlinedVector::operator=(...)` that replaces the elements of + // the inlined vector with copies of the elements of `other`. InlinedVector& operator=(const InlinedVector& other) { - if (ABSL_PREDICT_FALSE(this == &other)) return *this; - - // Optimized to avoid reallocation. - // Prefer reassignment to copy construction for elements. - if (size() < other.size()) { // grow - reserve(other.size()); - std::copy(other.begin(), other.begin() + size(), begin()); - std::copy(other.begin() + size(), other.end(), std::back_inserter(*this)); - } else { // maybe shrink - erase(begin() + other.size(), end()); - std::copy(other.begin(), other.end(), begin()); + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + const_pointer other_data = other.data(); + assign(other_data, other_data + other.size()); } + return *this; } - // Overload of `InlinedVector::operator=()` to replace the contents of the - // inlined vector with the contents of `other`. + // Overload of `InlinedVector::operator=(...)` that moves the elements of + // `other` into the inlined vector. // - // NOTE: As a result of calling this overload, `other` may be empty or it's - // contents may be left in a moved-from state. + // NOTE: as a result of calling this overload, `other` is left in a valid but + // unspecified state. InlinedVector& operator=(InlinedVector&& other) { - if (ABSL_PREDICT_FALSE(this == &other)) return *this; - - if (other.allocated()) { - clear(); - tag().set_allocated_size(other.size()); - init_allocation(other.allocation()); - other.tag() = Tag(); - } else { - if (allocated()) clear(); - // Both are inlined now. - if (size() < other.size()) { - auto mid = std::make_move_iterator(other.begin() + size()); - std::copy(std::make_move_iterator(other.begin()), mid, begin()); - UninitializedCopy(mid, std::make_move_iterator(other.end()), end()); + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) { + inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), + size()); + storage_.DeallocateIfAllocated(); + storage_.MemcpyFrom(other.storage_); + + other.storage_.SetInlinedSize(0); } else { - auto new_end = std::copy(std::make_move_iterator(other.begin()), - std::make_move_iterator(other.end()), begin()); - Destroy(new_end, end()); + storage_.Assign(IteratorValueAdapter<MoveIterator>( + MoveIterator(other.storage_.GetInlinedData())), + other.size()); } - tag().set_inline_size(other.size()); } + return *this; } - // `InlinedVector::assign()` + // `InlinedVector::assign(...)` // // Replaces the contents of the inlined vector with `n` copies of `v`. void assign(size_type n, const_reference v) { - if (n <= size()) { // Possibly shrink - std::fill_n(begin(), n, v); - erase(begin() + n, end()); - return; - } - // Grow - reserve(n); - std::fill_n(begin(), size(), v); - if (allocated()) { - UninitializedFill(allocated_space() + size(), allocated_space() + n, v); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space() + size(), inlined_space() + n, v); - tag().set_inline_size(n); - } + storage_.Assign(CopyValueAdapter(v), n); + } + + // Overload of `InlinedVector::assign(...)` that replaces the contents of the + // inlined vector with copies of the elements of `list`. + void assign(std::initializer_list<value_type> list) { + assign(list.begin(), list.end()); } - // Overload of `InlinedVector::assign()` to replace the contents of the - // inlined vector with copies of the values in the provided - // `std::initializer_list`. - void assign(std::initializer_list<value_type> init_list) { - AssignRange(init_list.begin(), init_list.end(), - IteratorCategory<decltype(init_list.begin())>{}); + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "forward" category or better. + template <typename ForwardIterator, + EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> + void assign(ForwardIterator first, ForwardIterator last) { + storage_.Assign(IteratorValueAdapter<ForwardIterator>(first), + std::distance(first, last)); } - // Overload of `InlinedVector::assign()` to replace the contents of the - // inlined vector with values constructed from the range [`first`, `last`). - template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr> + // Overload of `InlinedVector::assign(...)` to replace the contents of the + // inlined vector with the range [`first`, `last`). + // + // NOTE: this overload is for iterators that are "input" category. + template <typename InputIterator, + DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> void assign(InputIterator first, InputIterator last) { - AssignRange(first, last, IteratorCategory<InputIterator>{}); + size_type i = 0; + for (; i < size() && first != last; ++i, static_cast<void>(++first)) { + at(i) = *first; + } + + erase(data() + i, data() + size()); + + std::copy(first, last, std::back_inserter(*this)); } - // `InlinedVector::resize()` + // `InlinedVector::resize(...)` + // + // Resizes the inlined vector to contain `n` elements. // - // Resizes the inlined vector to contain `n` elements. If `n` is smaller than - // the inlined vector's current size, extra elements are destroyed. If `n` is - // larger than the initial size, new elements are value-initialized. - void resize(size_type n); + // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are value-initialized. + void resize(size_type n) { storage_.Resize(DefaultValueAdapter(), n); } - // Overload of `InlinedVector::resize()` to resize the inlined vector to - // contain `n` elements where, if `n` is larger than `size()`, the new values - // will be copy-constructed from `v`. - void resize(size_type n, const_reference v); + // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to + // contain `n` elements. + // + // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` + // is larger than `size()`, new elements are copied-constructed from `v`. + void resize(size_type n, const_reference v) { + storage_.Resize(CopyValueAdapter(v), n); + } - // `InlinedVector::insert()` + // `InlinedVector::insert(...)` // - // Copies `v` into `position`, returning an `iterator` pointing to the newly + // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly // inserted element. - iterator insert(const_iterator position, const_reference v) { - return emplace(position, v); + iterator insert(const_iterator pos, const_reference v) { + return emplace(pos, v); } - // Overload of `InlinedVector::insert()` for moving `v` into `position`, - // returning an iterator pointing to the newly inserted element. - iterator insert(const_iterator position, rvalue_reference v) { - return emplace(position, std::move(v)); + // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using + // move semantics, returning an `iterator` to the newly inserted element. + iterator insert(const_iterator pos, rvalue_reference v) { + return emplace(pos, std::move(v)); } - // Overload of `InlinedVector::insert()` for inserting `n` contiguous copies - // of `v` starting at `position`. Returns an `iterator` pointing to the first - // of the newly inserted elements. - iterator insert(const_iterator position, size_type n, const_reference v) { - return InsertWithCount(position, n, v); + // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies + // of `v` starting at `pos`, returning an `iterator` pointing to the first of + // the newly inserted elements. + iterator insert(const_iterator pos, size_type n, const_reference v) { + assert(pos >= begin()); + assert(pos <= end()); + + if (ABSL_PREDICT_TRUE(n != 0)) { + value_type dealias = v; + return storage_.Insert(pos, CopyValueAdapter(dealias), n); + } else { + return const_cast<iterator>(pos); + } } - // Overload of `InlinedVector::insert()` for copying the contents of the - // `std::initializer_list` into the vector starting at `position`. Returns an - // `iterator` pointing to the first of the newly inserted elements. - iterator insert(const_iterator position, - std::initializer_list<value_type> init_list) { - return insert(position, init_list.begin(), init_list.end()); + // Overload of `InlinedVector::insert(...)` that inserts copies of the + // elements of `list` starting at `pos`, returning an `iterator` pointing to + // the first of the newly inserted elements. + iterator insert(const_iterator pos, std::initializer_list<value_type> list) { + return insert(pos, list.begin(), list.end()); + } + + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. + // + // NOTE: this overload is for iterators that are "forward" category or better. + template <typename ForwardIterator, + EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> + iterator insert(const_iterator pos, ForwardIterator first, + ForwardIterator last) { + assert(pos >= begin()); + assert(pos <= end()); + + if (ABSL_PREDICT_TRUE(first != last)) { + return storage_.Insert(pos, IteratorValueAdapter<ForwardIterator>(first), + std::distance(first, last)); + } else { + return const_cast<iterator>(pos); + } } - // Overload of `InlinedVector::insert()` for inserting elements constructed - // from the range [`first`, `last`). Returns an `iterator` pointing to the - // first of the newly inserted elements. + // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, + // `last`) starting at `pos`, returning an `iterator` pointing to the first + // of the newly inserted elements. // - // NOTE: The `enable_if` is intended to disambiguate the two three-argument - // overloads of `insert()`. + // NOTE: this overload is for iterators that are "input" category. template <typename InputIterator, - typename = EnableIfInputIterator<InputIterator>> - iterator insert(const_iterator position, InputIterator first, - InputIterator last) { - return InsertWithRange(position, first, last, - IteratorCategory<InputIterator>()); + DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> + iterator insert(const_iterator pos, InputIterator first, InputIterator last) { + assert(pos >= begin()); + assert(pos <= end()); + + size_type index = std::distance(cbegin(), pos); + for (size_type i = index; first != last; ++i, static_cast<void>(++first)) { + insert(data() + i, *first); + } + + return iterator(data() + index); } - // `InlinedVector::emplace()` + // `InlinedVector::emplace(...)` // - // Constructs and inserts an object in the inlined vector at the given - // `position`, returning an `iterator` pointing to the newly emplaced element. + // Constructs and inserts an element using `args...` in the inlined vector at + // `pos`, returning an `iterator` pointing to the newly emplaced element. template <typename... Args> - iterator emplace(const_iterator position, Args&&... args); + iterator emplace(const_iterator pos, Args&&... args) { + assert(pos >= begin()); + assert(pos <= end()); - // `InlinedVector::emplace_back()` + value_type dealias(std::forward<Args>(args)...); + return storage_.Insert(pos, + IteratorValueAdapter<MoveIterator>( + MoveIterator(std::addressof(dealias))), + 1); + } + + // `InlinedVector::emplace_back(...)` // - // Constructs and appends a new element to the end of the inlined vector, - // returning a `reference` to the emplaced element. + // Constructs and inserts an element using `args...` in the inlined vector at + // `end()`, returning a `reference` to the newly emplaced element. template <typename... Args> reference emplace_back(Args&&... args) { - size_type s = size(); - assert(s <= capacity()); - if (ABSL_PREDICT_FALSE(s == capacity())) { - return GrowAndEmplaceBack(std::forward<Args>(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>(args)...); + return storage_.EmplaceBack(std::forward<Args>(args)...); } - // `InlinedVector::push_back()` + // `InlinedVector::push_back(...)` // - // Appends a copy of `v` to the end of the inlined vector. + // Inserts a copy of `v` in the inlined vector at `end()`. void push_back(const_reference v) { static_cast<void>(emplace_back(v)); } - // Overload of `InlinedVector::push_back()` for moving `v` into a newly - // appended element. + // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` + // using move semantics. void push_back(rvalue_reference v) { static_cast<void>(emplace_back(std::move(v))); } // `InlinedVector::pop_back()` // - // Destroys the element at the end of the inlined vector and shrinks the size - // by `1` (unless the inlined vector is empty, in which case this is a no-op). + // Destroys the element at `back()`, reducing the size by `1`. void pop_back() noexcept { assert(!empty()); - size_type s = size(); - if (allocated()) { - Destroy(allocated_space() + s - 1, allocated_space() + s); - tag().set_allocated_size(s - 1); - } else { - Destroy(inlined_space() + s - 1, inlined_space() + s); - tag().set_inline_size(s - 1); - } + + AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1)); + storage_.SubtractSize(1); } - // `InlinedVector::erase()` + // `InlinedVector::erase(...)` // - // Erases the element at `position` of the inlined vector, returning an - // `iterator` pointing to the first element following the erased element. + // Erases the element at `pos`, returning an `iterator` pointing to where the + // erased element was located. // - // NOTE: May return the end iterator, which is not dereferencable. - iterator erase(const_iterator position) { - assert(position >= begin()); - assert(position < end()); - - iterator pos = const_cast<iterator>(position); - std::move(pos + 1, end(), pos); - pop_back(); - return pos; + // NOTE: may return `end()`, which is not dereferencable. + iterator erase(const_iterator pos) { + assert(pos >= begin()); + assert(pos < end()); + + return storage_.Erase(pos, pos + 1); } - // Overload of `InlinedVector::erase()` for erasing all elements in the - // range [`from`, `to`) in the inlined vector. Returns an `iterator` pointing - // to the first element following the range erased or the end iterator if `to` - // was the end iterator. - iterator erase(const_iterator from, const_iterator to); + // Overload of `InlinedVector::erase(...)` that erases every element in the + // range [`from`, `to`), returning an `iterator` pointing to where the first + // erased element was located. + // + // NOTE: may return `end()`, which is not dereferencable. + iterator erase(const_iterator from, const_iterator to) { + assert(from >= begin()); + assert(from <= to); + assert(to <= end()); + + if (ABSL_PREDICT_TRUE(from != to)) { + return storage_.Erase(from, to); + } else { + return const_cast<iterator>(from); + } + } // `InlinedVector::clear()` // - // Destroys all elements in the inlined vector, sets the size of `0` and - // deallocates the heap allocation if the inlined vector was allocated. + // Destroys all elements in the inlined vector, setting the size to `0` and + // deallocating any held memory. void clear() noexcept { - size_type s = size(); - if (allocated()) { - Destroy(allocated_space(), allocated_space() + s); - allocation().Dealloc(allocator()); - } else if (s != 0) { // do nothing for empty vectors - Destroy(inlined_space(), inlined_space() + s); - } - tag() = Tag(); + inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), + size()); + storage_.DeallocateIfAllocated(); + storage_.SetInlinedSize(0); } - // `InlinedVector::reserve()` + // `InlinedVector::reserve(...)` // - // Enlarges the underlying representation of the inlined vector so it can hold - // at least `n` elements. This method does not change `size()` or the actual - // contents of the vector. - // - // NOTE: If `n` does not exceed `capacity()`, `reserve()` will have no - // effects. Otherwise, `reserve()` will reallocate, performing an n-time - // element-wise move of everything contained. - void reserve(size_type n) { - if (n > capacity()) { - // Make room for new elements - EnlargeBy(n - size()); - } - } + // Ensures that there is enough room for at least `n` elements. + void reserve(size_type n) { storage_.Reserve(n); } // `InlinedVector::shrink_to_fit()` // - // Reduces memory usage by freeing unused memory. After this call, calls to - // `capacity()` will be equal to `(std::max)(inlined_capacity(), size())`. + // Reduces memory usage by freeing unused memory. After being called, calls to + // `capacity()` will be equal to `max(N, size())`. // - // If `size() <= inlined_capacity()` and the elements are currently stored on - // the heap, they will be moved to the inlined storage and the heap memory + // If `size() <= N` and the inlined vector contains allocated memory, the + // elements will all be moved to the inlined space and the allocated memory // will be deallocated. // - // If `size() > inlined_capacity()` and `size() < capacity()` the elements - // will be moved to a smaller heap allocation. + // If `size() > N` and `size() < capacity()`, the elements will be moved to a + // smaller allocation. void shrink_to_fit() { - const auto s = size(); - if (ABSL_PREDICT_FALSE(!allocated() || s == capacity())) return; - - if (s <= inlined_capacity()) { - // Move the elements to the inlined storage. - // We have to do this using a temporary, because `inlined_storage` and - // `allocation_storage` are in a union field. - auto temp = std::move(*this); - assign(std::make_move_iterator(temp.begin()), - std::make_move_iterator(temp.end())); - return; + if (storage_.GetIsAllocated()) { + storage_.ShrinkToFit(); } - - // Reallocate storage and move elements. - // We can't simply use the same approach as above, because `assign()` would - // call into `reserve()` internally and reserve larger capacity than we need - Allocation new_allocation(allocator(), s); - UninitializedCopy(std::make_move_iterator(allocated_space()), - std::make_move_iterator(allocated_space() + s), - new_allocation.buffer()); - ResetAllocation(new_allocation, s); } - // `InlinedVector::swap()` + // `InlinedVector::swap(...)` // - // Swaps the contents of this inlined vector with the contents of `other`. - void swap(InlinedVector& other); - - template <typename Hash> - friend Hash AbslHashValue(Hash hash, const InlinedVector& inlined_vector) { - const_pointer p = inlined_vector.data(); - size_type n = inlined_vector.size(); - return Hash::combine(Hash::combine_contiguous(std::move(hash), p, n), n); - } - - private: - // Holds whether the vector is allocated or not in the lowest bit and the size - // in the high bits: - // `size_ = (size << 1) | is_allocated;` - class Tag { - public: - Tag() : size_(0) {} - size_type size() const { return size_ / 2; } - void add_size(size_type n) { size_ += n * 2; } - void set_inline_size(size_type n) { size_ = n * 2; } - void set_allocated_size(size_type n) { size_ = (n * 2) + 1; } - bool allocated() const { return size_ % 2; } - - private: - size_type size_; - }; - - // Derives from `allocator_type` to use the empty base class optimization. - // If the `allocator_type` is stateless, we can store our instance for free. - class AllocatorAndTag : private allocator_type { - public: - explicit AllocatorAndTag(const allocator_type& a) : allocator_type(a) {} - - Tag& tag() { return tag_; } - const Tag& tag() const { return tag_; } - - allocator_type& allocator() { return *this; } - const allocator_type& allocator() const { return *this; } - - private: - Tag tag_; - }; - - class Allocation { - public: - Allocation(allocator_type& a, size_type capacity) - : capacity_(capacity), buffer_(Create(a, capacity)) {} - - void Dealloc(allocator_type& a) { - std::allocator_traits<allocator_type>::deallocate(a, buffer_, capacity_); - } - - size_type capacity() const { return capacity_; } - - const_pointer buffer() const { return buffer_; } - - pointer buffer() { return buffer_; } - - private: - static pointer Create(allocator_type& a, size_type n) { - return std::allocator_traits<allocator_type>::allocate(a, n); + // Swaps the contents of the inlined vector with `other`. + void swap(InlinedVector& other) { + if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { + storage_.Swap(std::addressof(other.storage_)); } - - size_type capacity_; - pointer buffer_; - }; - - const Tag& tag() const { return allocator_and_tag_.tag(); } - - Tag& tag() { return allocator_and_tag_.tag(); } - - Allocation& allocation() { - return reinterpret_cast<Allocation&>(rep_.allocation_storage.allocation); - } - - const Allocation& allocation() const { - return reinterpret_cast<const Allocation&>( - rep_.allocation_storage.allocation); - } - - void init_allocation(const Allocation& allocation) { - new (&rep_.allocation_storage.allocation) Allocation(allocation); - } - - // TODO(absl-team): investigate whether the reinterpret_cast is appropriate. - pointer inlined_space() { - return reinterpret_cast<pointer>( - std::addressof(rep_.inlined_storage.inlined[0])); - } - - const_pointer inlined_space() const { - return reinterpret_cast<const_pointer>( - std::addressof(rep_.inlined_storage.inlined[0])); - } - - pointer allocated_space() { return allocation().buffer(); } - - const_pointer allocated_space() const { return allocation().buffer(); } - - const allocator_type& allocator() const { - return allocator_and_tag_.allocator(); } - allocator_type& allocator() { return allocator_and_tag_.allocator(); } - - bool allocated() const { return tag().allocated(); } - - // Enlarge the underlying representation so we can store `size_ + delta` elems - // in allocated space. The size is not changed, and any newly added memory is - // not initialized. - void EnlargeBy(size_type delta); - - // Shift all elements from `position` to `end()` by `n` places to the right. - // If the vector needs to be enlarged, memory will be allocated. - // Returns `iterator`s pointing to the start of the previously-initialized - // portion and the start of the uninitialized portion of the created gap. - // The number of initialized spots is `pair.second - pair.first`. The number - // of raw spots is `n - (pair.second - pair.first)`. - // - // Updates the size of the InlinedVector internally. - std::pair<iterator, iterator> ShiftRight(const_iterator position, - size_type n); - - void ResetAllocation(Allocation new_allocation, size_type new_size) { - if (allocated()) { - Destroy(allocated_space(), allocated_space() + size()); - assert(begin() == allocated_space()); - allocation().Dealloc(allocator()); - allocation() = new_allocation; - } else { - Destroy(inlined_space(), inlined_space() + size()); - init_allocation(new_allocation); // bug: only init once - } - tag().set_allocated_size(new_size); - } - - template <typename... Args> - reference GrowAndEmplaceBack(Args&&... args) { - assert(size() == capacity()); - const size_type s = size(); - - Allocation new_allocation(allocator(), 2 * capacity()); - - reference new_element = - Construct(new_allocation.buffer() + s, std::forward<Args>(args)...); - UninitializedCopy(std::make_move_iterator(data()), - std::make_move_iterator(data() + s), - new_allocation.buffer()); - - ResetAllocation(new_allocation, s + 1); - - return new_element; - } - - void InitAssign(size_type n); - - void InitAssign(size_type n, const_reference v); - - template <typename... Args> - reference Construct(pointer p, Args&&... args) { - std::allocator_traits<allocator_type>::construct( - allocator(), p, std::forward<Args>(args)...); - return *p; - } - - template <typename Iterator> - void UninitializedCopy(Iterator src, Iterator src_last, pointer dst) { - for (; src != src_last; ++dst, ++src) Construct(dst, *src); - } - - template <typename... Args> - void UninitializedFill(pointer dst, pointer dst_last, const Args&... args) { - for (; dst != dst_last; ++dst) Construct(dst, args...); - } - - // Destroy [`from`, `to`) in place. - void Destroy(pointer from, pointer to); - - template <typename Iterator> - void AppendRange(Iterator first, Iterator last, std::forward_iterator_tag); - - template <typename Iterator> - void AppendRange(Iterator first, Iterator last, std::input_iterator_tag); - - template <typename Iterator> - void AssignRange(Iterator first, Iterator last, std::forward_iterator_tag); + private: + template <typename H, typename TheT, size_t TheN, typename TheA> + friend H AbslHashValue(H h, const absl::InlinedVector<TheT, TheN, TheA>& a); - template <typename Iterator> - void AssignRange(Iterator first, Iterator last, std::input_iterator_tag); - - iterator InsertWithCount(const_iterator position, size_type n, - const_reference v); - - template <typename ForwardIterator> - iterator InsertWithRange(const_iterator position, ForwardIterator first, - ForwardIterator last, std::forward_iterator_tag); - - template <typename InputIterator> - iterator InsertWithRange(const_iterator position, InputIterator first, - InputIterator last, std::input_iterator_tag); - - // Stores either the inlined or allocated representation - union Rep { - using ValueTypeBuffer = - absl::aligned_storage_t<sizeof(value_type), alignof(value_type)>; - using AllocationBuffer = - absl::aligned_storage_t<sizeof(Allocation), alignof(Allocation)>; - - // Structs wrap the buffers to perform indirection that solves a bizarre - // compilation error on Visual Studio (all known versions). - struct InlinedRep { - ValueTypeBuffer inlined[N]; - }; - struct AllocatedRep { - AllocationBuffer allocation; - }; - - InlinedRep inlined_storage; - AllocatedRep allocation_storage; - }; - - AllocatorAndTag allocator_and_tag_; - Rep rep_; + Storage storage_; }; // ----------------------------------------------------------------------------- // InlinedVector Non-Member Functions // ----------------------------------------------------------------------------- -// `swap()` +// `swap(...)` // -// Swaps the contents of two inlined vectors. This convenience function -// simply calls `InlinedVector::swap()`. +// Swaps the contents of two inlined vectors. template <typename T, size_t N, typename A> -void swap(InlinedVector<T, N, A>& a, - InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) { +void swap(absl::InlinedVector<T, N, A>& a, + absl::InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) { a.swap(b); } -// `operator==()` +// `operator==(...)` // -// Tests the equivalency of the contents of two inlined vectors. +// Tests for value-equality of two inlined vectors. template <typename T, size_t N, typename A> -bool operator==(const InlinedVector<T, N, A>& a, - const InlinedVector<T, N, A>& b) { - return absl::equal(a.begin(), a.end(), b.begin(), b.end()); +bool operator==(const absl::InlinedVector<T, N, A>& a, + const absl::InlinedVector<T, N, A>& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return absl::equal(a_data, a_data + a.size(), b_data, b_data + b.size()); } -// `operator!=()` +// `operator!=(...)` // -// Tests the inequality of the contents of two inlined vectors. +// Tests for value-inequality of two inlined vectors. template <typename T, size_t N, typename A> -bool operator!=(const InlinedVector<T, N, A>& a, - const InlinedVector<T, N, A>& b) { +bool operator!=(const absl::InlinedVector<T, N, A>& a, + const absl::InlinedVector<T, N, A>& b) { return !(a == b); } -// `operator<()` +// `operator<(...)` // -// Tests whether the contents of one inlined vector are less than the contents -// of another through a lexicographical comparison operation. +// Tests whether the value of an inlined vector is less than the value of +// another inlined vector using a lexicographical comparison algorithm. template <typename T, size_t N, typename A> -bool operator<(const InlinedVector<T, N, A>& a, - const InlinedVector<T, N, A>& b) { - return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); +bool operator<(const absl::InlinedVector<T, N, A>& a, + const absl::InlinedVector<T, N, A>& b) { + auto a_data = a.data(); + auto b_data = b.data(); + return std::lexicographical_compare(a_data, a_data + a.size(), b_data, + b_data + b.size()); } -// `operator>()` +// `operator>(...)` // -// Tests whether the contents of one inlined vector are greater than the -// contents of another through a lexicographical comparison operation. +// Tests whether the value of an inlined vector is greater than the value of +// another inlined vector using a lexicographical comparison algorithm. template <typename T, size_t N, typename A> -bool operator>(const InlinedVector<T, N, A>& a, - const InlinedVector<T, N, A>& b) { +bool operator>(const absl::InlinedVector<T, N, A>& a, + const absl::InlinedVector<T, N, A>& b) { return b < a; } -// `operator<=()` +// `operator<=(...)` // -// Tests whether the contents of one inlined vector are less than or equal to -// the contents of another through a lexicographical comparison operation. +// Tests whether the value of an inlined vector is less than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. template <typename T, size_t N, typename A> -bool operator<=(const InlinedVector<T, N, A>& a, - const InlinedVector<T, N, A>& b) { +bool operator<=(const absl::InlinedVector<T, N, A>& a, + const absl::InlinedVector<T, N, A>& b) { return !(b < a); } -// `operator>=()` +// `operator>=(...)` // -// Tests whether the contents of one inlined vector are greater than or equal to -// the contents of another through a lexicographical comparison operation. +// Tests whether the value of an inlined vector is greater than or equal to the +// value of another inlined vector using a lexicographical comparison algorithm. template <typename T, size_t N, typename A> -bool operator>=(const InlinedVector<T, N, A>& a, - const InlinedVector<T, N, A>& b) { +bool operator>=(const absl::InlinedVector<T, N, A>& a, + const absl::InlinedVector<T, N, A>& b) { return !(a < b); } -// ----------------------------------------------------------------------------- -// Implementation of InlinedVector +// `AbslHashValue(...)` // -// Do not depend on any below implementation details! -// ----------------------------------------------------------------------------- - -template <typename T, size_t N, typename A> -InlinedVector<T, N, A>::InlinedVector(const InlinedVector& other) - : allocator_and_tag_(other.allocator()) { - reserve(other.size()); - if (allocated()) { - UninitializedCopy(other.begin(), other.end(), allocated_space()); - tag().set_allocated_size(other.size()); - } else { - UninitializedCopy(other.begin(), other.end(), inlined_space()); - tag().set_inline_size(other.size()); - } -} - -template <typename T, size_t N, typename A> -InlinedVector<T, N, A>::InlinedVector(const InlinedVector& other, - const allocator_type& alloc) - : allocator_and_tag_(alloc) { - reserve(other.size()); - if (allocated()) { - UninitializedCopy(other.begin(), other.end(), allocated_space()); - tag().set_allocated_size(other.size()); - } else { - UninitializedCopy(other.begin(), other.end(), inlined_space()); - tag().set_inline_size(other.size()); - } -} - -template <typename T, size_t N, typename A> -InlinedVector<T, N, A>::InlinedVector(InlinedVector&& other) noexcept( - absl::allocator_is_nothrow<allocator_type>::value || - std::is_nothrow_move_constructible<value_type>::value) - : allocator_and_tag_(other.allocator_and_tag_) { - if (other.allocated()) { - // We can just steal the underlying buffer from the source. - // That leaves the source empty, so we clear its size. - init_allocation(other.allocation()); - other.tag() = Tag(); - } else { - UninitializedCopy( - std::make_move_iterator(other.inlined_space()), - std::make_move_iterator(other.inlined_space() + other.size()), - inlined_space()); - } -} - -template <typename T, size_t N, typename A> -InlinedVector<T, N, A>::InlinedVector(InlinedVector&& other, - const allocator_type& alloc) noexcept( // - absl::allocator_is_nothrow<allocator_type>::value) - : allocator_and_tag_(alloc) { - if (other.allocated()) { - if (alloc == other.allocator()) { - // We can just steal the allocation from the source. - tag() = other.tag(); - init_allocation(other.allocation()); - other.tag() = Tag(); - } else { - // We need to use our own allocator - reserve(other.size()); - UninitializedCopy(std::make_move_iterator(other.begin()), - std::make_move_iterator(other.end()), - allocated_space()); - tag().set_allocated_size(other.size()); - } - } else { - UninitializedCopy( - std::make_move_iterator(other.inlined_space()), - std::make_move_iterator(other.inlined_space() + other.size()), - inlined_space()); - tag().set_inline_size(other.size()); - } -} - -template <typename T, size_t N, typename A> -void InlinedVector<T, N, A>::InitAssign(size_type n, const_reference v) { - if (n > inlined_capacity()) { - Allocation new_allocation(allocator(), n); - init_allocation(new_allocation); - UninitializedFill(allocated_space(), allocated_space() + n, v); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space(), inlined_space() + n, v); - tag().set_inline_size(n); - } -} - -template <typename T, size_t N, typename A> -void InlinedVector<T, N, A>::InitAssign(size_type n) { - if (n > inlined_capacity()) { - Allocation new_allocation(allocator(), n); - init_allocation(new_allocation); - UninitializedFill(allocated_space(), allocated_space() + n); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space(), inlined_space() + n); - tag().set_inline_size(n); - } -} - -template <typename T, size_t N, typename A> -void InlinedVector<T, N, A>::resize(size_type n) { - size_type s = size(); - if (n < s) { - erase(begin() + n, end()); - return; - } - reserve(n); - assert(capacity() >= n); - - // Fill new space with elements constructed in-place. - if (allocated()) { - UninitializedFill(allocated_space() + s, allocated_space() + n); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space() + s, inlined_space() + n); - tag().set_inline_size(n); - } -} - -template <typename T, size_t N, typename A> -void InlinedVector<T, N, A>::resize(size_type n, const_reference v) { - size_type s = size(); - if (n < s) { - erase(begin() + n, end()); - return; - } - reserve(n); - assert(capacity() >= n); - - // Fill new space with copies of 'v'. - if (allocated()) { - UninitializedFill(allocated_space() + s, allocated_space() + n, v); - tag().set_allocated_size(n); - } else { - UninitializedFill(inlined_space() + s, inlined_space() + n, v); - tag().set_inline_size(n); - } -} - -template <typename T, size_t N, typename A> -template <typename... Args> -auto InlinedVector<T, N, A>::emplace(const_iterator position, Args&&... args) - -> iterator { - assert(position >= begin()); - assert(position <= end()); - if (ABSL_PREDICT_FALSE(position == end())) { - emplace_back(std::forward<Args>(args)...); - return end() - 1; - } - - T new_t = T(std::forward<Args>(args)...); - - auto range = ShiftRight(position, 1); - if (range.first == range.second) { - // constructing into uninitialized memory - Construct(range.first, std::move(new_t)); - } else { - // assigning into moved-from object - *range.first = T(std::move(new_t)); - } - - return range.first; -} - -template <typename T, size_t N, typename A> -auto InlinedVector<T, N, A>::erase(const_iterator from, const_iterator to) - -> iterator { - assert(begin() <= from); - assert(from <= to); - assert(to <= end()); - - iterator range_start = const_cast<iterator>(from); - iterator range_end = const_cast<iterator>(to); - - size_type s = size(); - ptrdiff_t erase_gap = std::distance(range_start, range_end); - if (erase_gap > 0) { - pointer space; - if (allocated()) { - space = allocated_space(); - tag().set_allocated_size(s - erase_gap); - } else { - space = inlined_space(); - tag().set_inline_size(s - erase_gap); - } - std::move(range_end, space + s, range_start); - Destroy(space + s - erase_gap, space + s); - } - return range_start; -} - -template <typename T, size_t N, typename A> -void InlinedVector<T, N, A>::swap(InlinedVector& other) { - using std::swap; // Augment ADL with `std::swap`. - if (ABSL_PREDICT_FALSE(this == &other)) return; - - if (allocated() && other.allocated()) { - // Both out of line, so just swap the tag, allocation, and allocator. - swap(tag(), other.tag()); - swap(allocation(), other.allocation()); - swap(allocator(), other.allocator()); - return; - } - if (!allocated() && !other.allocated()) { - // Both inlined: swap up to smaller size, then move remaining elements. - InlinedVector* a = this; - InlinedVector* b = &other; - if (size() < other.size()) { - swap(a, b); - } - - const size_type a_size = a->size(); - const size_type b_size = b->size(); - assert(a_size >= b_size); - // `a` is larger. Swap the elements up to the smaller array size. - std::swap_ranges(a->inlined_space(), a->inlined_space() + b_size, - b->inlined_space()); - - // Move the remaining elements: - // [`b_size`, `a_size`) from `a` -> [`b_size`, `a_size`) from `b` - b->UninitializedCopy(a->inlined_space() + b_size, - a->inlined_space() + a_size, - b->inlined_space() + b_size); - a->Destroy(a->inlined_space() + b_size, a->inlined_space() + a_size); - - swap(a->tag(), b->tag()); - swap(a->allocator(), b->allocator()); - assert(b->size() == a_size); - assert(a->size() == b_size); - return; - } - - // One is out of line, one is inline. - // We first move the elements from the inlined vector into the - // inlined space in the other vector. We then put the other vector's - // pointer/capacity into the originally inlined vector and swap - // the tags. - InlinedVector* a = this; - InlinedVector* b = &other; - if (a->allocated()) { - swap(a, b); - } - assert(!a->allocated()); - assert(b->allocated()); - const size_type a_size = a->size(); - const size_type b_size = b->size(); - // In an optimized build, `b_size` would be unused. - static_cast<void>(b_size); - - // Made Local copies of `size()`, don't need `tag()` accurate anymore - swap(a->tag(), b->tag()); - - // Copy `b_allocation` out before `b`'s union gets clobbered by `inline_space` - Allocation b_allocation = b->allocation(); - - b->UninitializedCopy(a->inlined_space(), a->inlined_space() + a_size, - b->inlined_space()); - a->Destroy(a->inlined_space(), a->inlined_space() + a_size); - - a->allocation() = b_allocation; - - if (a->allocator() != b->allocator()) { - swap(a->allocator(), b->allocator()); - } - - assert(b->size() == a_size); - assert(a->size() == b_size); -} - -template <typename T, size_t N, typename A> -void InlinedVector<T, N, A>::EnlargeBy(size_type delta) { - const size_type s = size(); - assert(s <= capacity()); - - size_type target = std::max(inlined_capacity(), s + delta); - - // Compute new capacity by repeatedly doubling current capacity - // TODO(psrc): Check and avoid overflow? - size_type new_capacity = capacity(); - while (new_capacity < target) { - new_capacity <<= 1; - } - - Allocation new_allocation(allocator(), new_capacity); - - UninitializedCopy(std::make_move_iterator(data()), - std::make_move_iterator(data() + s), - new_allocation.buffer()); - - ResetAllocation(new_allocation, s); -} - -template <typename T, size_t N, typename A> -auto InlinedVector<T, N, A>::ShiftRight(const_iterator position, size_type n) - -> std::pair<iterator, iterator> { - iterator start_used = const_cast<iterator>(position); - iterator start_raw = const_cast<iterator>(position); - size_type s = size(); - size_type required_size = s + n; - - if (required_size > capacity()) { - // Compute new capacity by repeatedly doubling current capacity - size_type new_capacity = capacity(); - while (new_capacity < required_size) { - new_capacity <<= 1; - } - // Move everyone into the new allocation, leaving a gap of `n` for the - // requested shift. - Allocation new_allocation(allocator(), new_capacity); - size_type index = position - begin(); - UninitializedCopy(std::make_move_iterator(data()), - std::make_move_iterator(data() + index), - new_allocation.buffer()); - UninitializedCopy(std::make_move_iterator(data() + index), - std::make_move_iterator(data() + s), - new_allocation.buffer() + index + n); - ResetAllocation(new_allocation, s); - - // New allocation means our iterator is invalid, so we'll recalculate. - // Since the entire gap is in new space, there's no used space to reuse. - start_raw = begin() + index; - start_used = start_raw; - } else { - // If we had enough space, it's a two-part move. Elements going into - // previously-unoccupied space need an `UninitializedCopy()`. Elements - // going into a previously-occupied space are just a `std::move()`. - iterator pos = const_cast<iterator>(position); - iterator raw_space = end(); - size_type slots_in_used_space = raw_space - pos; - size_type new_elements_in_used_space = std::min(n, slots_in_used_space); - size_type new_elements_in_raw_space = n - new_elements_in_used_space; - size_type old_elements_in_used_space = - slots_in_used_space - new_elements_in_used_space; - - UninitializedCopy(std::make_move_iterator(pos + old_elements_in_used_space), - std::make_move_iterator(raw_space), - raw_space + new_elements_in_raw_space); - std::move_backward(pos, pos + old_elements_in_used_space, raw_space); - - // If the gap is entirely in raw space, the used space starts where the raw - // space starts, leaving no elements in used space. If the gap is entirely - // in used space, the raw space starts at the end of the gap, leaving all - // elements accounted for within the used space. - start_used = pos; - start_raw = pos + new_elements_in_used_space; - } - tag().add_size(n); - return std::make_pair(start_used, start_raw); -} - -template <typename T, size_t N, typename A> -void InlinedVector<T, N, A>::Destroy(pointer from, pointer to) { - for (pointer cur = from; cur != to; ++cur) { - std::allocator_traits<allocator_type>::destroy(allocator(), cur); - } -#ifndef NDEBUG - // Overwrite unused memory with `0xab` so we can catch uninitialized usage. - // Cast to `void*` to tell the compiler that we don't care that we might be - // scribbling on a vtable pointer. - if (from != to) { - auto len = sizeof(value_type) * std::distance(from, to); - std::memset(reinterpret_cast<void*>(from), 0xab, len); - } -#endif -} - -template <typename T, size_t N, typename A> -template <typename Iterator> -void InlinedVector<T, N, A>::AppendRange(Iterator first, Iterator last, - std::forward_iterator_tag) { - auto length = std::distance(first, last); - reserve(size() + length); - if (allocated()) { - UninitializedCopy(first, last, allocated_space() + size()); - tag().set_allocated_size(size() + length); - } else { - UninitializedCopy(first, last, inlined_space() + size()); - tag().set_inline_size(size() + length); - } -} - -template <typename T, size_t N, typename A> -template <typename Iterator> -void InlinedVector<T, N, A>::AppendRange(Iterator first, Iterator last, - std::input_iterator_tag) { - std::copy(first, last, std::back_inserter(*this)); -} - -template <typename T, size_t N, typename A> -template <typename Iterator> -void InlinedVector<T, N, A>::AssignRange(Iterator first, Iterator last, - std::forward_iterator_tag) { - auto length = std::distance(first, last); - // Prefer reassignment to copy construction for elements. - if (static_cast<size_type>(length) <= size()) { - erase(std::copy(first, last, begin()), end()); - return; - } - reserve(length); - iterator out = begin(); - for (; out != end(); ++first, ++out) *out = *first; - if (allocated()) { - UninitializedCopy(first, last, out); - tag().set_allocated_size(length); - } else { - UninitializedCopy(first, last, out); - tag().set_inline_size(length); - } -} - -template <typename T, size_t N, typename A> -template <typename Iterator> -void InlinedVector<T, N, A>::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 <typename T, size_t N, typename A> -auto InlinedVector<T, N, A>::InsertWithCount(const_iterator position, - size_type n, const_reference v) - -> iterator { - assert(position >= begin() && position <= end()); - if (ABSL_PREDICT_FALSE(n == 0)) return const_cast<iterator>(position); - - value_type copy = v; - std::pair<iterator, iterator> it_pair = ShiftRight(position, n); - std::fill(it_pair.first, it_pair.second, copy); - UninitializedFill(it_pair.second, it_pair.first + n, copy); - - return it_pair.first; -} - -template <typename T, size_t N, typename A> -template <typename ForwardIterator> -auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position, - ForwardIterator first, - ForwardIterator last, - std::forward_iterator_tag) - -> iterator { - assert(position >= begin() && position <= end()); - if (ABSL_PREDICT_FALSE(first == last)) return const_cast<iterator>(position); - - auto n = std::distance(first, last); - std::pair<iterator, iterator> it_pair = ShiftRight(position, n); - size_type used_spots = it_pair.second - it_pair.first; - ForwardIterator open_spot = std::next(first, used_spots); - std::copy(first, open_spot, it_pair.first); - UninitializedCopy(open_spot, last, it_pair.second); - return it_pair.first; -} - -template <typename T, size_t N, typename A> -template <typename InputIterator> -auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position, - InputIterator first, - InputIterator last, - std::input_iterator_tag) - -> iterator { - assert(position >= begin() && position <= end()); - size_type index = position - cbegin(); - size_type i = index; - while (first != last) insert(begin() + i++, *first++); - return begin() + index; +// Provides `absl::Hash` support for `absl::InlinedVector`. It is uncommon to +// call this directly. +template <typename H, typename T, size_t N, typename A> +H AbslHashValue(H h, const absl::InlinedVector<T, N, A>& a) { + auto size = a.size(); + return H::combine(H::combine_contiguous(std::move(h), a.data(), size), size); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INLINED_VECTOR_H_ diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc index a3ad0f8a..b99bbd62 100644 --- a/absl/container/inlined_vector_benchmark.cc +++ b/absl/container/inlined_vector_benchmark.cc @@ -1,10 +1,10 @@ -// Copyright 2017 The Abseil Authors. +// Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,28 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/container/inlined_vector.h" - +#include <array> #include <string> #include <vector> #include "benchmark/benchmark.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/container/inlined_vector.h" #include "absl/strings/str_cat.h" namespace { -using IntVec = absl::InlinedVector<int, 8>; - void BM_InlinedVectorFill(benchmark::State& state) { - const int len = state.range(0); + absl::InlinedVector<int, 8> v; + int val = 10; for (auto _ : state) { - IntVec v; - for (int i = 0; i < len; i++) { - v.push_back(i); - } + benchmark::DoNotOptimize(v); + v.push_back(val); } - state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len); } BENCHMARK(BM_InlinedVectorFill)->Range(0, 1024); @@ -43,23 +40,25 @@ void BM_InlinedVectorFillRange(benchmark::State& state) { for (int i = 0; i < len; i++) { ia[i] = i; } + auto* from = ia.get(); + auto* to = from + len; for (auto _ : state) { - IntVec v(ia.get(), ia.get() + len); + benchmark::DoNotOptimize(from); + benchmark::DoNotOptimize(to); + absl::InlinedVector<int, 8> v(from, to); benchmark::DoNotOptimize(v); } - state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len); } BENCHMARK(BM_InlinedVectorFillRange)->Range(0, 1024); void BM_StdVectorFill(benchmark::State& state) { - const int len = state.range(0); + std::vector<int> v; + int val = 10; for (auto _ : state) { - std::vector<int> v; - for (int i = 0; i < len; i++) { - v.push_back(i); - } + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(val); + v.push_back(val); } - state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * len); } BENCHMARK(BM_StdVectorFill)->Range(0, 1024); @@ -89,7 +88,7 @@ void BM_InlinedVectorFillString(benchmark::State& state) { const int len = state.range(0); const int no_sso = GetNonShortStringOptimizationSize(); std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'), - std::string(no_sso, 'C'), std::string(no_sso, 'D')}; + std::string(no_sso, 'C'), std::string(no_sso, 'D')}; for (auto _ : state) { absl::InlinedVector<std::string, 8> v; @@ -105,7 +104,7 @@ void BM_StdVectorFillString(benchmark::State& state) { const int len = state.range(0); const int no_sso = GetNonShortStringOptimizationSize(); std::string strings[4] = {std::string(no_sso, 'A'), std::string(no_sso, 'B'), - std::string(no_sso, 'C'), std::string(no_sso, 'D')}; + std::string(no_sso, 'C'), std::string(no_sso, 'D')}; for (auto _ : state) { std::vector<std::string> v; @@ -124,7 +123,7 @@ struct Buffer { // some arbitrary structure for benchmarking. void* user_data; }; -void BM_InlinedVectorTenAssignments(benchmark::State& state) { +void BM_InlinedVectorAssignments(benchmark::State& state) { const int len = state.range(0); using BufferVec = absl::InlinedVector<Buffer, 2>; @@ -133,18 +132,25 @@ void BM_InlinedVectorTenAssignments(benchmark::State& state) { BufferVec dst; for (auto _ : state) { - for (int i = 0; i < 10; ++i) { - dst = src; - } + benchmark::DoNotOptimize(dst); + benchmark::DoNotOptimize(src); + dst = src; } } -BENCHMARK(BM_InlinedVectorTenAssignments) - ->Arg(0)->Arg(1)->Arg(2)->Arg(3)->Arg(4)->Arg(20); +BENCHMARK(BM_InlinedVectorAssignments) + ->Arg(0) + ->Arg(1) + ->Arg(2) + ->Arg(3) + ->Arg(4) + ->Arg(20); void BM_CreateFromContainer(benchmark::State& state) { for (auto _ : state) { - absl::InlinedVector<int, 4> x(absl::InlinedVector<int, 4>{1, 2, 3}); - benchmark::DoNotOptimize(x); + absl::InlinedVector<int, 4> src{1, 2, 3}; + benchmark::DoNotOptimize(src); + absl::InlinedVector<int, 4> dst(std::move(src)); + benchmark::DoNotOptimize(dst); } } BENCHMARK(BM_CreateFromContainer); @@ -159,15 +165,14 @@ struct LargeCopyableOnly { struct LargeCopyableSwappable { LargeCopyableSwappable() : d(1024, 17) {} + LargeCopyableSwappable(const LargeCopyableSwappable& o) = default; - LargeCopyableSwappable(LargeCopyableSwappable&& o) = delete; LargeCopyableSwappable& operator=(LargeCopyableSwappable o) { using std::swap; swap(*this, o); return *this; } - LargeCopyableSwappable& operator=(LargeCopyableSwappable&& o) = delete; friend void swap(LargeCopyableSwappable& a, LargeCopyableSwappable& b) { using std::swap; @@ -215,6 +220,8 @@ void BM_SwapElements(benchmark::State& state) { Vec b; for (auto _ : state) { using std::swap; + benchmark::DoNotOptimize(a); + benchmark::DoNotOptimize(b); swap(a, b); } } @@ -260,60 +267,44 @@ BENCHMARK_TEMPLATE(BM_Sizeof, absl::InlinedVector<std::string, 8>); void BM_InlinedVectorIndexInlined(benchmark::State& state) { absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7}; for (auto _ : state) { - for (int i = 0; i < 1000; ++i) { - benchmark::DoNotOptimize(v); - benchmark::DoNotOptimize(v[4]); - } + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v[4]); } - state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_InlinedVectorIndexInlined); void BM_InlinedVectorIndexExternal(benchmark::State& state) { absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - for (int i = 0; i < 1000; ++i) { - benchmark::DoNotOptimize(v); - benchmark::DoNotOptimize(v[4]); - } + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v[4]); } - state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_InlinedVectorIndexExternal); void BM_StdVectorIndex(benchmark::State& state) { std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - for (int i = 0; i < 1000; ++i) { - benchmark::DoNotOptimize(v); - benchmark::DoNotOptimize(v[4]); - } + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v[4]); } - state.SetItemsProcessed(1000 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_StdVectorIndex); -#define UNROLL_2(x) \ - benchmark::DoNotOptimize(x); \ - benchmark::DoNotOptimize(x); - -#define UNROLL_4(x) UNROLL_2(x) UNROLL_2(x) -#define UNROLL_8(x) UNROLL_4(x) UNROLL_4(x) -#define UNROLL_16(x) UNROLL_8(x) UNROLL_8(x); - void BM_InlinedVectorDataInlined(benchmark::State& state) { absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7}; for (auto _ : state) { - UNROLL_16(v.data()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.data()); } - state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_InlinedVectorDataInlined); void BM_InlinedVectorDataExternal(benchmark::State& state) { absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.data()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.data()); } state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); } @@ -322,7 +313,8 @@ BENCHMARK(BM_InlinedVectorDataExternal); void BM_StdVectorData(benchmark::State& state) { std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.data()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.data()); } state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); } @@ -331,55 +323,482 @@ BENCHMARK(BM_StdVectorData); void BM_InlinedVectorSizeInlined(benchmark::State& state) { absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7}; for (auto _ : state) { - UNROLL_16(v.size()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.size()); } - state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_InlinedVectorSizeInlined); void BM_InlinedVectorSizeExternal(benchmark::State& state) { absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.size()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.size()); } - state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_InlinedVectorSizeExternal); void BM_StdVectorSize(benchmark::State& state) { std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.size()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.size()); } - state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_StdVectorSize); void BM_InlinedVectorEmptyInlined(benchmark::State& state) { absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7}; for (auto _ : state) { - UNROLL_16(v.empty()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.empty()); } - state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_InlinedVectorEmptyInlined); void BM_InlinedVectorEmptyExternal(benchmark::State& state) { absl::InlinedVector<int, 8> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.empty()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.empty()); } - state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_InlinedVectorEmptyExternal); void BM_StdVectorEmpty(benchmark::State& state) { std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (auto _ : state) { - UNROLL_16(v.empty()); + benchmark::DoNotOptimize(v); + benchmark::DoNotOptimize(v.empty()); } - state.SetItemsProcessed(16 * static_cast<int64_t>(state.iterations())); } BENCHMARK(BM_StdVectorEmpty); +constexpr size_t kInlinedCapacity = 4; +constexpr size_t kLargeSize = kInlinedCapacity * 2; +constexpr size_t kSmallSize = kInlinedCapacity / 2; +constexpr size_t kBatchSize = 100; + +#define ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_FunctionTemplate, T) \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize); \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize) + +#define ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_FunctionTemplate, T) \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize, kLargeSize); \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kLargeSize, kSmallSize); \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize, kLargeSize); \ + BENCHMARK_TEMPLATE(BM_FunctionTemplate, T, kSmallSize, kSmallSize) + +template <typename T> +using InlVec = absl::InlinedVector<T, kInlinedCapacity>; + +struct TrivialType { + size_t val; +}; + +class NontrivialType { + public: + ABSL_ATTRIBUTE_NOINLINE NontrivialType() : val_() { + benchmark::DoNotOptimize(*this); + } + + ABSL_ATTRIBUTE_NOINLINE NontrivialType(const NontrivialType& other) + : val_(other.val_) { + benchmark::DoNotOptimize(*this); + } + + ABSL_ATTRIBUTE_NOINLINE NontrivialType& operator=( + const NontrivialType& other) { + val_ = other.val_; + benchmark::DoNotOptimize(*this); + return *this; + } + + ABSL_ATTRIBUTE_NOINLINE ~NontrivialType() noexcept { + benchmark::DoNotOptimize(*this); + } + + private: + size_t val_; +}; + +template <typename T, typename PrepareVecFn, typename TestVecFn> +void BatchedBenchmark(benchmark::State& state, PrepareVecFn prepare_vec, + TestVecFn test_vec) { + std::array<InlVec<T>, kBatchSize> vector_batch{}; + + while (state.KeepRunningBatch(kBatchSize)) { + // Prepare batch + state.PauseTiming(); + for (size_t i = 0; i < kBatchSize; ++i) { + prepare_vec(vector_batch.data() + i, i); + } + benchmark::DoNotOptimize(vector_batch); + state.ResumeTiming(); + + // Test batch + for (size_t i = 0; i < kBatchSize; ++i) { + test_vec(vector_batch.data() + i, i); + } + } +} + +template <typename T, size_t ToSize> +void BM_ConstructFromSize(benchmark::State& state) { + using VecT = InlVec<T>; + auto size = ToSize; + BatchedBenchmark<T>( + state, + /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->~VecT(); }, + /* test_vec = */ + [&](void* ptr, size_t) { + benchmark::DoNotOptimize(size); + ::new (ptr) VecT(size); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSize, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSize, NontrivialType); + +template <typename T, size_t ToSize> +void BM_ConstructFromSizeRef(benchmark::State& state) { + using VecT = InlVec<T>; + auto size = ToSize; + auto ref = T(); + BatchedBenchmark<T>( + state, + /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->~VecT(); }, + /* test_vec = */ + [&](void* ptr, size_t) { + benchmark::DoNotOptimize(size); + benchmark::DoNotOptimize(ref); + ::new (ptr) VecT(size, ref); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSizeRef, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromSizeRef, NontrivialType); + +template <typename T, size_t ToSize> +void BM_ConstructFromRange(benchmark::State& state) { + using VecT = InlVec<T>; + std::array<T, ToSize> arr{}; + BatchedBenchmark<T>( + state, + /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->~VecT(); }, + /* test_vec = */ + [&](void* ptr, size_t) { + benchmark::DoNotOptimize(arr); + ::new (ptr) VecT(arr.begin(), arr.end()); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromRange, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromRange, NontrivialType); + +template <typename T, size_t ToSize> +void BM_ConstructFromCopy(benchmark::State& state) { + using VecT = InlVec<T>; + VecT other_vec(ToSize); + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { vec->~VecT(); }, + /* test_vec = */ + [&](void* ptr, size_t) { + benchmark::DoNotOptimize(other_vec); + ::new (ptr) VecT(other_vec); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromCopy, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromCopy, NontrivialType); + +template <typename T, size_t ToSize> +void BM_ConstructFromMove(benchmark::State& state) { + using VecT = InlVec<T>; + std::array<VecT, kBatchSize> vector_batch{}; + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [&](InlVec<T>* vec, size_t i) { + vector_batch[i].clear(); + vector_batch[i].resize(ToSize); + vec->~VecT(); + }, + /* test_vec = */ + [&](void* ptr, size_t i) { + benchmark::DoNotOptimize(vector_batch[i]); + ::new (ptr) VecT(std::move(vector_batch[i])); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_ConstructFromMove, NontrivialType); + +template <typename T, size_t FromSize, size_t ToSize> +void BM_AssignSizeRef(benchmark::State& state) { + auto size = ToSize; + auto ref = T(); + BatchedBenchmark<T>( + state, + /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->resize(FromSize); }, + /* test_vec = */ + [&](InlVec<T>* vec, size_t) { + benchmark::DoNotOptimize(size); + benchmark::DoNotOptimize(ref); + vec->assign(size, ref); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignSizeRef, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignSizeRef, NontrivialType); + +template <typename T, size_t FromSize, size_t ToSize> +void BM_AssignRange(benchmark::State& state) { + std::array<T, ToSize> arr{}; + BatchedBenchmark<T>( + state, + /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->resize(FromSize); }, + /* test_vec = */ + [&](InlVec<T>* vec, size_t) { + benchmark::DoNotOptimize(arr); + vec->assign(arr.begin(), arr.end()); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignRange, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignRange, NontrivialType); + +template <typename T, size_t FromSize, size_t ToSize> +void BM_AssignFromCopy(benchmark::State& state) { + InlVec<T> other_vec(ToSize); + BatchedBenchmark<T>( + state, + /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->resize(FromSize); }, + /* test_vec = */ + [&](InlVec<T>* vec, size_t) { + benchmark::DoNotOptimize(other_vec); + *vec = other_vec; + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromCopy, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromCopy, NontrivialType); + +template <typename T, size_t FromSize, size_t ToSize> +void BM_AssignFromMove(benchmark::State& state) { + using VecT = InlVec<T>; + std::array<VecT, kBatchSize> vector_batch{}; + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [&](InlVec<T>* vec, size_t i) { + vector_batch[i].clear(); + vector_batch[i].resize(ToSize); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec<T>* vec, size_t i) { + benchmark::DoNotOptimize(vector_batch[i]); + *vec = std::move(vector_batch[i]); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromMove, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromMove, NontrivialType); + +template <typename T, size_t FromSize, size_t ToSize> +void BM_ResizeSize(benchmark::State& state) { + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec<T>* vec, size_t) { vec->resize(ToSize); }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSize, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSize, NontrivialType); + +template <typename T, size_t FromSize, size_t ToSize> +void BM_ResizeSizeRef(benchmark::State& state) { + auto t = T(); + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec<T>* vec, size_t) { + benchmark::DoNotOptimize(t); + vec->resize(ToSize, t); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSizeRef, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSizeRef, NontrivialType); + +template <typename T, size_t FromSize, size_t ToSize> +void BM_InsertSizeRef(benchmark::State& state) { + auto t = T(); + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec<T>* vec, size_t) { + benchmark::DoNotOptimize(t); + auto* pos = vec->data() + (vec->size() / 2); + vec->insert(pos, t); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertSizeRef, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertSizeRef, NontrivialType); + +template <typename T, size_t FromSize, size_t ToSize> +void BM_InsertRange(benchmark::State& state) { + InlVec<T> other_vec(ToSize); + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec<T>* vec, size_t) { + benchmark::DoNotOptimize(other_vec); + auto* pos = vec->data() + (vec->size() / 2); + vec->insert(pos, other_vec.begin(), other_vec.end()); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertRange, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertRange, NontrivialType); + +template <typename T, size_t FromSize> +void BM_EmplaceBack(benchmark::State& state) { + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec<T>* vec, size_t) { vec->emplace_back(); }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EmplaceBack, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EmplaceBack, NontrivialType); + +template <typename T, size_t FromSize> +void BM_PopBack(benchmark::State& state) { + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec<T>* vec, size_t) { vec->pop_back(); }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_PopBack, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_PopBack, NontrivialType); + +template <typename T, size_t FromSize> +void BM_EraseOne(benchmark::State& state) { + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec<T>* vec, size_t) { + auto* pos = vec->data() + (vec->size() / 2); + vec->erase(pos); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseOne, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseOne, NontrivialType); + +template <typename T, size_t FromSize> +void BM_EraseRange(benchmark::State& state) { + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec<T>* vec, size_t) { + auto* pos = vec->data() + (vec->size() / 2); + vec->erase(pos, pos + 1); + }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseRange, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseRange, NontrivialType); + +template <typename T, size_t FromSize> +void BM_Clear(benchmark::State& state) { + BatchedBenchmark<T>( + state, + /* prepare_vec = */ [](InlVec<T>* vec, size_t) { vec->resize(FromSize); }, + /* test_vec = */ [](InlVec<T>* vec, size_t) { vec->clear(); }); +} +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, TrivialType); +ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, NontrivialType); + +template <typename T, size_t FromSize, size_t ToCapacity> +void BM_Reserve(benchmark::State& state) { + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(FromSize); + }, + /* test_vec = */ + [](InlVec<T>* vec, size_t) { vec->reserve(ToCapacity); }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Reserve, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Reserve, NontrivialType); + +template <typename T, size_t FromCapacity, size_t ToCapacity> +void BM_ShrinkToFit(benchmark::State& state) { + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [](InlVec<T>* vec, size_t) { + vec->clear(); + vec->resize(ToCapacity); + vec->reserve(FromCapacity); + }, + /* test_vec = */ [](InlVec<T>* vec, size_t) { vec->shrink_to_fit(); }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ShrinkToFit, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ShrinkToFit, NontrivialType); + +template <typename T, size_t FromSize, size_t ToSize> +void BM_Swap(benchmark::State& state) { + using VecT = InlVec<T>; + std::array<VecT, kBatchSize> vector_batch{}; + BatchedBenchmark<T>( + state, + /* prepare_vec = */ + [&](InlVec<T>* vec, size_t i) { + vector_batch[i].clear(); + vector_batch[i].resize(ToSize); + vec->resize(FromSize); + }, + /* test_vec = */ + [&](InlVec<T>* vec, size_t i) { + using std::swap; + benchmark::DoNotOptimize(vector_batch[i]); + swap(*vec, vector_batch[i]); + }); +} +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Swap, TrivialType); +ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Swap, NontrivialType); + } // namespace diff --git a/absl/container/inlined_vector_exception_safety_test.cc b/absl/container/inlined_vector_exception_safety_test.cc new file mode 100644 index 00000000..ff0da75b --- /dev/null +++ b/absl/container/inlined_vector_exception_safety_test.cc @@ -0,0 +1,489 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <array> +#include <initializer_list> +#include <iterator> +#include <memory> +#include <utility> + +#include "gtest/gtest.h" +#include "absl/base/internal/exception_safety_testing.h" +#include "absl/container/inlined_vector.h" + +namespace { + +constexpr size_t kInlinedCapacity = 4; +constexpr size_t kLargeSize = kInlinedCapacity * 2; +constexpr size_t kSmallSize = kInlinedCapacity / 2; + +using Thrower = testing::ThrowingValue<>; +using MovableThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>; +using ThrowAlloc = testing::ThrowingAllocator<Thrower>; + +using ThrowerVec = absl::InlinedVector<Thrower, kInlinedCapacity>; +using MovableThrowerVec = absl::InlinedVector<MovableThrower, kInlinedCapacity>; + +using ThrowAllocThrowerVec = + absl::InlinedVector<Thrower, kInlinedCapacity, ThrowAlloc>; +using ThrowAllocMovableThrowerVec = + absl::InlinedVector<MovableThrower, kInlinedCapacity, ThrowAlloc>; + +// In GCC, if an element of a `std::initializer_list` throws during construction +// the elements that were constructed before it are not destroyed. This causes +// incorrect exception safety test failures. Thus, `testing::nothrow_ctor` is +// required. See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66139 +#define ABSL_INTERNAL_MAKE_INIT_LIST(T, N) \ + (N > kInlinedCapacity \ + ? std::initializer_list<T>{T(0, testing::nothrow_ctor), \ + T(1, testing::nothrow_ctor), \ + T(2, testing::nothrow_ctor), \ + T(3, testing::nothrow_ctor), \ + T(4, testing::nothrow_ctor), \ + T(5, testing::nothrow_ctor), \ + T(6, testing::nothrow_ctor), \ + T(7, testing::nothrow_ctor)} \ + \ + : std::initializer_list<T>{T(0, testing::nothrow_ctor), \ + T(1, testing::nothrow_ctor)}) +static_assert((kLargeSize == 8 || kSmallSize == 2), + "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)."); + +template <typename TheVecT, size_t... TheSizes> +class TestParams { + public: + using VecT = TheVecT; + constexpr static size_t GetSizeAt(size_t i) { return kSizes[1 + i]; } + + private: + constexpr static size_t kSizes[1 + sizeof...(TheSizes)] = {1, TheSizes...}; +}; + +using NoSizeTestParams = + ::testing::Types<TestParams<ThrowerVec>, TestParams<MovableThrowerVec>, + TestParams<ThrowAllocThrowerVec>, + TestParams<ThrowAllocMovableThrowerVec>>; + +using OneSizeTestParams = + ::testing::Types<TestParams<ThrowerVec, kLargeSize>, + TestParams<ThrowerVec, kSmallSize>, + TestParams<MovableThrowerVec, kLargeSize>, + TestParams<MovableThrowerVec, kSmallSize>, + TestParams<ThrowAllocThrowerVec, kLargeSize>, + TestParams<ThrowAllocThrowerVec, kSmallSize>, + TestParams<ThrowAllocMovableThrowerVec, kLargeSize>, + TestParams<ThrowAllocMovableThrowerVec, kSmallSize>>; + +using TwoSizeTestParams = ::testing::Types< + TestParams<ThrowerVec, kLargeSize, kLargeSize>, + TestParams<ThrowerVec, kLargeSize, kSmallSize>, + TestParams<ThrowerVec, kSmallSize, kLargeSize>, + TestParams<ThrowerVec, kSmallSize, kSmallSize>, + TestParams<MovableThrowerVec, kLargeSize, kLargeSize>, + TestParams<MovableThrowerVec, kLargeSize, kSmallSize>, + TestParams<MovableThrowerVec, kSmallSize, kLargeSize>, + TestParams<MovableThrowerVec, kSmallSize, kSmallSize>, + TestParams<ThrowAllocThrowerVec, kLargeSize, kLargeSize>, + TestParams<ThrowAllocThrowerVec, kLargeSize, kSmallSize>, + TestParams<ThrowAllocThrowerVec, kSmallSize, kLargeSize>, + TestParams<ThrowAllocThrowerVec, kSmallSize, kSmallSize>, + TestParams<ThrowAllocMovableThrowerVec, kLargeSize, kLargeSize>, + TestParams<ThrowAllocMovableThrowerVec, kLargeSize, kSmallSize>, + TestParams<ThrowAllocMovableThrowerVec, kSmallSize, kLargeSize>, + TestParams<ThrowAllocMovableThrowerVec, kSmallSize, kSmallSize>>; + +template <typename> +struct NoSizeTest : ::testing::Test {}; +TYPED_TEST_SUITE(NoSizeTest, NoSizeTestParams); + +template <typename> +struct OneSizeTest : ::testing::Test {}; +TYPED_TEST_SUITE(OneSizeTest, OneSizeTestParams); + +template <typename> +struct TwoSizeTest : ::testing::Test {}; +TYPED_TEST_SUITE(TwoSizeTest, TwoSizeTestParams); + +template <typename VecT> +bool InlinedVectorInvariants(VecT* vec) { + if (*vec != *vec) return false; + if (vec->size() > vec->capacity()) return false; + if (vec->size() > vec->max_size()) return false; + if (vec->capacity() > vec->max_size()) return false; + if (vec->data() != std::addressof(vec->at(0))) return false; + if (vec->data() != vec->begin()) return false; + if (*vec->data() != *vec->begin()) return false; + if (vec->begin() > vec->end()) return false; + if ((vec->end() - vec->begin()) != vec->size()) return false; + if (std::distance(vec->begin(), vec->end()) != vec->size()) return false; + return true; +} + +// Function that always returns false is correct, but refactoring is required +// for clarity. It's needed to express that, as a contract, certain operations +// should not throw at all. Execution of this function means an exception was +// thrown and thus the test should fail. +// TODO(johnsoncj): Add `testing::NoThrowGuarantee` to the framework +template <typename VecT> +bool NoThrowGuarantee(VecT* /* vec */) { + return false; +} + +TYPED_TEST(NoSizeTest, DefaultConstructor) { + using VecT = typename TypeParam::VecT; + using allocator_type = typename VecT::allocator_type; + + testing::TestThrowingCtor<VecT>(); + + testing::TestThrowingCtor<VecT>(allocator_type{}); +} + +TYPED_TEST(OneSizeTest, SizeConstructor) { + using VecT = typename TypeParam::VecT; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + testing::TestThrowingCtor<VecT>(size); + + testing::TestThrowingCtor<VecT>(size, allocator_type{}); +} + +TYPED_TEST(OneSizeTest, SizeRefConstructor) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + testing::TestThrowingCtor<VecT>(size, value_type{}); + + testing::TestThrowingCtor<VecT>(size, value_type{}, allocator_type{}); +} + +TYPED_TEST(OneSizeTest, InitializerListConstructor) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + testing::TestThrowingCtor<VecT>( + ABSL_INTERNAL_MAKE_INIT_LIST(value_type, size)); + + testing::TestThrowingCtor<VecT>( + ABSL_INTERNAL_MAKE_INIT_LIST(value_type, size), allocator_type{}); +} + +TYPED_TEST(OneSizeTest, RangeConstructor) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + std::array<value_type, size> arr{}; + + testing::TestThrowingCtor<VecT>(arr.begin(), arr.end()); + + testing::TestThrowingCtor<VecT>(arr.begin(), arr.end(), allocator_type{}); +} + +TYPED_TEST(OneSizeTest, CopyConstructor) { + using VecT = typename TypeParam::VecT; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + VecT other_vec{size}; + + testing::TestThrowingCtor<VecT>(other_vec); + + testing::TestThrowingCtor<VecT>(other_vec, allocator_type{}); +} + +TYPED_TEST(OneSizeTest, MoveConstructor) { + using VecT = typename TypeParam::VecT; + using allocator_type = typename VecT::allocator_type; + constexpr static auto size = TypeParam::GetSizeAt(0); + + if (!absl::allocator_is_nothrow<allocator_type>::value) { + testing::TestThrowingCtor<VecT>(VecT{size}); + + testing::TestThrowingCtor<VecT>(VecT{size}, allocator_type{}); + } +} + +TYPED_TEST(TwoSizeTest, Assign) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto to_size = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants<VecT>); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + *vec = ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + VecT other_vec{to_size}; + *vec = other_vec; + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + VecT other_vec{to_size}; + *vec = std::move(other_vec); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + value_type val{}; + vec->assign(to_size, val); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->assign(ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size)); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + std::array<value_type, to_size> arr{}; + vec->assign(arr.begin(), arr.end()); + })); +} + +TYPED_TEST(TwoSizeTest, Resize) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto to_size = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants<VecT>, + testing::strong_guarantee); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->resize(to_size); // + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->resize(to_size, value_type{}); // + })); +} + +TYPED_TEST(OneSizeTest, Insert) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants<VecT>); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->insert(it, value_type{}); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->insert(it, value_type{}); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->end(); + vec->insert(it, value_type{}); + })); +} + +TYPED_TEST(TwoSizeTest, Insert) { + using VecT = typename TypeParam::VecT; + using value_type = typename VecT::value_type; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto count = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants<VecT>); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->insert(it, count, value_type{}); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->insert(it, count, value_type{}); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->end(); + vec->insert(it, count, value_type{}); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count)); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count)); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->end(); + vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count)); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + std::array<value_type, count> arr{}; + vec->insert(it, arr.begin(), arr.end()); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + std::array<value_type, count> arr{}; + vec->insert(it, arr.begin(), arr.end()); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->end(); + std::array<value_type, count> arr{}; + vec->insert(it, arr.begin(), arr.end()); + })); +} + +TYPED_TEST(OneSizeTest, EmplaceBack) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + VecT full_vec{size}; + full_vec.resize(full_vec.capacity()); + + VecT nonfull_vec{size}; + nonfull_vec.reserve(size + 1); + + auto tester = testing::MakeExceptionSafetyTester().WithContracts( + InlinedVectorInvariants<VecT>); + + EXPECT_TRUE(tester.WithInitialValue(nonfull_vec).Test([](VecT* vec) { + vec->emplace_back(); // + })); + + EXPECT_TRUE(tester.WithInitialValue(full_vec).Test([](VecT* vec) { + vec->emplace_back(); // + })); +} + +TYPED_TEST(OneSizeTest, PopBack) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{size}) + .WithContracts(NoThrowGuarantee<VecT>); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->pop_back(); // + })); +} + +TYPED_TEST(OneSizeTest, Erase) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{size}) + .WithContracts(InlinedVectorInvariants<VecT>); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->erase(it); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->erase(it); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() - 1); + vec->erase(it); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->erase(it, it + 1); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->erase(it, it + 1); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() - 1); + vec->erase(it, it + 1); + })); +} + +TYPED_TEST(OneSizeTest, Clear) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{size}) + .WithContracts(NoThrowGuarantee<VecT>); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->clear(); // + })); +} + +TYPED_TEST(TwoSizeTest, Reserve) { + using VecT = typename TypeParam::VecT; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto to_capacity = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants<VecT>); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->reserve(to_capacity); // + })); +} + +TYPED_TEST(OneSizeTest, ShrinkToFit) { + using VecT = typename TypeParam::VecT; + constexpr static auto size = TypeParam::GetSizeAt(0); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{size}) + .WithContracts(InlinedVectorInvariants<VecT>); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + vec->shrink_to_fit(); // + })); +} + +TYPED_TEST(TwoSizeTest, Swap) { + using VecT = typename TypeParam::VecT; + constexpr static auto from_size = TypeParam::GetSizeAt(0); + constexpr static auto to_size = TypeParam::GetSizeAt(1); + + auto tester = testing::MakeExceptionSafetyTester() + .WithInitialValue(VecT{from_size}) + .WithContracts(InlinedVectorInvariants<VecT>); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + VecT other_vec{to_size}; + vec->swap(other_vec); + })); + + EXPECT_TRUE(tester.Test([](VecT* vec) { + using std::swap; + VecT other_vec{to_size}; + swap(*vec, other_vec); + })); +} + +} // namespace diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index 3a1ea8ac..bada4fec 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc @@ -1,10 +1,10 @@ -// Copyright 2017 The Abseil Authors. +// Copyright 2019 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,6 +30,7 @@ #include "absl/base/internal/exception_testing.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" +#include "absl/container/internal/counting_allocator.h" #include "absl/container/internal/test_instance_tracker.h" #include "absl/hash/hash_testing.h" #include "absl/memory/memory.h" @@ -37,6 +38,7 @@ namespace { +using absl::container_internal::CountingAllocator; using absl::test_internal::CopyableMovableInstance; using absl::test_internal::CopyableOnlyInstance; using absl::test_internal::InstanceTracker; @@ -68,18 +70,15 @@ MATCHER_P(ValueIs, e, "") { // test_instance_tracker.h. template <typename T> class InstanceTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(InstanceTest); +TYPED_TEST_SUITE_P(InstanceTest); // A simple reference counted class to make sure that the proper elements are // destroyed in the erase(begin, end) test. class RefCounted { public: - RefCounted(int value, int* count) : value_(value), count_(count) { - Ref(); - } + RefCounted(int value, int* count) : value_(value), count_(count) { Ref(); } - RefCounted(const RefCounted& v) - : value_(v.value_), count_(v.count_) { + RefCounted(const RefCounted& v) : value_(v.value_), count_(v.count_) { Ref(); } @@ -138,57 +137,6 @@ static IntVec Fill(int len, int offset = 0) { return v; } -// This is a stateful allocator, but the state lives outside of the -// allocator (in whatever test is using the allocator). This is odd -// but helps in tests where the allocator is propagated into nested -// containers - that chain of allocators uses the same state and is -// thus easier to query for aggregate allocation information. -template <typename T> -class CountingAllocator : public std::allocator<T> { - public: - using Alloc = std::allocator<T>; - using pointer = typename Alloc::pointer; - using size_type = typename Alloc::size_type; - - CountingAllocator() : bytes_used_(nullptr) {} - explicit CountingAllocator(int64_t* b) : bytes_used_(b) {} - - template <typename U> - CountingAllocator(const CountingAllocator<U>& x) - : Alloc(x), bytes_used_(x.bytes_used_) {} - - pointer allocate(size_type n, - std::allocator<void>::const_pointer hint = nullptr) { - assert(bytes_used_ != nullptr); - *bytes_used_ += n * sizeof(T); - return Alloc::allocate(n, hint); - } - - void deallocate(pointer p, size_type n) { - Alloc::deallocate(p, n); - assert(bytes_used_ != nullptr); - *bytes_used_ -= n * sizeof(T); - } - - template<typename U> - class rebind { - public: - using other = CountingAllocator<U>; - }; - - friend bool operator==(const CountingAllocator& a, - const CountingAllocator& b) { - return a.bytes_used_ == b.bytes_used_; - } - - friend bool operator!=(const CountingAllocator& a, - const CountingAllocator& b) { - return !(a == b); - } - - int64_t* bytes_used_; -}; - TEST(IntVec, SimpleOps) { for (int len = 0; len < 20; len++) { IntVec v; @@ -239,6 +187,12 @@ TEST(IntVec, SimpleOps) { } } +TEST(IntVec, PopBackNoOverflow) { + IntVec v = {1}; + v.pop_back(); + EXPECT_EQ(v.size(), 0); +} + TEST(IntVec, AtThrows) { IntVec v = {1, 2, 3}; EXPECT_EQ(v.at(2), 3); @@ -333,7 +287,7 @@ TEST(RefCountedVec, EraseBeginEnd) { } // Check that the elements at the end are preserved. - for (int i = erase_end; i< len; ++i) { + for (int i = erase_end; i < len; ++i) { EXPECT_EQ(1, counts[i]); } } @@ -595,10 +549,10 @@ TEST(IntVec, Resize) { static const int kResizeElem = 1000000; for (int k = 0; k < 10; k++) { // Enlarging resize - v.resize(len+k, kResizeElem); - EXPECT_EQ(len+k, v.size()); - EXPECT_LE(len+k, v.capacity()); - for (int i = 0; i < len+k; i++) { + v.resize(len + k, kResizeElem); + EXPECT_EQ(len + k, v.size()); + EXPECT_LE(len + k, v.capacity()); + for (int i = 0; i < len + k; i++) { if (i < len) { EXPECT_EQ(i, v[i]); } else { @@ -909,7 +863,7 @@ TYPED_TEST_P(InstanceTest, Swap) { auto min_len = std::min(l1, l2); auto max_len = std::max(l1, l2); for (int i = 0; i < l1; i++) a.push_back(Instance(i)); - for (int i = 0; i < l2; i++) b.push_back(Instance(100+i)); + for (int i = 0; i < l2; i++) b.push_back(Instance(100 + i)); EXPECT_EQ(tracker.instances(), l1 + l2); tracker.ResetCopiesMovesSwaps(); { @@ -977,7 +931,7 @@ TEST(IntVec, EqualAndNotEqual) { EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); - b[i] = b[i] - 1; // Back to before + b[i] = b[i] - 1; // Back to before EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); } @@ -1044,7 +998,7 @@ TYPED_TEST_P(InstanceTest, CountConstructorsDestructors) { // reserve() must not increase the number of initialized objects SCOPED_TRACE("reserve"); - v.reserve(len+1000); + v.reserve(len + 1000); EXPECT_EQ(tracker.instances(), len); EXPECT_EQ(tracker.copies() + tracker.moves(), len); @@ -1290,9 +1244,8 @@ void InstanceCountElemAssignWithAllocationTest() { absl::InlinedVector<Instance, 2> v(original_contents.begin(), original_contents.end()); v.assign(3, Instance(123)); - EXPECT_THAT(v, - AllOf(SizeIs(3), - ElementsAre(ValueIs(123), ValueIs(123), ValueIs(123)))); + EXPECT_THAT(v, AllOf(SizeIs(3), ElementsAre(ValueIs(123), ValueIs(123), + ValueIs(123)))); EXPECT_LE(v.size(), v.capacity()); } } @@ -1571,8 +1524,8 @@ TYPED_TEST_P(InstanceTest, InitializerListAssign) { SCOPED_TRACE(original_size); absl::InlinedVector<Instance, 2> v(original_size, Instance(12345)); v.assign({Instance(3), Instance(4), Instance(5)}); - EXPECT_THAT(v, AllOf(SizeIs(3), - ElementsAre(ValueIs(3), ValueIs(4), ValueIs(5)))); + EXPECT_THAT( + v, AllOf(SizeIs(3), ElementsAre(ValueIs(3), ValueIs(4), ValueIs(5)))); EXPECT_LE(3, v.capacity()); } } @@ -1597,7 +1550,7 @@ TEST(DynamicVec, DynamicVecCompiles) { TEST(AllocatorSupportTest, Constructors) { using MyAlloc = CountingAllocator<int>; using AllocVec = absl::InlinedVector<int, 4, MyAlloc>; - const int ia[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7}; int64_t allocated = 0; MyAlloc alloc(&allocated); { AllocVec ABSL_ATTRIBUTE_UNUSED v; } @@ -1613,7 +1566,7 @@ TEST(AllocatorSupportTest, Constructors) { TEST(AllocatorSupportTest, CountAllocations) { using MyAlloc = CountingAllocator<int>; using AllocVec = absl::InlinedVector<int, 4, MyAlloc>; - const int ia[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7}; int64_t allocated = 0; MyAlloc alloc(&allocated); { @@ -1677,8 +1630,8 @@ TEST(AllocatorSupportTest, SwapBothAllocated) { int64_t allocated1 = 0; int64_t allocated2 = 0; { - const int ia1[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; - const int ia2[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + const int ia1[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const int ia2[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; MyAlloc a1(&allocated1); MyAlloc a2(&allocated2); AllocVec v1(ia1, ia1 + ABSL_ARRAYSIZE(ia1), a1); @@ -1702,8 +1655,8 @@ TEST(AllocatorSupportTest, SwapOneAllocated) { int64_t allocated1 = 0; int64_t allocated2 = 0; { - const int ia1[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; - const int ia2[] = { 0, 1, 2, 3 }; + const int ia1[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const int ia2[] = {0, 1, 2, 3}; MyAlloc a1(&allocated1); MyAlloc a2(&allocated2); AllocVec v1(ia1, ia1 + ABSL_ARRAYSIZE(ia1), a1); @@ -1722,67 +1675,53 @@ TEST(AllocatorSupportTest, SwapOneAllocated) { EXPECT_THAT(allocated2, 0); } -TEST(AllocatorSupportTest, ScopedAllocatorWorks) { +TEST(AllocatorSupportTest, ScopedAllocatorWorksInlined) { using StdVector = std::vector<int, CountingAllocator<int>>; - using MyAlloc = - std::scoped_allocator_adaptor<CountingAllocator<StdVector>>; - using AllocVec = absl::InlinedVector<StdVector, 4, MyAlloc>; - - // MSVC 2017's std::vector allocates different amounts of memory in debug - // versus opt mode. - int64_t test_allocated = 0; - StdVector v(CountingAllocator<int>{&test_allocated}); - // The amount of memory allocated by a default constructed vector<int> - auto default_std_vec_allocated = test_allocated; - v.push_back(1); - // The amound of memory allocated by a copy-constructed vector<int> with one - // element. - int64_t one_element_std_vec_copy_allocated = test_allocated; + using Alloc = CountingAllocator<StdVector>; + using ScopedAlloc = std::scoped_allocator_adaptor<Alloc>; + using AllocVec = absl::InlinedVector<StdVector, 1, ScopedAlloc>; - int64_t allocated = 0; - AllocVec vec(MyAlloc{CountingAllocator<StdVector>{&allocated}}); - EXPECT_EQ(allocated, 0); + int64_t total_allocated_byte_count = 0; - // This default constructs a vector<int>, but the allocator should pass itself - // into the vector<int>, so check allocation compared to that. - // The absl::InlinedVector does not allocate any memory. - // The vector<int> may allocate any memory. - auto expected = default_std_vec_allocated; - vec.resize(1); - EXPECT_EQ(allocated, expected); - - // We make vector<int> allocate memory. - // It must go through the allocator even though we didn't construct the - // vector directly. This assumes that vec[0] doesn't need to grow its - // allocation. - expected += sizeof(int); - vec[0].push_back(1); - EXPECT_EQ(allocated, expected); - - // Another allocating vector. - expected += one_element_std_vec_copy_allocated; - vec.push_back(vec[0]); - EXPECT_EQ(allocated, expected); - - // Overflow the inlined memory. - // The absl::InlinedVector will now allocate. - expected += sizeof(StdVector) * 8 + default_std_vec_allocated * 3; - vec.resize(5); - EXPECT_EQ(allocated, expected); - - // Adding one more in external mode should also work. - expected += one_element_std_vec_copy_allocated; - vec.push_back(vec[0]); - EXPECT_EQ(allocated, expected); - - // And extending these should still work. This assumes that vec[0] does not - // need to grow its allocation. - expected += sizeof(int); - vec[0].push_back(1); - EXPECT_EQ(allocated, expected); - - vec.clear(); - EXPECT_EQ(allocated, 0); + AllocVec inlined_case(ScopedAlloc(Alloc(+&total_allocated_byte_count))); + + // Called only once to remain inlined + inlined_case.emplace_back(); + + int64_t absl_responsible_for_count = total_allocated_byte_count; + EXPECT_EQ(absl_responsible_for_count, 0); + + inlined_case[0].emplace_back(); + EXPECT_GT(total_allocated_byte_count, absl_responsible_for_count); + + inlined_case.clear(); + inlined_case.shrink_to_fit(); + EXPECT_EQ(total_allocated_byte_count, 0); +} + +TEST(AllocatorSupportTest, ScopedAllocatorWorksAllocated) { + using StdVector = std::vector<int, CountingAllocator<int>>; + using Alloc = CountingAllocator<StdVector>; + using ScopedAlloc = std::scoped_allocator_adaptor<Alloc>; + using AllocVec = absl::InlinedVector<StdVector, 1, ScopedAlloc>; + + int64_t total_allocated_byte_count = 0; + + AllocVec allocated_case(ScopedAlloc(Alloc(+&total_allocated_byte_count))); + + // Called twice to force into being allocated + allocated_case.emplace_back(); + allocated_case.emplace_back(); + + int64_t absl_responsible_for_count = total_allocated_byte_count; + EXPECT_GT(absl_responsible_for_count, 0); + + allocated_case[1].emplace_back(); + EXPECT_GT(total_allocated_byte_count, absl_responsible_for_count); + + allocated_case.clear(); + allocated_case.shrink_to_fit(); + EXPECT_EQ(total_allocated_byte_count, 0); } TEST(AllocatorSupportTest, SizeAllocConstructor) { @@ -1811,4 +1750,23 @@ TEST(AllocatorSupportTest, SizeAllocConstructor) { } } +TEST(InlinedVectorTest, AbslHashValueWorks) { + using V = absl::InlinedVector<int, 4>; + std::vector<V> cases; + + // Generate a variety of vectors some of these are small enough for the inline + // space but are stored out of line. + for (int i = 0; i < 10; ++i) { + V v; + for (int j = 0; j < i; ++j) { + v.push_back(j); + } + cases.push_back(v); + v.resize(i % 4); + cases.push_back(v); + } + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases)); +} + } // anonymous namespace diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h new file mode 100644 index 00000000..a02cd5c3 --- /dev/null +++ b/absl/container/internal/common.h @@ -0,0 +1,198 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_H_ +#define ABSL_CONTAINER_INTERNAL_CONTAINER_H_ + +#include <cassert> +#include <type_traits> + +#include "absl/meta/type_traits.h" +#include "absl/types/optional.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +template <class, class = void> +struct IsTransparent : std::false_type {}; +template <class T> +struct IsTransparent<T, absl::void_t<typename T::is_transparent>> + : std::true_type {}; + +template <bool is_transparent> +struct KeyArg { + // Transparent. Forward `K`. + template <typename K, typename key_type> + using type = K; +}; + +template <> +struct KeyArg<false> { + // Not transparent. Always use `key_type`. + template <typename K, typename key_type> + using type = key_type; +}; + +// The node_handle concept from C++17. +// We specialize node_handle for sets and maps. node_handle_base holds the +// common API of both. +template <typename PolicyTraits, typename Alloc> +class node_handle_base { + protected: + using slot_type = typename PolicyTraits::slot_type; + + public: + using allocator_type = Alloc; + + constexpr node_handle_base() {} + node_handle_base(node_handle_base&& other) noexcept { + *this = std::move(other); + } + ~node_handle_base() { destroy(); } + node_handle_base& operator=(node_handle_base&& other) noexcept { + destroy(); + if (!other.empty()) { + alloc_ = other.alloc_; + PolicyTraits::transfer(alloc(), slot(), other.slot()); + other.reset(); + } + return *this; + } + + bool empty() const noexcept { return !alloc_; } + explicit operator bool() const noexcept { return !empty(); } + allocator_type get_allocator() const { return *alloc_; } + + protected: + friend struct CommonAccess; + + struct transfer_tag_t {}; + node_handle_base(transfer_tag_t, const allocator_type& a, slot_type* s) + : alloc_(a) { + PolicyTraits::transfer(alloc(), slot(), s); + } + + struct move_tag_t {}; + node_handle_base(move_tag_t, const allocator_type& a, slot_type* s) + : alloc_(a) { + PolicyTraits::construct(alloc(), slot(), s); + } + + void destroy() { + if (!empty()) { + PolicyTraits::destroy(alloc(), slot()); + reset(); + } + } + + void reset() { + assert(alloc_.has_value()); + alloc_ = absl::nullopt; + } + + slot_type* slot() const { + assert(!empty()); + return reinterpret_cast<slot_type*>(std::addressof(slot_space_)); + } + allocator_type* alloc() { return std::addressof(*alloc_); } + + private: + absl::optional<allocator_type> alloc_; + mutable absl::aligned_storage_t<sizeof(slot_type), alignof(slot_type)> + slot_space_; +}; + +// For sets. +template <typename Policy, typename PolicyTraits, typename Alloc, + typename = void> +class node_handle : public node_handle_base<PolicyTraits, Alloc> { + using Base = typename node_handle::node_handle_base; + + public: + using value_type = typename PolicyTraits::value_type; + + constexpr node_handle() {} + + value_type& value() const { return PolicyTraits::element(this->slot()); } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// For maps. +template <typename Policy, typename PolicyTraits, typename Alloc> +class node_handle<Policy, PolicyTraits, Alloc, + absl::void_t<typename Policy::mapped_type>> + : public node_handle_base<PolicyTraits, Alloc> { + using Base = typename node_handle::node_handle_base; + + public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + + constexpr node_handle() {} + + auto key() const -> decltype(PolicyTraits::key(this->slot())) { + return PolicyTraits::key(this->slot()); + } + + mapped_type& mapped() const { + return PolicyTraits::value(&PolicyTraits::element(this->slot())); + } + + private: + friend struct CommonAccess; + + using Base::Base; +}; + +// Provide access to non-public node-handle functions. +struct CommonAccess { + template <typename Node> + static auto GetSlot(const Node& node) -> decltype(node.slot()) { + return node.slot(); + } + + template <typename Node> + static void Reset(Node* node) { + node->reset(); + } + + template <typename T, typename... Args> + static T Transfer(Args&&... args) { + return T(typename T::transfer_tag_t{}, std::forward<Args>(args)...); + } + + template <typename T, typename... Args> + static T Move(Args&&... args) { + return T(typename T::move_tag_t{}, std::forward<Args>(args)...); + } +}; + +// Implement the insert_return_type<> concept of C++17. +template <class Iterator, class NodeType> +struct InsertReturnType { + Iterator position; + bool inserted; + NodeType node; +}; + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_CONTAINER_H_ diff --git a/absl/container/internal/compressed_tuple.h b/absl/container/internal/compressed_tuple.h index 29fe7c12..fbace496 100644 --- a/absl/container/internal/compressed_tuple.h +++ b/absl/container/internal/compressed_tuple.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,27 +27,28 @@ // const T2& t2 = value.get<2>(); // ... // -// http://en.cppreference.com/w/cpp/language/ebo +// https://en.cppreference.com/w/cpp/language/ebo #ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ #define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ +#include <initializer_list> #include <tuple> #include <type_traits> #include <utility> #include "absl/utility/utility.h" -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__NVCC__) // We need to mark these classes with this declspec to ensure that // CompressedTuple happens. #define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases) -#else // _MSC_VER +#else #define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC -#endif // _MSC_VER +#endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <typename... Ts> @@ -76,57 +77,110 @@ constexpr bool IsFinal() { #endif } +// We can't use EBCO on other CompressedTuples because that would mean that we +// derive from multiple Storage<> instantiations with the same I parameter, +// and potentially from multiple identical Storage<> instantiations. So anytime +// we use type inheritance rather than encapsulation, we mark +// CompressedTupleImpl, to make this easy to detect. +struct uses_inheritance {}; + template <typename T> constexpr bool ShouldUseBase() { - return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>(); + return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>() && + !std::is_base_of<uses_inheritance, T>::value; } // The storage class provides two specializations: // - For empty classes, it stores T as a base class. // - For everything else, it stores T as a member. -template <typename D, size_t I, bool = ShouldUseBase<ElemT<D, I>>()> +template <typename T, size_t I, +#if defined(_MSC_VER) + bool UseBase = + ShouldUseBase<typename std::enable_if<true, T>::type>()> +#else + bool UseBase = ShouldUseBase<T>()> +#endif struct Storage { - using T = ElemT<D, I>; T value; constexpr Storage() = default; - explicit constexpr Storage(T&& v) : value(absl::forward<T>(v)) {} - constexpr const T& get() const { return value; } - T& get() { return value; } + template <typename V> + explicit constexpr Storage(absl::in_place_t, V&& v) + : value(absl::forward<V>(v)) {} + constexpr const T& get() const& { return value; } + T& get() & { return value; } + constexpr const T&& get() const&& { return absl::move(*this).value; } + T&& get() && { return std::move(*this).value; } }; -template <typename D, size_t I> -struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<D, I, true> - : ElemT<D, I> { - using T = internal_compressed_tuple::ElemT<D, I>; +template <typename T, size_t I> +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<T, I, true> : T { constexpr Storage() = default; - explicit constexpr Storage(T&& v) : T(absl::forward<T>(v)) {} - constexpr const T& get() const { return *this; } - T& get() { return *this; } + + template <typename V> + explicit constexpr Storage(absl::in_place_t, V&& v) + : T(absl::forward<V>(v)) {} + + constexpr const T& get() const& { return *this; } + T& get() & { return *this; } + constexpr const T&& get() const&& { return absl::move(*this); } + T&& get() && { return std::move(*this); } }; -template <typename D, typename I> +template <typename D, typename I, bool ShouldAnyUseBase> struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl; -template <typename... Ts, size_t... I> -struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC - CompressedTupleImpl<CompressedTuple<Ts...>, absl::index_sequence<I...>> +template <typename... Ts, size_t... I, bool ShouldAnyUseBase> +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple<Ts...>, absl::index_sequence<I...>, ShouldAnyUseBase> // We use the dummy identity function through std::integral_constant to // convince MSVC of accepting and expanding I in that context. Without it // you would get: // error C3548: 'I': parameter pack cannot be used in this context - : Storage<CompressedTuple<Ts...>, - std::integral_constant<size_t, I>::value>... { + : uses_inheritance, + Storage<Ts, std::integral_constant<size_t, I>::value>... { constexpr CompressedTupleImpl() = default; - explicit constexpr CompressedTupleImpl(Ts&&... args) - : Storage<CompressedTuple<Ts...>, I>(absl::forward<Ts>(args))... {} + template <typename... Vs> + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage<Ts, I>(absl::in_place, absl::forward<Vs>(args))... {} + friend CompressedTuple<Ts...>; }; +template <typename... Ts, size_t... I> +struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< + CompressedTuple<Ts...>, absl::index_sequence<I...>, false> + // We use the dummy identity function as above... + : Storage<Ts, std::integral_constant<size_t, I>::value, false>... { + constexpr CompressedTupleImpl() = default; + template <typename... Vs> + explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) + : Storage<Ts, I, false>(absl::in_place, absl::forward<Vs>(args))... {} + friend CompressedTuple<Ts...>; +}; + +std::false_type Or(std::initializer_list<std::false_type>); +std::true_type Or(std::initializer_list<bool>); + +// MSVC requires this to be done separately rather than within the declaration +// of CompressedTuple below. +template <typename... Ts> +constexpr bool ShouldAnyUseBase() { + return decltype( + Or({std::integral_constant<bool, ShouldUseBase<Ts>()>()...})){}; +} + +template <typename T, typename V> +using TupleMoveConstructible = typename std::conditional< + std::is_reference<T>::value, std::is_convertible<V, T>, + std::is_constructible<T, V&&>>::type; + } // 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<Ts...> is itself an empty class. +// empty classes, then CompressedTuple<Ts...> is itself an empty class. (This +// does not apply when one or more of those empty classes is itself an empty +// CompressedTuple.) // // To access the members, use member .get<N>() function. // @@ -138,28 +192,62 @@ struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC // const T2& t2 = value.get<2>(); // ... // -// http://en.cppreference.com/w/cpp/language/ebo +// https://en.cppreference.com/w/cpp/language/ebo template <typename... Ts> class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple : private internal_compressed_tuple::CompressedTupleImpl< - CompressedTuple<Ts...>, absl::index_sequence_for<Ts...>> { + CompressedTuple<Ts...>, absl::index_sequence_for<Ts...>, + internal_compressed_tuple::ShouldAnyUseBase<Ts...>()> { private: template <int I> using ElemT = internal_compressed_tuple::ElemT<CompressedTuple, I>; + template <int I> + using StorageT = internal_compressed_tuple::Storage<ElemT<I>, I>; + public: + // There seems to be a bug in MSVC dealing in which using '=default' here will + // cause the compiler to ignore the body of other constructors. The work- + // around is to explicitly implement the default constructor. +#if defined(_MSC_VER) + constexpr CompressedTuple() : CompressedTuple::CompressedTupleImpl() {} +#else constexpr CompressedTuple() = default; - explicit constexpr CompressedTuple(Ts... base) - : CompressedTuple::CompressedTupleImpl(absl::forward<Ts>(base)...) {} +#endif + explicit constexpr CompressedTuple(const Ts&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {} + + template <typename... Vs, + absl::enable_if_t< + absl::conjunction< + // Ensure we are not hiding default copy/move constructors. + absl::negation<std::is_same<void(CompressedTuple), + void(absl::decay_t<Vs>...)>>, + internal_compressed_tuple::TupleMoveConstructible< + Ts, Vs&&>...>::value, + bool> = true> + explicit constexpr CompressedTuple(Vs&&... base) + : CompressedTuple::CompressedTupleImpl(absl::in_place, + absl::forward<Vs>(base)...) {} + + template <int I> + ElemT<I>& get() & { + return internal_compressed_tuple::Storage<ElemT<I>, I>::get(); + } + + template <int I> + constexpr const ElemT<I>& get() const& { + return StorageT<I>::get(); + } template <int I> - ElemT<I>& get() { - return internal_compressed_tuple::Storage<CompressedTuple, I>::get(); + ElemT<I>&& get() && { + return std::move(*this).StorageT<I>::get(); } template <int I> - constexpr const ElemT<I>& get() const { - return internal_compressed_tuple::Storage<CompressedTuple, I>::get(); + constexpr const ElemT<I>&& get() const&& { + return absl::move(*this).StorageT<I>::get(); } }; @@ -169,7 +257,7 @@ template <> class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {}; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC diff --git a/absl/container/internal/compressed_tuple_test.cc b/absl/container/internal/compressed_tuple_test.cc index 2b5ed4a4..ec893b90 100644 --- a/absl/container/internal/compressed_tuple_test.cc +++ b/absl/container/internal/compressed_tuple_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -14,18 +14,26 @@ #include "absl/container/internal/compressed_tuple.h" +#include <memory> #include <string> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/container/internal/test_instance_tracker.h" +#include "absl/memory/memory.h" +#include "absl/types/any.h" +#include "absl/types/optional.h" +#include "absl/utility/utility.h" -namespace absl { -inline namespace lts_2018_12_18 { -namespace container_internal { -namespace { +// These are declared at global scope purely so that error messages +// are smaller and easier to understand. +enum class CallType { kConstRef, kConstMove }; template <int> -struct Empty {}; +struct Empty { + constexpr CallType value() const& { return CallType::kConstRef; } + constexpr CallType value() const&& { return CallType::kConstMove; } +}; template <typename T> struct NotEmpty { @@ -38,6 +46,15 @@ struct TwoValues { U value2; }; + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { +namespace { + +using absl::test_internal::CopyableMovableInstance; +using absl::test_internal::InstanceTracker; + TEST(CompressedTupleTest, Sizeof) { EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>)); EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>)); @@ -53,6 +70,141 @@ TEST(CompressedTupleTest, Sizeof) { sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>, Empty<1>>)); } +TEST(CompressedTupleTest, OneMoveOnRValueConstructionTemp) { + InstanceTracker tracker; + CompressedTuple<CopyableMovableInstance> x1(CopyableMovableInstance(1)); + EXPECT_EQ(tracker.instances(), 1); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_LE(tracker.moves(), 1); + EXPECT_EQ(x1.get<0>().value(), 1); +} + +TEST(CompressedTupleTest, OneMoveOnRValueConstructionMove) { + InstanceTracker tracker; + + CopyableMovableInstance i1(1); + CompressedTuple<CopyableMovableInstance> x1(std::move(i1)); + EXPECT_EQ(tracker.instances(), 2); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_LE(tracker.moves(), 1); + EXPECT_EQ(x1.get<0>().value(), 1); +} + +TEST(CompressedTupleTest, OneMoveOnRValueConstructionMixedTypes) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + CopyableMovableInstance i2(2); + Empty<0> empty; + CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>> + x1(std::move(i1), i2, empty); + EXPECT_EQ(x1.get<0>().value(), 1); + EXPECT_EQ(x1.get<1>().value(), 2); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); +} + +struct IncompleteType; +CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>> +MakeWithIncomplete(CopyableMovableInstance i1, + IncompleteType& t, // NOLINT + Empty<0> empty) { + return CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>{ + std::move(i1), t, empty}; +} + +struct IncompleteType {}; +TEST(CompressedTupleTest, OneMoveOnRValueConstructionWithIncompleteType) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + Empty<0> empty; + struct DerivedType : IncompleteType {int value = 0;}; + DerivedType fd; + fd.value = 7; + + CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>> x1 = + MakeWithIncomplete(std::move(i1), fd, empty); + + EXPECT_EQ(x1.get<0>().value(), 1); + EXPECT_EQ(static_cast<DerivedType&>(x1.get<1>()).value, 7); + + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 2); +} + +TEST(CompressedTupleTest, + OneMoveOnRValueConstructionMixedTypes_BraceInitPoisonPillExpected) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + CopyableMovableInstance i2(2); + CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>> + x1(std::move(i1), i2, {}); // NOLINT + EXPECT_EQ(x1.get<0>().value(), 1); + EXPECT_EQ(x1.get<1>().value(), 2); + EXPECT_EQ(tracker.instances(), 3); + // We are forced into the `const Ts&...` constructor (invoking copies) + // because we need it to deduce the type of `{}`. + // std::tuple also has this behavior. + // Note, this test is proof that this is expected behavior, but it is not + // _desired_ behavior. + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 0); +} + +TEST(CompressedTupleTest, OneCopyOnLValueConstruction) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + + CompressedTuple<CopyableMovableInstance> x1(i1); + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 0); + + tracker.ResetCopiesMovesSwaps(); + + CopyableMovableInstance i2(2); + const CopyableMovableInstance& i2_ref = i2; + CompressedTuple<CopyableMovableInstance> x2(i2_ref); + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 0); +} + +TEST(CompressedTupleTest, OneMoveOnRValueAccess) { + InstanceTracker tracker; + CopyableMovableInstance i1(1); + CompressedTuple<CopyableMovableInstance> x(std::move(i1)); + tracker.ResetCopiesMovesSwaps(); + + CopyableMovableInstance i2 = std::move(x).get<0>(); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); +} + +TEST(CompressedTupleTest, OneCopyOnLValueAccess) { + InstanceTracker tracker; + + CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0)); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); + + CopyableMovableInstance t = x.get<0>(); + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 1); +} + +TEST(CompressedTupleTest, ZeroCopyOnRefAccess) { + InstanceTracker tracker; + + CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0)); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); + + CopyableMovableInstance& t1 = x.get<0>(); + const CopyableMovableInstance& t2 = x.get<0>(); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); + EXPECT_EQ(t1.value(), 0); + EXPECT_EQ(t2.value(), 0); +} + TEST(CompressedTupleTest, Access) { struct S { std::string x; @@ -113,9 +265,14 @@ TEST(CompressedTupleTest, Nested) { EXPECT_EQ(4 * sizeof(char), sizeof(CompressedTuple<CompressedTuple<char, char>, CompressedTuple<char, char>>)); - EXPECT_TRUE( - (std::is_empty<CompressedTuple<CompressedTuple<Empty<0>>, - CompressedTuple<Empty<1>>>>::value)); + EXPECT_TRUE((std::is_empty<CompressedTuple<Empty<0>, Empty<1>>>::value)); + + // Make sure everything still works when things are nested. + struct CT_Empty : CompressedTuple<Empty<0>> {}; + CompressedTuple<Empty<0>, CT_Empty> nested_empty; + auto contained = nested_empty.get<0>(); + auto nested = nested_empty.get<1>().get<0>(); + EXPECT_TRUE((std::is_same<decltype(contained), decltype(nested)>::value)); } TEST(CompressedTupleTest, Reference) { @@ -141,15 +298,103 @@ TEST(CompressedTupleTest, NoElements) { EXPECT_TRUE(std::is_empty<CompressedTuple<>>::value); } +TEST(CompressedTupleTest, MoveOnlyElements) { + CompressedTuple<std::unique_ptr<std::string>> str_tup( + absl::make_unique<std::string>("str")); + + CompressedTuple<CompressedTuple<std::unique_ptr<std::string>>, + std::unique_ptr<int>> + x(std::move(str_tup), absl::make_unique<int>(5)); + + EXPECT_EQ(*x.get<0>().get<0>(), "str"); + EXPECT_EQ(*x.get<1>(), 5); + + std::unique_ptr<std::string> x0 = std::move(x.get<0>()).get<0>(); + std::unique_ptr<int> x1 = std::move(x).get<1>(); + + EXPECT_EQ(*x0, "str"); + EXPECT_EQ(*x1, 5); +} + +TEST(CompressedTupleTest, MoveConstructionMoveOnlyElements) { + CompressedTuple<std::unique_ptr<std::string>> base( + absl::make_unique<std::string>("str")); + EXPECT_EQ(*base.get<0>(), "str"); + + CompressedTuple<std::unique_ptr<std::string>> copy(std::move(base)); + EXPECT_EQ(*copy.get<0>(), "str"); +} + +TEST(CompressedTupleTest, AnyElements) { + any a(std::string("str")); + CompressedTuple<any, any&> x(any(5), a); + EXPECT_EQ(absl::any_cast<int>(x.get<0>()), 5); + EXPECT_EQ(absl::any_cast<std::string>(x.get<1>()), "str"); + + a = 0.5f; + EXPECT_EQ(absl::any_cast<float>(x.get<1>()), 0.5); + + // Ensure copy construction work in the face of a type with a universal + // implicit constructor; + CompressedTuple<absl::any> c{}, d(c); // NOLINT +} + TEST(CompressedTupleTest, Constexpr) { - constexpr CompressedTuple<int, double, CompressedTuple<int>> x( - 7, 1.25, CompressedTuple<int>(5)); + struct NonTrivialStruct { + constexpr NonTrivialStruct() = default; + constexpr int value() const { return v; } + int v = 5; + }; + struct TrivialStruct { + TrivialStruct() = default; + constexpr int value() const { return v; } + int v; + }; + constexpr CompressedTuple<int, double, CompressedTuple<int>, Empty<0>> x( + 7, 1.25, CompressedTuple<int>(5), {}); constexpr int x0 = x.get<0>(); constexpr double x1 = x.get<1>(); constexpr int x2 = x.get<2>().get<0>(); + constexpr CallType x3 = x.get<3>().value(); + EXPECT_EQ(x0, 7); EXPECT_EQ(x1, 1.25); EXPECT_EQ(x2, 5); + EXPECT_EQ(x3, CallType::kConstRef); + +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 + constexpr CompressedTuple<Empty<0>, TrivialStruct, int> trivial = {}; + constexpr CallType trivial0 = trivial.get<0>().value(); + constexpr int trivial1 = trivial.get<1>().value(); + constexpr int trivial2 = trivial.get<2>(); + + EXPECT_EQ(trivial0, CallType::kConstRef); + EXPECT_EQ(trivial1, 0); + EXPECT_EQ(trivial2, 0); +#endif + + constexpr CompressedTuple<Empty<0>, NonTrivialStruct, absl::optional<int>> + non_trivial = {}; + constexpr CallType non_trivial0 = non_trivial.get<0>().value(); + constexpr int non_trivial1 = non_trivial.get<1>().value(); + constexpr absl::optional<int> non_trivial2 = non_trivial.get<2>(); + + EXPECT_EQ(non_trivial0, CallType::kConstRef); + EXPECT_EQ(non_trivial1, 5); + EXPECT_EQ(non_trivial2, absl::nullopt); + + static constexpr char data[] = "DEF"; + constexpr CompressedTuple<const char*> z(data); + constexpr const char* z1 = z.get<0>(); + EXPECT_EQ(std::string(z1), std::string(data)); + +#if defined(__clang__) + // An apparent bug in earlier versions of gcc claims these are ambiguous. + constexpr int x2m = absl::move(x.get<2>()).get<0>(); + constexpr CallType x3m = absl::move(x).get<3>().value(); + EXPECT_EQ(x2m, 5); + EXPECT_EQ(x3m, CallType::kConstMove); +#endif } #if defined(__clang__) || defined(__GNUC__) @@ -164,5 +409,5 @@ TEST(CompressedTupleTest, EmptyFinalClass) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index ddccbe05..eb6d7eb7 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -34,7 +34,7 @@ #include "absl/utility/utility.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // Allocates at least n bytes aligned to the specified alignment. @@ -287,13 +287,48 @@ struct IsLayoutCompatible { } // namespace memory_internal -// If kMutableKeys is false, only the value member is accessed. +// The internal storage type for key-value containers like flat_hash_map. // -// If kMutableKeys is true, key is accessed through all slots while value and -// mutable_value are accessed only via INITIALIZED slots. Slots are created and -// destroyed via mutable_value so that the key can be moved later. +// It is convenient for the value_type of a flat_hash_map<K, V> to be +// pair<const K, V>; the "const K" prevents accidental modification of the key +// when dealing with the reference returned from find() and similar methods. +// However, this creates other problems; we want to be able to emplace(K, V) +// efficiently with move operations, and similarly be able to move a +// pair<K, V> in insert(). +// +// The solution is this union, which aliases the const and non-const versions +// of the pair. This also allows flat_hash_map<const K, V> to work, even though +// that has the same efficiency issues with move in emplace() and insert() - +// but people do it anyway. +// +// If kMutableKeys is false, only the value member can be accessed. +// +// If kMutableKeys is true, key can be accessed through all slots while value +// and mutable_value must be accessed only via INITIALIZED slots. Slots are +// created and destroyed via mutable_value so that the key can be moved later. +// +// Accessing one of the union fields while the other is active is safe as +// long as they are layout-compatible, which is guaranteed by the definition of +// kMutableKeys. For C++11, the relevant section of the standard is +// https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19) template <class K, class V> -union slot_type { +union map_slot_type { + map_slot_type() {} + ~map_slot_type() = delete; + using value_type = std::pair<const K, V>; + using mutable_value_type = std::pair<K, V>; + + value_type value; + mutable_value_type mutable_value; + K key; +}; + +template <class K, class V> +struct map_slot_policy { + using slot_type = map_slot_type<K, V>; + using value_type = std::pair<const K, V>; + using mutable_value_type = std::pair<K, V>; + private: static void emplace(slot_type* slot) { // The construction of union doesn't do anything at runtime but it allows us @@ -303,19 +338,17 @@ union slot_type { // If pair<const K, V> and pair<K, V> 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<bool, - memory_internal::IsLayoutCompatible<K, V>::value>; + using kMutableKeys = memory_internal::IsLayoutCompatible<K, V>; public: - slot_type() {} - ~slot_type() = delete; - using value_type = std::pair<const K, V>; - using mutable_value_type = std::pair<K, V>; + static value_type& element(slot_type* slot) { return slot->value; } + static const value_type& element(const slot_type* slot) { + return slot->value; + } - value_type value; - mutable_value_type mutable_value; - K key; + static const K& key(const slot_type* slot) { + return kMutableKeys::value ? slot->key : slot->value.first; + } template <class Allocator, class... Args> static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { @@ -401,7 +434,7 @@ union slot_type { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ diff --git a/absl/container/internal/container_memory_test.cc b/absl/container/internal/container_memory_test.cc index da87ca20..ea9568dc 100644 --- a/absl/container/internal/container_memory_test.cc +++ b/absl/container/internal/container_memory_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -186,5 +186,5 @@ TEST(DecomposePair, NotDecomposable) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/counting_allocator.h b/absl/container/internal/counting_allocator.h new file mode 100644 index 00000000..94a457ca --- /dev/null +++ b/absl/container/internal/counting_allocator.h @@ -0,0 +1,81 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ +#define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ + +#include <cassert> +#include <cstdint> +#include <memory> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +// This is a stateful allocator, but the state lives outside of the +// allocator (in whatever test is using the allocator). This is odd +// but helps in tests where the allocator is propagated into nested +// containers - that chain of allocators uses the same state and is +// thus easier to query for aggregate allocation information. +template <typename T> +class CountingAllocator : public std::allocator<T> { + public: + using Alloc = std::allocator<T>; + using pointer = typename Alloc::pointer; + using size_type = typename Alloc::size_type; + + CountingAllocator() : bytes_used_(nullptr) {} + explicit CountingAllocator(int64_t* b) : bytes_used_(b) {} + + template <typename U> + CountingAllocator(const CountingAllocator<U>& x) + : Alloc(x), bytes_used_(x.bytes_used_) {} + + pointer allocate(size_type n, + std::allocator<void>::const_pointer hint = nullptr) { + assert(bytes_used_ != nullptr); + *bytes_used_ += n * sizeof(T); + return Alloc::allocate(n, hint); + } + + void deallocate(pointer p, size_type n) { + Alloc::deallocate(p, n); + assert(bytes_used_ != nullptr); + *bytes_used_ -= n * sizeof(T); + } + + template<typename U> + class rebind { + public: + using other = CountingAllocator<U>; + }; + + friend bool operator==(const CountingAllocator& a, + const CountingAllocator& b) { + return a.bytes_used_ == b.bytes_used_; + } + + friend bool operator!=(const CountingAllocator& a, + const CountingAllocator& b) { + return !(a == b); + } + + int64_t* bytes_used_; +}; + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ diff --git a/absl/container/internal/hash_function_defaults.h b/absl/container/internal/hash_function_defaults.h index 72c75fa0..2155076d 100644 --- a/absl/container/internal/hash_function_defaults.h +++ b/absl/container/internal/hash_function_defaults.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -39,8 +39,8 @@ // equal functions are still bound to T. This is important because some type U // can be hashed by/tested for equality differently depending on T. A notable // example is `const char*`. `const char*` is treated as a c-style string when -// the hash function is hash<string> but as a pointer when the hash function is -// hash<void*>. +// the hash function is hash<std::string> but as a pointer when the hash +// function is hash<void*>. // #ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ #define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ @@ -56,7 +56,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // The hash of an object of type T is computed by using absl::Hash. @@ -84,6 +84,7 @@ struct StringHashEq { } }; }; + template <> struct HashEq<std::string> : StringHashEq {}; template <> @@ -139,7 +140,7 @@ template <class T> using hash_default_eq = typename container_internal::HashEq<T>::Eq; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ diff --git a/absl/container/internal/hash_function_defaults_test.cc b/absl/container/internal/hash_function_defaults_test.cc index 4610843a..ce6133f8 100644 --- a/absl/container/internal/hash_function_defaults_test.cc +++ b/absl/container/internal/hash_function_defaults_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -78,14 +78,14 @@ struct EqString : ::testing::Test { hash_default_eq<T> key_eq; }; -TYPED_TEST_CASE(EqString, StringTypes); +TYPED_TEST_SUITE(EqString, StringTypes); template <class T> struct HashString : ::testing::Test { hash_default_hash<T> hasher; }; -TYPED_TEST_CASE(HashString, StringTypes); +TYPED_TEST_SUITE(HashString, StringTypes); TYPED_TEST(EqString, Works) { auto eq = this->key_eq; @@ -122,14 +122,14 @@ struct EqPointer : ::testing::Test { hash_default_eq<T> key_eq; }; -TYPED_TEST_CASE(EqPointer, PointerTypes); +TYPED_TEST_SUITE(EqPointer, PointerTypes); template <class T> struct HashPointer : ::testing::Test { hash_default_hash<T> hasher; }; -TYPED_TEST_CASE(HashPointer, PointerTypes); +TYPED_TEST_SUITE(HashPointer, PointerTypes); TYPED_TEST(EqPointer, Works) { int dummy; @@ -203,15 +203,11 @@ TYPED_TEST(HashPointer, Works) { EXPECT_NE(hash(&dummy), hash(cuptr)); } -// Cartesian product of (string, std::string, absl::string_view) -// with (string, std::string, absl::string_view, const char*). +// Cartesian product of (std::string, absl::string_view) +// with (std::string, absl::string_view, const char*). using StringTypesCartesianProduct = Types< // clang-format off - std::pair<std::string, std::string>, - std::pair<std::string, absl::string_view>, - std::pair<std::string, const char*>, - std::pair<absl::string_view, std::string>, std::pair<absl::string_view, absl::string_view>, std::pair<absl::string_view, const char*>>; @@ -249,11 +245,11 @@ TYPED_TEST_P(StringLikeTest, HashEq) { EXPECT_NE(this->hash(this->a1), this->hash(this->b2)); } -TYPED_TEST_CASE(StringLikeTest, StringTypesCartesianProduct); +TYPED_TEST_SUITE(StringLikeTest, StringTypesCartesianProduct); } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl enum Hash : size_t { @@ -284,7 +280,7 @@ struct hash<Hashable<H>> { } // namespace std namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -299,5 +295,5 @@ TEST(Delegate, HashDispatch) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/hash_generator_testing.cc b/absl/container/internal/hash_generator_testing.cc index aef41d72..36b2571b 100644 --- a/absl/container/internal/hash_generator_testing.cc +++ b/absl/container/internal/hash_generator_testing.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include <deque> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace hash_internal { namespace { @@ -70,5 +70,5 @@ absl::string_view Generator<absl::string_view>::operator()() const { } // namespace hash_internal } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/hash_generator_testing.h b/absl/container/internal/hash_generator_testing.h index 65e88964..27962c35 100644 --- a/absl/container/internal/hash_generator_testing.h +++ b/absl/container/internal/hash_generator_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -31,7 +31,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace hash_internal { namespace generator_internal { @@ -146,7 +146,7 @@ using GeneratedType = decltype( } // namespace hash_internal } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ diff --git a/absl/container/internal/hash_policy_testing.h b/absl/container/internal/hash_policy_testing.h index 9c310ad4..8f0d2a52 100644 --- a/absl/container/internal/hash_policy_testing.h +++ b/absl/container/internal/hash_policy_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,7 +30,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace hash_testing_internal { @@ -163,7 +163,7 @@ auto keys(const Set& s) } } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions diff --git a/absl/container/internal/hash_policy_testing_test.cc b/absl/container/internal/hash_policy_testing_test.cc index 00c436b3..8fd1df00 100644 --- a/absl/container/internal/hash_policy_testing_test.cc +++ b/absl/container/internal/hash_policy_testing_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -41,5 +41,5 @@ TEST(_, Hash) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/hash_policy_traits.h b/absl/container/internal/hash_policy_traits.h index 41e26212..3d87e821 100644 --- a/absl/container/internal/hash_policy_traits.h +++ b/absl/container/internal/hash_policy_traits.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // Defines how slots are initialized/destroyed/moved. @@ -185,7 +185,7 @@ struct hash_policy_traits { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ diff --git a/absl/container/internal/hash_policy_traits_test.cc b/absl/container/internal/hash_policy_traits_test.cc index 07cecdfa..edfaf63e 100644 --- a/absl/container/internal/hash_policy_traits_test.cc +++ b/absl/container/internal/hash_policy_traits_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -140,5 +140,5 @@ TEST_F(Test, with_transfer) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/hashtable_debug.h b/absl/container/internal/hashtable_debug.h index b6a43512..1d1a9c28 100644 --- a/absl/container/internal/hashtable_debug.h +++ b/absl/container/internal/hashtable_debug.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,7 +38,7 @@ #include "absl/container/internal/hashtable_debug_hooks.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // Returns the number of probes required to lookup `key`. Returns 0 for a @@ -61,7 +61,7 @@ std::vector<size_t> GetHashtableDebugNumProbesHistogram(const C& container) { size_t num_probes = GetHashtableDebugNumProbes( container, absl::container_internal::hashtable_debug_internal::GetKey<C>(*it, 0)); - v.resize(std::max(v.size(), num_probes + 1)); + v.resize((std::max)(v.size(), num_probes + 1)); v[num_probes]++; } return v; @@ -104,7 +104,7 @@ size_t LowerBoundAllocatedByteSize(size_t num_elements) { } } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ diff --git a/absl/container/internal/hashtable_debug_hooks.h b/absl/container/internal/hashtable_debug_hooks.h index 50ba6ba5..7b95fcef 100644 --- a/absl/container/internal/hashtable_debug_hooks.h +++ b/absl/container/internal/hashtable_debug_hooks.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include <vector> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace hashtable_debug_internal { @@ -77,7 +77,7 @@ struct HashtableDebugAccess { } // namespace hashtable_debug_internal } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc new file mode 100644 index 00000000..2338045d --- /dev/null +++ b/absl/container/internal/hashtablez_sampler.cc @@ -0,0 +1,310 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include <atomic> +#include <cassert> +#include <cmath> +#include <functional> +#include <limits> + +#include "absl/base/attributes.h" +#include "absl/container/internal/have_sse.h" +#include "absl/debugging/stacktrace.h" +#include "absl/memory/memory.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { +constexpr int HashtablezInfo::kMaxStackDepth; + +namespace { +ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{ + false +}; +ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10}; +ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_max_samples{1 << 20}; + +// Returns the next pseudo-random value. +// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 +// This is the lrand64 generator. +uint64_t NextRandom(uint64_t rnd) { + const uint64_t prng_mult = uint64_t{0x5DEECE66D}; + const uint64_t prng_add = 0xB; + const uint64_t prng_mod_power = 48; + const uint64_t prng_mod_mask = ~(~uint64_t{0} << prng_mod_power); + return (prng_mult * rnd + prng_add) & prng_mod_mask; +} + +// Generates a geometric variable with the specified mean. +// This is done by generating a random number between 0 and 1 and applying +// the inverse cumulative distribution function for an exponential. +// Specifically: Let m be the inverse of the sample period, then +// the probability distribution function is m*exp(-mx) so the CDF is +// p = 1 - exp(-mx), so +// q = 1 - p = exp(-mx) +// log_e(q) = -mx +// -log_e(q)/m = x +// log_2(q) * (-log_e(2) * 1/m) = x +// In the code, q is actually in the range 1 to 2**26, hence the -26 below +// +int64_t GetGeometricVariable(int64_t mean) { +#if ABSL_HAVE_THREAD_LOCAL + thread_local +#else // ABSL_HAVE_THREAD_LOCAL + // SampleSlow and hence GetGeometricVariable is guarded by a single mutex when + // there are not thread locals. Thus, a single global rng is acceptable for + // that case. + static +#endif // ABSL_HAVE_THREAD_LOCAL + uint64_t rng = []() { + // We don't get well distributed numbers from this so we call + // NextRandom() a bunch to mush the bits around. We use a global_rand + // to handle the case where the same thread (by memory address) gets + // created and destroyed repeatedly. + ABSL_CONST_INIT static std::atomic<uint32_t> global_rand(0); + uint64_t r = reinterpret_cast<uint64_t>(&rng) + + global_rand.fetch_add(1, std::memory_order_relaxed); + for (int i = 0; i < 20; ++i) { + r = NextRandom(r); + } + return r; + }(); + + rng = NextRandom(rng); + + // Take the top 26 bits as the random number + // (This plus the 1<<58 sampling bound give a max possible step of + // 5194297183973780480 bytes.) + const uint64_t prng_mod_power = 48; // Number of bits in prng + // The uint32_t cast is to prevent a (hard-to-reproduce) NAN + // under piii debug for some binaries. + double q = static_cast<uint32_t>(rng >> (prng_mod_power - 26)) + 1.0; + // Put the computed p-value through the CDF of a geometric. + double interval = (log2(q) - 26) * (-std::log(2.0) * mean); + + // Very large values of interval overflow int64_t. If we happen to + // hit such improbable condition, we simply cheat and clamp interval + // to largest supported value. + if (interval > static_cast<double>(std::numeric_limits<int64_t>::max() / 2)) { + return std::numeric_limits<int64_t>::max() / 2; + } + + // Small values of interval are equivalent to just sampling next time. + if (interval < 1) { + return 1; + } + return static_cast<int64_t>(interval); +} + +} // namespace + +HashtablezSampler& HashtablezSampler::Global() { + static auto* sampler = new HashtablezSampler(); + return *sampler; +} + +HashtablezSampler::DisposeCallback HashtablezSampler::SetDisposeCallback( + DisposeCallback f) { + return dispose_.exchange(f, std::memory_order_relaxed); +} + +HashtablezInfo::HashtablezInfo() { PrepareForSampling(); } +HashtablezInfo::~HashtablezInfo() = default; + +void HashtablezInfo::PrepareForSampling() { + capacity.store(0, std::memory_order_relaxed); + size.store(0, std::memory_order_relaxed); + num_erases.store(0, std::memory_order_relaxed); + max_probe_length.store(0, std::memory_order_relaxed); + total_probe_length.store(0, std::memory_order_relaxed); + hashes_bitwise_or.store(0, std::memory_order_relaxed); + hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed); + + create_time = absl::Now(); + // The inliner makes hardcoded skip_count difficult (especially when combined + // with LTO). We use the ability to exclude stacks by regex when encoding + // instead. + depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth, + /* skip_count= */ 0); + dead = nullptr; +} + +HashtablezSampler::HashtablezSampler() + : dropped_samples_(0), size_estimate_(0), all_(nullptr), dispose_(nullptr) { + absl::MutexLock l(&graveyard_.init_mu); + graveyard_.dead = &graveyard_; +} + +HashtablezSampler::~HashtablezSampler() { + HashtablezInfo* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + HashtablezInfo* next = s->next; + delete s; + s = next; + } +} + +void HashtablezSampler::PushNew(HashtablezInfo* sample) { + sample->next = all_.load(std::memory_order_relaxed); + while (!all_.compare_exchange_weak(sample->next, sample, + std::memory_order_release, + std::memory_order_relaxed)) { + } +} + +void HashtablezSampler::PushDead(HashtablezInfo* sample) { + if (auto* dispose = dispose_.load(std::memory_order_relaxed)) { + dispose(*sample); + } + + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + absl::MutexLock sample_lock(&sample->init_mu); + sample->dead = graveyard_.dead; + graveyard_.dead = sample; +} + +HashtablezInfo* HashtablezSampler::PopDead() { + absl::MutexLock graveyard_lock(&graveyard_.init_mu); + + // The list is circular, so eventually it collapses down to + // graveyard_.dead == &graveyard_ + // when it is empty. + HashtablezInfo* sample = graveyard_.dead; + if (sample == &graveyard_) return nullptr; + + absl::MutexLock sample_lock(&sample->init_mu); + graveyard_.dead = sample->dead; + sample->PrepareForSampling(); + return sample; +} + +HashtablezInfo* HashtablezSampler::Register() { + int64_t size = size_estimate_.fetch_add(1, std::memory_order_relaxed); + if (size > g_hashtablez_max_samples.load(std::memory_order_relaxed)) { + size_estimate_.fetch_sub(1, std::memory_order_relaxed); + dropped_samples_.fetch_add(1, std::memory_order_relaxed); + return nullptr; + } + + HashtablezInfo* sample = PopDead(); + if (sample == nullptr) { + // Resurrection failed. Hire a new warlock. + sample = new HashtablezInfo(); + PushNew(sample); + } + + return sample; +} + +void HashtablezSampler::Unregister(HashtablezInfo* sample) { + PushDead(sample); + size_estimate_.fetch_sub(1, std::memory_order_relaxed); +} + +int64_t HashtablezSampler::Iterate( + const std::function<void(const HashtablezInfo& stack)>& f) { + HashtablezInfo* s = all_.load(std::memory_order_acquire); + while (s != nullptr) { + absl::MutexLock l(&s->init_mu); + if (s->dead == nullptr) { + f(*s); + } + s = s->next; + } + + return dropped_samples_.load(std::memory_order_relaxed); +} + +HashtablezInfo* SampleSlow(int64_t* next_sample) { + if (kAbslContainerInternalSampleEverything) { + *next_sample = 1; + return HashtablezSampler::Global().Register(); + } + + bool first = *next_sample < 0; + *next_sample = GetGeometricVariable( + g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); + + // g_hashtablez_enabled can be dynamically flipped, we need to set a threshold + // low enough that we will start sampling in a reasonable time, so we just use + // the default sampling rate. + if (!g_hashtablez_enabled.load(std::memory_order_relaxed)) return nullptr; + + // We will only be negative on our first count, so we should just retry in + // that case. + if (first) { + if (ABSL_PREDICT_TRUE(--*next_sample > 0)) return nullptr; + return SampleSlow(next_sample); + } + + return HashtablezSampler::Global().Register(); +} + +#if ABSL_PER_THREAD_TLS == 1 +ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0; +#endif // ABSL_PER_THREAD_TLS == 1 + +void UnsampleSlow(HashtablezInfo* info) { + HashtablezSampler::Global().Unregister(info); +} + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired) { + // SwissTables probe in groups of 16, so scale this to count items probes and + // not offset from desired. + size_t probe_length = distance_from_desired; +#if SWISSTABLE_HAVE_SSE2 + probe_length /= 16; +#else + probe_length /= 8; +#endif + + info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed); + info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed); + info->max_probe_length.store( + std::max(info->max_probe_length.load(std::memory_order_relaxed), + probe_length), + std::memory_order_relaxed); + info->total_probe_length.fetch_add(probe_length, std::memory_order_relaxed); + info->size.fetch_add(1, std::memory_order_relaxed); +} + +void SetHashtablezEnabled(bool enabled) { + g_hashtablez_enabled.store(enabled, std::memory_order_release); +} + +void SetHashtablezSampleParameter(int32_t rate) { + if (rate > 0) { + g_hashtablez_sample_parameter.store(rate, std::memory_order_release); + } else { + ABSL_RAW_LOG(ERROR, "Invalid hashtablez sample rate: %lld", + static_cast<long long>(rate)); // NOLINT(runtime/int) + } +} + +void SetHashtablezMaxSamples(int32_t max) { + if (max > 0) { + g_hashtablez_max_samples.store(max, std::memory_order_release); + } else { + ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: %lld", + static_cast<long long>(max)); // NOLINT(runtime/int) + } +} + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h new file mode 100644 index 00000000..f17c425c --- /dev/null +++ b/absl/container/internal/hashtablez_sampler.h @@ -0,0 +1,290 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: hashtablez_sampler.h +// ----------------------------------------------------------------------------- +// +// This header file defines the API for a low level library to sample hashtables +// and collect runtime statistics about them. +// +// `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which +// store information about a single sample. +// +// `Record*` methods store information into samples. +// `Sample()` and `Unsample()` make use of a single global sampler with +// properties controlled by the flags hashtablez_enabled, +// hashtablez_sample_rate, and hashtablez_max_samples. +// +// WARNING +// +// Using this sampling API may cause sampled Swiss tables to use the global +// allocator (operator `new`) in addition to any custom allocator. If you +// are using a table in an unusual circumstance where allocation or calling a +// linux syscall is unacceptable, this could interfere. +// +// This utility is internal-only. Use at your own risk. + +#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ +#define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ + +#include <atomic> +#include <functional> +#include <memory> +#include <vector> + +#include "absl/base/internal/per_thread_tls.h" +#include "absl/base/optimization.h" +#include "absl/container/internal/have_sse.h" +#include "absl/synchronization/mutex.h" +#include "absl/utility/utility.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +// Stores information about a sampled hashtable. All mutations to this *must* +// be made through `Record*` functions below. All reads from this *must* only +// occur in the callback to `HashtablezSampler::Iterate`. +struct HashtablezInfo { + // Constructs the object but does not fill in any fields. + HashtablezInfo(); + ~HashtablezInfo(); + HashtablezInfo(const HashtablezInfo&) = delete; + HashtablezInfo& operator=(const HashtablezInfo&) = delete; + + // Puts the object into a clean state, fills in the logically `const` members, + // blocking for any readers that are currently sampling the object. + void PrepareForSampling() EXCLUSIVE_LOCKS_REQUIRED(init_mu); + + // These fields are mutated by the various Record* APIs and need to be + // thread-safe. + std::atomic<size_t> capacity; + std::atomic<size_t> size; + std::atomic<size_t> num_erases; + std::atomic<size_t> max_probe_length; + std::atomic<size_t> total_probe_length; + std::atomic<size_t> hashes_bitwise_or; + std::atomic<size_t> hashes_bitwise_and; + + // `HashtablezSampler` maintains intrusive linked lists for all samples. See + // comments on `HashtablezSampler::all_` for details on these. `init_mu` + // guards the ability to restore the sample to a pristine state. This + // prevents races with sampling and resurrecting an object. + absl::Mutex init_mu; + HashtablezInfo* next; + HashtablezInfo* dead GUARDED_BY(init_mu); + + // All of the fields below are set by `PrepareForSampling`, they must not be + // mutated in `Record*` functions. They are logically `const` in that sense. + // These are guarded by init_mu, but that is not externalized to clients, who + // can only read them during `HashtablezSampler::Iterate` which will hold the + // lock. + static constexpr int kMaxStackDepth = 64; + absl::Time create_time; + int32_t depth; + void* stack[kMaxStackDepth]; +}; + +inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) { +#if SWISSTABLE_HAVE_SSE2 + total_probe_length /= 16; +#else + total_probe_length /= 8; +#endif + info->total_probe_length.store(total_probe_length, std::memory_order_relaxed); + info->num_erases.store(0, std::memory_order_relaxed); +} + +inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, + size_t capacity) { + info->size.store(size, std::memory_order_relaxed); + info->capacity.store(capacity, std::memory_order_relaxed); + if (size == 0) { + // This is a clear, reset the total/num_erases too. + RecordRehashSlow(info, 0); + } +} + +void RecordInsertSlow(HashtablezInfo* info, size_t hash, + size_t distance_from_desired); + +inline void RecordEraseSlow(HashtablezInfo* info) { + info->size.fetch_sub(1, std::memory_order_relaxed); + info->num_erases.fetch_add(1, std::memory_order_relaxed); +} + +HashtablezInfo* SampleSlow(int64_t* next_sample); +void UnsampleSlow(HashtablezInfo* info); + +class HashtablezInfoHandle { + public: + explicit HashtablezInfoHandle() : info_(nullptr) {} + explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {} + ~HashtablezInfoHandle() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + UnsampleSlow(info_); + } + + HashtablezInfoHandle(const HashtablezInfoHandle&) = delete; + HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete; + + HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept + : info_(absl::exchange(o.info_, nullptr)) {} + HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept { + if (ABSL_PREDICT_FALSE(info_ != nullptr)) { + UnsampleSlow(info_); + } + info_ = absl::exchange(o.info_, nullptr); + return *this; + } + + inline void RecordStorageChanged(size_t size, size_t capacity) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordStorageChangedSlow(info_, size, capacity); + } + + inline void RecordRehash(size_t total_probe_length) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordRehashSlow(info_, total_probe_length); + } + + inline void RecordInsert(size_t hash, size_t distance_from_desired) { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordInsertSlow(info_, hash, distance_from_desired); + } + + inline void RecordErase() { + if (ABSL_PREDICT_TRUE(info_ == nullptr)) return; + RecordEraseSlow(info_); + } + + friend inline void swap(HashtablezInfoHandle& lhs, + HashtablezInfoHandle& rhs) { + std::swap(lhs.info_, rhs.info_); + } + + private: + friend class HashtablezInfoHandlePeer; + HashtablezInfo* info_; +}; + +#if ABSL_PER_THREAD_TLS == 1 +extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample; +#endif // ABSL_PER_THREAD_TLS + +// Returns an RAII sampling handle that manages registration and unregistation +// with the global sampler. +inline HashtablezInfoHandle Sample() { +#if ABSL_PER_THREAD_TLS == 0 + static auto* mu = new absl::Mutex; + static int64_t global_next_sample = 0; + absl::MutexLock l(mu); +#endif // !ABSL_HAVE_THREAD_LOCAL + + if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) { + return HashtablezInfoHandle(nullptr); + } + return HashtablezInfoHandle(SampleSlow(&global_next_sample)); +} + +// Holds samples and their associated stack traces with a soft limit of +// `SetHashtablezMaxSamples()`. +// +// Thread safe. +class HashtablezSampler { + public: + // Returns a global Sampler. + static HashtablezSampler& Global(); + + HashtablezSampler(); + ~HashtablezSampler(); + + // Registers for sampling. Returns an opaque registration info. + HashtablezInfo* Register(); + + // Unregisters the sample. + void Unregister(HashtablezInfo* sample); + + // The dispose callback will be called on all samples the moment they are + // being unregistered. Only affects samples that are unregistered after the + // callback has been set. + // Returns the previous callback. + using DisposeCallback = void (*)(const HashtablezInfo&); + DisposeCallback SetDisposeCallback(DisposeCallback f); + + // Iterates over all the registered `StackInfo`s. Returning the number of + // samples that have been dropped. + int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& f); + + private: + void PushNew(HashtablezInfo* sample); + void PushDead(HashtablezInfo* sample); + HashtablezInfo* PopDead(); + + std::atomic<size_t> dropped_samples_; + std::atomic<size_t> size_estimate_; + + // Intrusive lock free linked lists for tracking samples. + // + // `all_` records all samples (they are never removed from this list) and is + // terminated with a `nullptr`. + // + // `graveyard_.dead` is a circular linked list. When it is empty, + // `graveyard_.dead == &graveyard`. The list is circular so that + // every item on it (even the last) has a non-null dead pointer. This allows + // `Iterate` to determine if a given sample is live or dead using only + // information on the sample itself. + // + // For example, nodes [A, B, C, D, E] with [A, C, E] alive and [B, D] dead + // looks like this (G is the Graveyard): + // + // +---+ +---+ +---+ +---+ +---+ + // all -->| A |--->| B |--->| C |--->| D |--->| E | + // | | | | | | | | | | + // +---+ | | +->| |-+ | | +->| |-+ | | + // | G | +---+ | +---+ | +---+ | +---+ | +---+ + // | | | | | | + // | | --------+ +--------+ | + // +---+ | + // ^ | + // +--------------------------------------+ + // + std::atomic<HashtablezInfo*> all_; + HashtablezInfo graveyard_; + + std::atomic<DisposeCallback> dispose_; +}; + +// Enables or disables sampling for Swiss tables. +void SetHashtablezEnabled(bool enabled); + +// Sets the rate at which Swiss tables will be sampled. +void SetHashtablezSampleParameter(int32_t rate); + +// Sets a soft max for the number of samples that will be kept. +void SetHashtablezMaxSamples(int32_t max); + +// Configuration override. +// This allows process-wide sampling without depending on order of +// initialization of static storage duration objects. +// The definition of this constant is weak, which allows us to inject a +// different value for it at link time. +extern "C" const bool kAbslContainerInternalSampleEverything; + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ diff --git a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc new file mode 100644 index 00000000..d3f41c7c --- /dev/null +++ b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc @@ -0,0 +1,29 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include "absl/base/attributes.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +// See hashtablez_sampler.h for details. +extern "C" ABSL_ATTRIBUTE_WEAK const bool + kAbslContainerInternalSampleEverything = false; + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc new file mode 100644 index 00000000..bdae75f3 --- /dev/null +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -0,0 +1,357 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/container/internal/hashtablez_sampler.h" + +#include <atomic> +#include <limits> +#include <random> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/container/internal/have_sse.h" +#include "absl/synchronization/blocking_counter.h" +#include "absl/synchronization/internal/thread_pool.h" +#include "absl/synchronization/mutex.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" + +#if SWISSTABLE_HAVE_SSE2 +constexpr int kProbeLength = 16; +#else +constexpr int kProbeLength = 8; +#endif + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { +class HashtablezInfoHandlePeer { + public: + static bool IsSampled(const HashtablezInfoHandle& h) { + return h.info_ != nullptr; + } + + static HashtablezInfo* GetInfo(HashtablezInfoHandle* h) { return h->info_; } +}; + +namespace { +using ::absl::synchronization_internal::ThreadPool; +using ::testing::IsEmpty; +using ::testing::UnorderedElementsAre; + +std::vector<size_t> GetSizes(HashtablezSampler* s) { + std::vector<size_t> res; + s->Iterate([&](const HashtablezInfo& info) { + res.push_back(info.size.load(std::memory_order_acquire)); + }); + return res; +} + +HashtablezInfo* Register(HashtablezSampler* s, size_t size) { + auto* info = s->Register(); + assert(info != nullptr); + info->size.store(size); + return info; +} + +TEST(HashtablezInfoTest, PrepareForSampling) { + absl::Time test_start = absl::Now(); + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + + EXPECT_EQ(info.capacity.load(), 0); + EXPECT_EQ(info.size.load(), 0); + EXPECT_EQ(info.num_erases.load(), 0); + EXPECT_EQ(info.max_probe_length.load(), 0); + EXPECT_EQ(info.total_probe_length.load(), 0); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0); + EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); + EXPECT_GE(info.create_time, test_start); + + info.capacity.store(1, std::memory_order_relaxed); + info.size.store(1, std::memory_order_relaxed); + info.num_erases.store(1, std::memory_order_relaxed); + info.max_probe_length.store(1, std::memory_order_relaxed); + info.total_probe_length.store(1, std::memory_order_relaxed); + info.hashes_bitwise_or.store(1, std::memory_order_relaxed); + info.hashes_bitwise_and.store(1, std::memory_order_relaxed); + info.create_time = test_start - absl::Hours(20); + + info.PrepareForSampling(); + EXPECT_EQ(info.capacity.load(), 0); + EXPECT_EQ(info.size.load(), 0); + EXPECT_EQ(info.num_erases.load(), 0); + EXPECT_EQ(info.max_probe_length.load(), 0); + EXPECT_EQ(info.total_probe_length.load(), 0); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0); + EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{}); + EXPECT_GE(info.create_time, test_start); +} + +TEST(HashtablezInfoTest, RecordStorageChanged) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + RecordStorageChangedSlow(&info, 17, 47); + EXPECT_EQ(info.size.load(), 17); + EXPECT_EQ(info.capacity.load(), 47); + RecordStorageChangedSlow(&info, 20, 20); + EXPECT_EQ(info.size.load(), 20); + EXPECT_EQ(info.capacity.load(), 20); +} + +TEST(HashtablezInfoTest, RecordInsert) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + EXPECT_EQ(info.max_probe_length.load(), 0); + RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); + EXPECT_EQ(info.max_probe_length.load(), 6); + EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00); + RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength); + EXPECT_EQ(info.max_probe_length.load(), 6); + EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00); + RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength); + EXPECT_EQ(info.max_probe_length.load(), 12); + EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000); + EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00); +} + +TEST(HashtablezInfoTest, RecordErase) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + EXPECT_EQ(info.num_erases.load(), 0); + EXPECT_EQ(info.size.load(), 0); + RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength); + EXPECT_EQ(info.size.load(), 1); + RecordEraseSlow(&info); + EXPECT_EQ(info.size.load(), 0); + EXPECT_EQ(info.num_erases.load(), 1); +} + +TEST(HashtablezInfoTest, RecordRehash) { + HashtablezInfo info; + absl::MutexLock l(&info.init_mu); + info.PrepareForSampling(); + RecordInsertSlow(&info, 0x1, 0); + RecordInsertSlow(&info, 0x2, kProbeLength); + RecordInsertSlow(&info, 0x4, kProbeLength); + RecordInsertSlow(&info, 0x8, 2 * kProbeLength); + EXPECT_EQ(info.size.load(), 4); + EXPECT_EQ(info.total_probe_length.load(), 4); + + RecordEraseSlow(&info); + RecordEraseSlow(&info); + EXPECT_EQ(info.size.load(), 2); + EXPECT_EQ(info.total_probe_length.load(), 4); + EXPECT_EQ(info.num_erases.load(), 2); + + RecordRehashSlow(&info, 3 * kProbeLength); + EXPECT_EQ(info.size.load(), 2); + EXPECT_EQ(info.total_probe_length.load(), 3); + EXPECT_EQ(info.num_erases.load(), 0); +} + +TEST(HashtablezSamplerTest, SmallSampleParameter) { + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(100); + + for (int i = 0; i < 1000; ++i) { + int64_t next_sample = 0; + HashtablezInfo* sample = SampleSlow(&next_sample); + EXPECT_GT(next_sample, 0); + EXPECT_NE(sample, nullptr); + UnsampleSlow(sample); + } +} + +TEST(HashtablezSamplerTest, LargeSampleParameter) { + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max()); + + for (int i = 0; i < 1000; ++i) { + int64_t next_sample = 0; + HashtablezInfo* sample = SampleSlow(&next_sample); + EXPECT_GT(next_sample, 0); + EXPECT_NE(sample, nullptr); + UnsampleSlow(sample); + } +} + +TEST(HashtablezSamplerTest, Sample) { + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(100); + int64_t num_sampled = 0; + int64_t total = 0; + double sample_rate = 0.0; + for (int i = 0; i < 1000000; ++i) { + HashtablezInfoHandle h = Sample(); + ++total; + if (HashtablezInfoHandlePeer::IsSampled(h)) { + ++num_sampled; + } + sample_rate = static_cast<double>(num_sampled) / total; + if (0.005 < sample_rate && sample_rate < 0.015) break; + } + EXPECT_NEAR(sample_rate, 0.01, 0.005); +} + +TEST(HashtablezSamplerTest, Handle) { + auto& sampler = HashtablezSampler::Global(); + HashtablezInfoHandle h(sampler.Register()); + auto* info = HashtablezInfoHandlePeer::GetInfo(&h); + info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed); + + bool found = false; + sampler.Iterate([&](const HashtablezInfo& h) { + if (&h == info) { + EXPECT_EQ(h.hashes_bitwise_and.load(), 0x12345678); + found = true; + } + }); + EXPECT_TRUE(found); + + h = HashtablezInfoHandle(); + found = false; + sampler.Iterate([&](const HashtablezInfo& h) { + if (&h == info) { + // this will only happen if some other thread has resurrected the info + // the old handle was using. + if (h.hashes_bitwise_and.load() == 0x12345678) { + found = true; + } + } + }); + EXPECT_FALSE(found); +} + +TEST(HashtablezSamplerTest, Registration) { + HashtablezSampler sampler; + auto* info1 = Register(&sampler, 1); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1)); + + auto* info2 = Register(&sampler, 2); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2)); + info1->size.store(3); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2)); + + sampler.Unregister(info1); + sampler.Unregister(info2); +} + +TEST(HashtablezSamplerTest, Unregistration) { + HashtablezSampler sampler; + std::vector<HashtablezInfo*> infos; + for (size_t i = 0; i < 3; ++i) { + infos.push_back(Register(&sampler, i)); + } + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2)); + + sampler.Unregister(infos[1]); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2)); + + infos.push_back(Register(&sampler, 3)); + infos.push_back(Register(&sampler, 4)); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4)); + sampler.Unregister(infos[3]); + EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4)); + + sampler.Unregister(infos[0]); + sampler.Unregister(infos[2]); + sampler.Unregister(infos[4]); + EXPECT_THAT(GetSizes(&sampler), IsEmpty()); +} + +TEST(HashtablezSamplerTest, MultiThreaded) { + HashtablezSampler sampler; + Notification stop; + ThreadPool pool(10); + + for (int i = 0; i < 10; ++i) { + pool.Schedule([&sampler, &stop]() { + std::random_device rd; + std::mt19937 gen(rd()); + + std::vector<HashtablezInfo*> infoz; + while (!stop.HasBeenNotified()) { + if (infoz.empty()) { + infoz.push_back(sampler.Register()); + } + switch (std::uniform_int_distribution<>(0, 2)(gen)) { + case 0: { + infoz.push_back(sampler.Register()); + break; + } + case 1: { + size_t p = + std::uniform_int_distribution<>(0, infoz.size() - 1)(gen); + HashtablezInfo* info = infoz[p]; + infoz[p] = infoz.back(); + infoz.pop_back(); + sampler.Unregister(info); + break; + } + case 2: { + absl::Duration oldest = absl::ZeroDuration(); + sampler.Iterate([&](const HashtablezInfo& info) { + oldest = std::max(oldest, absl::Now() - info.create_time); + }); + ASSERT_GE(oldest, absl::ZeroDuration()); + break; + } + } + } + }); + } + // The threads will hammer away. Give it a little bit of time for tsan to + // spot errors. + absl::SleepFor(absl::Seconds(3)); + stop.Notify(); +} + +TEST(HashtablezSamplerTest, Callback) { + HashtablezSampler sampler; + + auto* info1 = Register(&sampler, 1); + auto* info2 = Register(&sampler, 2); + + static const HashtablezInfo* expected; + + auto callback = [](const HashtablezInfo& info) { + // We can't use `info` outside of this callback because the object will be + // disposed as soon as we return from here. + EXPECT_EQ(&info, expected); + }; + + // Set the callback. + EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr); + expected = info1; + sampler.Unregister(info1); + + // Unset the callback. + EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr)); + expected = nullptr; // no more calls. + sampler.Unregister(info2); +} + +} // namespace +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/container/internal/have_sse.h b/absl/container/internal/have_sse.h new file mode 100644 index 00000000..43414418 --- /dev/null +++ b/absl/container/internal/have_sse.h @@ -0,0 +1,49 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Shared config probing for SSE instructions used in Swiss tables. +#ifndef ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ +#define ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ + +#ifndef SWISSTABLE_HAVE_SSE2 +#if defined(__SSE2__) || \ + (defined(_MSC_VER) && \ + (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2))) +#define SWISSTABLE_HAVE_SSE2 1 +#else +#define SWISSTABLE_HAVE_SSE2 0 +#endif +#endif + +#ifndef SWISSTABLE_HAVE_SSSE3 +#ifdef __SSSE3__ +#define SWISSTABLE_HAVE_SSSE3 1 +#else +#define SWISSTABLE_HAVE_SSSE3 0 +#endif +#endif + +#if SWISSTABLE_HAVE_SSSE3 && !SWISSTABLE_HAVE_SSE2 +#error "Bad configuration!" +#endif + +#if SWISSTABLE_HAVE_SSE2 +#include <emmintrin.h> +#endif + +#if SWISSTABLE_HAVE_SSSE3 +#include <tmmintrin.h> +#endif + +#endif // ABSL_CONTAINER_INTERNAL_HAVE_SSE_H_ diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h new file mode 100644 index 00000000..123e04c9 --- /dev/null +++ b/absl/container/internal/inlined_vector.h @@ -0,0 +1,895 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ +#define ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ + +#include <algorithm> +#include <cstddef> +#include <cstring> +#include <iterator> +#include <limits> +#include <memory> +#include <utility> + +#include "absl/base/macros.h" +#include "absl/container/internal/compressed_tuple.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/types/span.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace inlined_vector_internal { + +template <typename Iterator> +using IsAtLeastForwardIterator = std::is_convertible< + typename std::iterator_traits<Iterator>::iterator_category, + std::forward_iterator_tag>; + +template <typename AllocatorType> +using IsMemcpyOk = absl::conjunction< + std::is_same<std::allocator<typename AllocatorType::value_type>, + AllocatorType>, + absl::is_trivially_copy_constructible<typename AllocatorType::value_type>, + absl::is_trivially_copy_assignable<typename AllocatorType::value_type>, + absl::is_trivially_destructible<typename AllocatorType::value_type>>; + +template <typename AllocatorType, typename ValueType, typename SizeType> +void DestroyElements(AllocatorType* alloc_ptr, ValueType* destroy_first, + SizeType destroy_size) { + using AllocatorTraits = absl::allocator_traits<AllocatorType>; + + if (destroy_first != nullptr) { + for (auto i = destroy_size; i != 0;) { + --i; + AllocatorTraits::destroy(*alloc_ptr, destroy_first + i); + } + +#ifndef NDEBUG + // Overwrite unused memory with `0xab` so we can catch uninitialized usage. + // + // Cast to `void*` to tell the compiler that we don't care that we might be + // scribbling on a vtable pointer. + auto* memory_ptr = static_cast<void*>(destroy_first); + auto memory_size = sizeof(ValueType) * destroy_size; + std::memset(memory_ptr, 0xab, memory_size); +#endif // NDEBUG + } +} + +template <typename AllocatorType, typename ValueType, typename ValueAdapter, + typename SizeType> +void ConstructElements(AllocatorType* alloc_ptr, ValueType* construct_first, + ValueAdapter* values_ptr, SizeType construct_size) { + for (SizeType i = 0; i < construct_size; ++i) { + ABSL_INTERNAL_TRY { + values_ptr->ConstructNext(alloc_ptr, construct_first + i); + } + ABSL_INTERNAL_CATCH_ANY { + inlined_vector_internal::DestroyElements(alloc_ptr, construct_first, i); + ABSL_INTERNAL_RETHROW; + } + } +} + +template <typename ValueType, typename ValueAdapter, typename SizeType> +void AssignElements(ValueType* assign_first, ValueAdapter* values_ptr, + SizeType assign_size) { + for (SizeType i = 0; i < assign_size; ++i) { + values_ptr->AssignNext(assign_first + i); + } +} + +template <typename AllocatorType> +struct StorageView { + using pointer = typename AllocatorType::pointer; + using size_type = typename AllocatorType::size_type; + + pointer data; + size_type size; + size_type capacity; +}; + +template <typename AllocatorType, typename Iterator> +class IteratorValueAdapter { + using pointer = typename AllocatorType::pointer; + using AllocatorTraits = absl::allocator_traits<AllocatorType>; + + public: + explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} + + void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at, *it_); + ++it_; + } + + void AssignNext(pointer assign_at) { + *assign_at = *it_; + ++it_; + } + + private: + Iterator it_; +}; + +template <typename AllocatorType> +class CopyValueAdapter { + using pointer = typename AllocatorType::pointer; + using const_pointer = typename AllocatorType::const_pointer; + using const_reference = typename AllocatorType::const_reference; + using AllocatorTraits = absl::allocator_traits<AllocatorType>; + + public: + explicit CopyValueAdapter(const_reference v) : ptr_(std::addressof(v)) {} + + void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_); + } + + void AssignNext(pointer assign_at) { *assign_at = *ptr_; } + + private: + const_pointer ptr_; +}; + +template <typename AllocatorType> +class DefaultValueAdapter { + using pointer = typename AllocatorType::pointer; + using value_type = typename AllocatorType::value_type; + using AllocatorTraits = absl::allocator_traits<AllocatorType>; + + public: + explicit DefaultValueAdapter() {} + + void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + AllocatorTraits::construct(*alloc_ptr, construct_at); + } + + void AssignNext(pointer assign_at) { *assign_at = value_type(); } +}; + +template <typename AllocatorType> +class AllocationTransaction { + using value_type = typename AllocatorType::value_type; + using pointer = typename AllocatorType::pointer; + using size_type = typename AllocatorType::size_type; + using AllocatorTraits = absl::allocator_traits<AllocatorType>; + + public: + explicit AllocationTransaction(AllocatorType* alloc_ptr) + : alloc_data_(*alloc_ptr, nullptr) {} + + ~AllocationTransaction() { + if (DidAllocate()) { + AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity()); + } + } + + AllocationTransaction(const AllocationTransaction&) = delete; + void operator=(const AllocationTransaction&) = delete; + + AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } + pointer& GetData() { return alloc_data_.template get<1>(); } + size_type& GetCapacity() { return capacity_; } + + bool DidAllocate() { return GetData() != nullptr; } + pointer Allocate(size_type capacity) { + GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); + GetCapacity() = capacity; + return GetData(); + } + + private: + container_internal::CompressedTuple<AllocatorType, pointer> alloc_data_; + size_type capacity_ = 0; +}; + +template <typename AllocatorType> +class ConstructionTransaction { + using pointer = typename AllocatorType::pointer; + using size_type = typename AllocatorType::size_type; + + public: + explicit ConstructionTransaction(AllocatorType* alloc_ptr) + : alloc_data_(*alloc_ptr, nullptr) {} + + ~ConstructionTransaction() { + if (DidConstruct()) { + inlined_vector_internal::DestroyElements(std::addressof(GetAllocator()), + GetData(), GetSize()); + } + } + + ConstructionTransaction(const ConstructionTransaction&) = delete; + void operator=(const ConstructionTransaction&) = delete; + + AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } + pointer& GetData() { return alloc_data_.template get<1>(); } + size_type& GetSize() { return size_; } + + bool DidConstruct() { return GetData() != nullptr; } + template <typename ValueAdapter> + void Construct(pointer data, ValueAdapter* values_ptr, size_type size) { + inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()), + data, values_ptr, size); + GetData() = data; + GetSize() = size; + } + void Commit() { + GetData() = nullptr; + GetSize() = 0; + } + + private: + container_internal::CompressedTuple<AllocatorType, pointer> alloc_data_; + size_type size_ = 0; +}; + +template <typename T, size_t N, typename A> +class Storage { + public: + using allocator_type = A; + using value_type = typename allocator_type::value_type; + using pointer = typename allocator_type::pointer; + using const_pointer = typename allocator_type::const_pointer; + using reference = typename allocator_type::reference; + using const_reference = typename allocator_type::const_reference; + using rvalue_reference = typename allocator_type::value_type&&; + using size_type = typename allocator_type::size_type; + using difference_type = typename allocator_type::difference_type; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + using MoveIterator = std::move_iterator<iterator>; + using AllocatorTraits = absl::allocator_traits<allocator_type>; + using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<allocator_type>; + + using StorageView = inlined_vector_internal::StorageView<allocator_type>; + + template <typename Iterator> + using IteratorValueAdapter = + inlined_vector_internal::IteratorValueAdapter<allocator_type, Iterator>; + using CopyValueAdapter = + inlined_vector_internal::CopyValueAdapter<allocator_type>; + using DefaultValueAdapter = + inlined_vector_internal::DefaultValueAdapter<allocator_type>; + + using AllocationTransaction = + inlined_vector_internal::AllocationTransaction<allocator_type>; + using ConstructionTransaction = + inlined_vector_internal::ConstructionTransaction<allocator_type>; + + static size_type NextCapacity(size_type current_capacity) { + return current_capacity * 2; + } + + static size_type ComputeCapacity(size_type current_capacity, + size_type requested_capacity) { + return (std::max)(NextCapacity(current_capacity), requested_capacity); + } + + // --------------------------------------------------------------------------- + // Storage Constructors and Destructor + // --------------------------------------------------------------------------- + + Storage() : metadata_() {} + + explicit Storage(const allocator_type& alloc) + : metadata_(alloc, /* empty and inlined */ 0) {} + + ~Storage() { + pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); + inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize()); + DeallocateIfAllocated(); + } + + // --------------------------------------------------------------------------- + // Storage Member Accessors + // --------------------------------------------------------------------------- + + size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } + + const size_type& GetSizeAndIsAllocated() const { + return metadata_.template get<1>(); + } + + size_type GetSize() const { return GetSizeAndIsAllocated() >> 1; } + + bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } + + pointer GetAllocatedData() { return data_.allocated.allocated_data; } + + const_pointer GetAllocatedData() const { + return data_.allocated.allocated_data; + } + + pointer GetInlinedData() { + return reinterpret_cast<pointer>( + std::addressof(data_.inlined.inlined_data[0])); + } + + const_pointer GetInlinedData() const { + return reinterpret_cast<const_pointer>( + std::addressof(data_.inlined.inlined_data[0])); + } + + size_type GetAllocatedCapacity() const { + return data_.allocated.allocated_capacity; + } + + size_type GetInlinedCapacity() const { return static_cast<size_type>(N); } + + StorageView MakeStorageView() { + return GetIsAllocated() + ? StorageView{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()} + : StorageView{GetInlinedData(), GetSize(), GetInlinedCapacity()}; + } + + allocator_type* GetAllocPtr() { + return std::addressof(metadata_.template get<0>()); + } + + const allocator_type* GetAllocPtr() const { + return std::addressof(metadata_.template get<0>()); + } + + // --------------------------------------------------------------------------- + // Storage Member Mutators + // --------------------------------------------------------------------------- + + template <typename ValueAdapter> + void Initialize(ValueAdapter values, size_type new_size); + + template <typename ValueAdapter> + void Assign(ValueAdapter values, size_type new_size); + + template <typename ValueAdapter> + void Resize(ValueAdapter values, size_type new_size); + + template <typename ValueAdapter> + iterator Insert(const_iterator pos, ValueAdapter values, + size_type insert_count); + + template <typename... Args> + reference EmplaceBack(Args&&... args); + + iterator Erase(const_iterator from, const_iterator to); + + void Reserve(size_type requested_capacity); + + void ShrinkToFit(); + + void Swap(Storage* other_storage_ptr); + + void SetIsAllocated() { + GetSizeAndIsAllocated() |= static_cast<size_type>(1); + } + + void UnsetIsAllocated() { + GetSizeAndIsAllocated() &= ((std::numeric_limits<size_type>::max)() - 1); + } + + void SetSize(size_type size) { + GetSizeAndIsAllocated() = + (size << 1) | static_cast<size_type>(GetIsAllocated()); + } + + void SetAllocatedSize(size_type size) { + GetSizeAndIsAllocated() = (size << 1) | static_cast<size_type>(1); + } + + void SetInlinedSize(size_type size) { + GetSizeAndIsAllocated() = size << static_cast<size_type>(1); + } + + void AddSize(size_type count) { + GetSizeAndIsAllocated() += count << static_cast<size_type>(1); + } + + void SubtractSize(size_type count) { + assert(count <= GetSize()); + + GetSizeAndIsAllocated() -= count << static_cast<size_type>(1); + } + + void SetAllocatedData(pointer data, size_type capacity) { + data_.allocated.allocated_data = data; + data_.allocated.allocated_capacity = capacity; + } + + void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) { + SetAllocatedData(allocation_tx_ptr->GetData(), + allocation_tx_ptr->GetCapacity()); + allocation_tx_ptr->GetData() = nullptr; + allocation_tx_ptr->GetCapacity() = 0; + } + + void MemcpyFrom(const Storage& other_storage) { + assert(IsMemcpyOk::value || other_storage.GetIsAllocated()); + + GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); + data_ = other_storage.data_; + } + + void DeallocateIfAllocated() { + if (GetIsAllocated()) { + AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(), + GetAllocatedCapacity()); + } + } + + private: + using Metadata = + container_internal::CompressedTuple<allocator_type, size_type>; + + struct Allocated { + pointer allocated_data; + size_type allocated_capacity; + }; + + struct Inlined { + using InlinedDataElement = + absl::aligned_storage_t<sizeof(value_type), alignof(value_type)>; + InlinedDataElement inlined_data[N]; + }; + + union Data { + Allocated allocated; + Inlined inlined; + }; + + Metadata metadata_; + Data data_; +}; + +template <typename T, size_t N, typename A> +template <typename ValueAdapter> +auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size) + -> void { + // Only callable from constructors! + assert(!GetIsAllocated()); + assert(GetSize() == 0); + + pointer construct_data; + + if (new_size > GetInlinedCapacity()) { + // Because this is only called from the `InlinedVector` constructors, it's + // safe to take on the allocation with size `0`. If `ConstructElements(...)` + // throws, deallocation will be automatically handled by `~Storage()`. + size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); + pointer new_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); + + SetAllocatedData(new_data, new_capacity); + SetIsAllocated(); + + construct_data = new_data; + } else { + construct_data = GetInlinedData(); + } + + inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, + &values, new_size); + + // Since the initial size was guaranteed to be `0` and the allocated bit is + // already correct for either case, *adding* `new_size` gives us the correct + // result faster than setting it directly. + AddSize(new_size); +} + +template <typename T, size_t N, typename A> +template <typename ValueAdapter> +auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocPtr()); + + absl::Span<value_type> assign_loop; + absl::Span<value_type> construct_loop; + absl::Span<value_type> destroy_loop; + + if (new_size > storage_view.capacity) { + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + + construct_loop = {new_data, new_size}; + destroy_loop = {storage_view.data, storage_view.size}; + } else if (new_size > storage_view.size) { + assign_loop = {storage_view.data, storage_view.size}; + construct_loop = {storage_view.data + storage_view.size, + new_size - storage_view.size}; + } else { + assign_loop = {storage_view.data, new_size}; + destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; + } + + inlined_vector_internal::AssignElements(assign_loop.data(), &values, + assign_loop.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), construct_loop.data(), &values, construct_loop.size()); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), + destroy_loop.size()); + + if (allocation_tx.DidAllocate()) { + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + SetSize(new_size); +} + +template <typename T, size_t N, typename A> +template <typename ValueAdapter> +auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocPtr()); + ConstructionTransaction construction_tx(GetAllocPtr()); + + IteratorValueAdapter<MoveIterator> move_values( + MoveIterator(storage_view.data)); + + absl::Span<value_type> construct_loop; + absl::Span<value_type> move_construct_loop; + absl::Span<value_type> destroy_loop; + + if (new_size > storage_view.capacity) { + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + construct_loop = {new_data + storage_view.size, + new_size - storage_view.size}; + move_construct_loop = {new_data, storage_view.size}; + destroy_loop = {storage_view.data, storage_view.size}; + } else if (new_size > storage_view.size) { + construct_loop = {storage_view.data + storage_view.size, + new_size - storage_view.size}; + } else { + destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; + } + + construction_tx.Construct(construct_loop.data(), &values, + construct_loop.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), move_construct_loop.data(), &move_values, + move_construct_loop.size()); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), + destroy_loop.size()); + + construction_tx.Commit(); + if (allocation_tx.DidAllocate()) { + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + SetSize(new_size); +} + +template <typename T, size_t N, typename A> +template <typename ValueAdapter> +auto Storage<T, N, A>::Insert(const_iterator pos, ValueAdapter values, + size_type insert_count) -> iterator { + StorageView storage_view = MakeStorageView(); + + size_type insert_index = + std::distance(const_iterator(storage_view.data), pos); + size_type insert_end_index = insert_index + insert_count; + size_type new_size = storage_view.size + insert_count; + + if (new_size > storage_view.capacity) { + AllocationTransaction allocation_tx(GetAllocPtr()); + ConstructionTransaction construction_tx(GetAllocPtr()); + ConstructionTransaction move_construciton_tx(GetAllocPtr()); + + IteratorValueAdapter<MoveIterator> move_values( + MoveIterator(storage_view.data)); + + size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); + pointer new_data = allocation_tx.Allocate(new_capacity); + + construction_tx.Construct(new_data + insert_index, &values, insert_count); + + move_construciton_tx.Construct(new_data, &move_values, insert_index); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), new_data + insert_end_index, &move_values, + storage_view.size - insert_index); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + construction_tx.Commit(); + move_construciton_tx.Commit(); + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + + SetAllocatedSize(new_size); + return iterator(new_data + insert_index); + } else { + size_type move_construction_destination_index = + (std::max)(insert_end_index, storage_view.size); + + ConstructionTransaction move_construction_tx(GetAllocPtr()); + + IteratorValueAdapter<MoveIterator> move_construction_values( + MoveIterator(storage_view.data + + (move_construction_destination_index - insert_count))); + absl::Span<value_type> move_construction = { + storage_view.data + move_construction_destination_index, + new_size - move_construction_destination_index}; + + pointer move_assignment_values = storage_view.data + insert_index; + absl::Span<value_type> move_assignment = { + storage_view.data + insert_end_index, + move_construction_destination_index - insert_end_index}; + + absl::Span<value_type> insert_assignment = {move_assignment_values, + move_construction.size()}; + + absl::Span<value_type> insert_construction = { + insert_assignment.data() + insert_assignment.size(), + insert_count - insert_assignment.size()}; + + move_construction_tx.Construct(move_construction.data(), + &move_construction_values, + move_construction.size()); + + for (pointer destination = move_assignment.data() + move_assignment.size(), + last_destination = move_assignment.data(), + source = move_assignment_values + move_assignment.size(); + ;) { + --destination; + --source; + if (destination < last_destination) break; + *destination = std::move(*source); + } + + inlined_vector_internal::AssignElements(insert_assignment.data(), &values, + insert_assignment.size()); + + inlined_vector_internal::ConstructElements( + GetAllocPtr(), insert_construction.data(), &values, + insert_construction.size()); + + move_construction_tx.Commit(); + + AddSize(insert_count); + return iterator(storage_view.data + insert_index); + } +} + +template <typename T, size_t N, typename A> +template <typename... Args> +auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference { + StorageView storage_view = MakeStorageView(); + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter<MoveIterator> move_values( + MoveIterator(storage_view.data)); + + pointer construct_data; + + if (storage_view.size == storage_view.capacity) { + size_type new_capacity = NextCapacity(storage_view.capacity); + pointer new_data = allocation_tx.Allocate(new_capacity); + + construct_data = new_data; + } else { + construct_data = storage_view.data; + } + + pointer end = construct_data + storage_view.size; + + AllocatorTraits::construct(*GetAllocPtr(), end, std::forward<Args>(args)...); + + if (allocation_tx.DidAllocate()) { + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements( + GetAllocPtr(), allocation_tx.GetData(), &move_values, + storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + AllocatorTraits::destroy(*GetAllocPtr(), end); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); + } + + AddSize(1); + return *end; +} + +template <typename T, size_t N, typename A> +auto Storage<T, N, A>::Erase(const_iterator from, const_iterator to) + -> iterator { + assert(from != to); + + StorageView storage_view = MakeStorageView(); + + size_type erase_size = std::distance(from, to); + size_type erase_index = + std::distance(const_iterator(storage_view.data), from); + size_type erase_end_index = erase_index + erase_size; + + IteratorValueAdapter<MoveIterator> move_values( + MoveIterator(storage_view.data + erase_end_index)); + + inlined_vector_internal::AssignElements(storage_view.data + erase_index, + &move_values, + storage_view.size - erase_end_index); + + inlined_vector_internal::DestroyElements( + GetAllocPtr(), storage_view.data + (storage_view.size - erase_size), + erase_size); + + SubtractSize(erase_size); + return iterator(storage_view.data + erase_index); +} + +template <typename T, size_t N, typename A> +auto Storage<T, N, A>::Reserve(size_type requested_capacity) -> void { + StorageView storage_view = MakeStorageView(); + + if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter<MoveIterator> move_values( + MoveIterator(storage_view.data)); + + size_type new_capacity = + ComputeCapacity(storage_view.capacity, requested_capacity); + pointer new_data = allocation_tx.Allocate(new_capacity); + + inlined_vector_internal::ConstructElements(GetAllocPtr(), new_data, + &move_values, storage_view.size); + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + DeallocateIfAllocated(); + AcquireAllocatedData(&allocation_tx); + SetIsAllocated(); +} + +template <typename T, size_t N, typename A> +auto Storage<T, N, A>::ShrinkToFit() -> void { + // May only be called on allocated instances! + assert(GetIsAllocated()); + + StorageView storage_view{GetAllocatedData(), GetSize(), + GetAllocatedCapacity()}; + + if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; + + AllocationTransaction allocation_tx(GetAllocPtr()); + + IteratorValueAdapter<MoveIterator> move_values( + MoveIterator(storage_view.data)); + + pointer construct_data; + + if (storage_view.size > GetInlinedCapacity()) { + size_type new_capacity = storage_view.size; + pointer new_data = allocation_tx.Allocate(new_capacity); + + construct_data = new_data; + } else { + construct_data = GetInlinedData(); + } + + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, + &move_values, storage_view.size); + } + ABSL_INTERNAL_CATCH_ANY { + SetAllocatedData(storage_view.data, storage_view.capacity); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, + storage_view.size); + + AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data, + storage_view.capacity); + + if (allocation_tx.DidAllocate()) { + AcquireAllocatedData(&allocation_tx); + } else { + UnsetIsAllocated(); + } +} + +template <typename T, size_t N, typename A> +auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { + using std::swap; + assert(this != other_storage_ptr); + + if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { + swap(data_.allocated, other_storage_ptr->data_.allocated); + } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) { + Storage* small_ptr = this; + Storage* large_ptr = other_storage_ptr; + if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); + + for (size_type i = 0; i < small_ptr->GetSize(); ++i) { + swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); + } + + IteratorValueAdapter<MoveIterator> move_values( + MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize())); + + inlined_vector_internal::ConstructElements( + large_ptr->GetAllocPtr(), + small_ptr->GetInlinedData() + small_ptr->GetSize(), &move_values, + large_ptr->GetSize() - small_ptr->GetSize()); + + inlined_vector_internal::DestroyElements( + large_ptr->GetAllocPtr(), + large_ptr->GetInlinedData() + small_ptr->GetSize(), + large_ptr->GetSize() - small_ptr->GetSize()); + } else { + Storage* allocated_ptr = this; + Storage* inlined_ptr = other_storage_ptr; + if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); + + StorageView allocated_storage_view{allocated_ptr->GetAllocatedData(), + allocated_ptr->GetSize(), + allocated_ptr->GetAllocatedCapacity()}; + + IteratorValueAdapter<MoveIterator> move_values( + MoveIterator(inlined_ptr->GetInlinedData())); + + ABSL_INTERNAL_TRY { + inlined_vector_internal::ConstructElements( + inlined_ptr->GetAllocPtr(), allocated_ptr->GetInlinedData(), + &move_values, inlined_ptr->GetSize()); + } + ABSL_INTERNAL_CATCH_ANY { + allocated_ptr->SetAllocatedData(allocated_storage_view.data, + allocated_storage_view.capacity); + ABSL_INTERNAL_RETHROW; + } + + inlined_vector_internal::DestroyElements(inlined_ptr->GetAllocPtr(), + inlined_ptr->GetInlinedData(), + inlined_ptr->GetSize()); + + inlined_ptr->SetAllocatedData(allocated_storage_view.data, + allocated_storage_view.capacity); + } + + swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); + swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr()); +} + +} // namespace inlined_vector_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ diff --git a/absl/container/internal/layout.h b/absl/container/internal/layout.h index f11a6ad2..3924b8aa 100644 --- a/absl/container/internal/layout.h +++ b/absl/container/internal/layout.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -188,7 +188,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // A type wrapper that instructs `Layout` to use the specific alignment for the @@ -644,7 +644,8 @@ class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>, std::string DebugString() const { const auto offsets = Offsets(); const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>()...}; - const std::string types[] = {adl_barrier::TypeName<ElementType<OffsetSeq>>()...}; + const std::string types[] = { + adl_barrier::TypeName<ElementType<OffsetSeq>>()...}; std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); for (size_t i = 0; i != NumOffsets - 1; ++i) { absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1], @@ -734,7 +735,7 @@ class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ diff --git a/absl/container/internal/layout_test.cc b/absl/container/internal/layout_test.cc index b9f98471..44d84607 100644 --- a/absl/container/internal/layout_test.cc +++ b/absl/container/internal/layout_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/types/span.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -46,7 +46,7 @@ Expected Type(Actual val) { return val; } -// Helper class to test different size and alignments. +// Helper classes to test different size and alignments. struct alignas(8) Int128 { uint64_t a, b; friend bool operator==(Int128 lhs, Int128 rhs) { @@ -58,6 +58,14 @@ struct alignas(8) Int128 { } }; +// int64_t is *not* 8-byte aligned on all platforms! +struct alignas(8) Int64 { + int64_t a; + friend bool operator==(Int64 lhs, Int64 rhs) { + return lhs.a == rhs.a; + } +}; + // Properties of types that this test relies on. static_assert(sizeof(int8_t) == 1, ""); static_assert(alignof(int8_t) == 1, ""); @@ -65,6 +73,8 @@ static_assert(sizeof(int16_t) == 2, ""); static_assert(alignof(int16_t) == 2, ""); static_assert(sizeof(int32_t) == 4, ""); static_assert(alignof(int32_t) == 4, ""); +static_assert(sizeof(Int64) == 8, ""); +static_assert(alignof(Int64) == 8, ""); static_assert(sizeof(Int128) == 16, ""); static_assert(alignof(Int128) == 8, ""); @@ -1282,14 +1292,14 @@ TEST(Layout, OverAligned) { TEST(Layout, Alignment) { static_assert(Layout<int8_t>::Alignment() == 1, ""); static_assert(Layout<int32_t>::Alignment() == 4, ""); - static_assert(Layout<int64_t>::Alignment() == 8, ""); + static_assert(Layout<Int64>::Alignment() == 8, ""); static_assert(Layout<Aligned<int8_t, 64>>::Alignment() == 64, ""); - static_assert(Layout<int8_t, int32_t, int64_t>::Alignment() == 8, ""); - static_assert(Layout<int8_t, int64_t, int32_t>::Alignment() == 8, ""); - static_assert(Layout<int32_t, int8_t, int64_t>::Alignment() == 8, ""); - static_assert(Layout<int32_t, int64_t, int8_t>::Alignment() == 8, ""); - static_assert(Layout<int64_t, int8_t, int32_t>::Alignment() == 8, ""); - static_assert(Layout<int64_t, int32_t, int8_t>::Alignment() == 8, ""); + static_assert(Layout<int8_t, int32_t, Int64>::Alignment() == 8, ""); + static_assert(Layout<int8_t, Int64, int32_t>::Alignment() == 8, ""); + static_assert(Layout<int32_t, int8_t, Int64>::Alignment() == 8, ""); + static_assert(Layout<int32_t, Int64, int8_t>::Alignment() == 8, ""); + static_assert(Layout<Int64, int8_t, int32_t>::Alignment() == 8, ""); + static_assert(Layout<Int64, int32_t, int8_t>::Alignment() == 8, ""); } TEST(Layout, ConstexprPartial) { @@ -1324,7 +1334,7 @@ void ExpectPoisoned(const unsigned char (&buf)[N], } TEST(Layout, PoisonPadding) { - using L = Layout<int8_t, int64_t, int32_t, Int128>; + using L = Layout<int8_t, Int64, int32_t, Int128>; constexpr size_t n = L::Partial(1, 2, 3, 4).AllocSize(); { @@ -1553,5 +1563,5 @@ TEST(CompactString, Works) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/node_hash_policy.h b/absl/container/internal/node_hash_policy.h index e8d89f63..d7581360 100644 --- a/absl/container/internal/node_hash_policy.h +++ b/absl/container/internal/node_hash_policy.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -40,7 +40,7 @@ #include <utility> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class Reference, class Policy> @@ -84,7 +84,7 @@ struct node_hash_policy { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_NODE_HASH_POLICY_H_ diff --git a/absl/container/internal/node_hash_policy_test.cc b/absl/container/internal/node_hash_policy_test.cc index a73c7bba..d53b7364 100644 --- a/absl/container/internal/node_hash_policy_test.cc +++ b/absl/container/internal/node_hash_policy_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,7 @@ #include "absl/container/internal/hash_policy_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -65,5 +65,5 @@ TEST_F(NodeTest, transfer) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h index 53d4619a..00caa373 100644 --- a/absl/container/internal/raw_hash_map.h +++ b/absl/container/internal/raw_hash_map.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,11 +19,12 @@ #include <type_traits> #include <utility> +#include "absl/base/internal/throw_delegate.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class Policy, class Hash, class Eq, class Alloc> @@ -40,8 +41,8 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> { using MappedConstReference = decltype(P::value( std::addressof(std::declval<typename raw_hash_map::const_reference>()))); - using KeyArgImpl = container_internal::KeyArg<IsTransparent<Eq>::value && - IsTransparent<Hash>::value>; + using KeyArgImpl = + KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>; public: using key_type = typename Policy::key_type; @@ -137,14 +138,20 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> { template <class K = key_type, class P = Policy> MappedReference<P> at(const key_arg<K>& key) { auto it = this->find(key); - if (it == this->end()) std::abort(); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } return Policy::value(&*it); } template <class K = key_type, class P = Policy> MappedConstReference<P> at(const key_arg<K>& key) const { auto it = this->find(key); - if (it == this->end()) std::abort(); + if (it == this->end()) { + base_internal::ThrowStdOutOfRange( + "absl::container_internal::raw_hash_map<>::at"); + } return Policy::value(&*it); } @@ -181,7 +188,7 @@ class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index 4e690dac..02e74e21 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { constexpr size_t Group::kWidth; @@ -44,5 +44,5 @@ bool ShouldInsertBackwards(size_t hash, ctrl_t* ctrl) { } } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 0c42e4ae..7b379d4f 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -91,36 +91,6 @@ #ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ #define ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ -#ifndef SWISSTABLE_HAVE_SSE2 -#if defined(__SSE2__) || \ - (defined(_MSC_VER) && \ - (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2))) -#define SWISSTABLE_HAVE_SSE2 1 -#else -#define SWISSTABLE_HAVE_SSE2 0 -#endif -#endif - -#ifndef SWISSTABLE_HAVE_SSSE3 -#ifdef __SSSE3__ -#define SWISSTABLE_HAVE_SSSE3 1 -#else -#define SWISSTABLE_HAVE_SSSE3 0 -#endif -#endif - -#if SWISSTABLE_HAVE_SSSE3 && !SWISSTABLE_HAVE_SSE2 -#error "Bad configuration!" -#endif - -#if SWISSTABLE_HAVE_SSE2 -#include <emmintrin.h> -#endif - -#if SWISSTABLE_HAVE_SSSE3 -#include <tmmintrin.h> -#endif - #include <algorithm> #include <cmath> #include <cstdint> @@ -135,18 +105,20 @@ #include "absl/base/internal/bits.h" #include "absl/base/internal/endian.h" #include "absl/base/port.h" +#include "absl/container/internal/common.h" #include "absl/container/internal/compressed_tuple.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/hash_policy_traits.h" #include "absl/container/internal/hashtable_debug_hooks.h" +#include "absl/container/internal/hashtablez_sampler.h" +#include "absl/container/internal/have_sse.h" #include "absl/container/internal/layout.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" -#include "absl/types/optional.h" #include "absl/utility/utility.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <size_t Width> @@ -194,12 +166,6 @@ struct IsDecomposable< std::declval<Ts>()...))>, Policy, Hash, Eq, Ts...> : std::true_type {}; -template <class, class = void> -struct IsTransparent : std::false_type {}; -template <class T> -struct IsTransparent<T, absl::void_t<typename T::is_transparent>> - : std::true_type {}; - // TODO(alkis): Switch to std::is_nothrow_swappable when gcc/clang supports it. template <class T> constexpr bool IsNoThrowSwappable() { @@ -385,7 +351,7 @@ struct GroupSse2Impl { return BitMask<uint32_t, kWidth>( _mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))); #else - return Match(kEmpty); + return Match(static_cast<h2_t>(kEmpty)); #endif } @@ -481,9 +447,7 @@ using Group = GroupPortableImpl; template <class Policy, class Hash, class Eq, class Alloc> class raw_hash_set; -inline bool IsValidCapacity(size_t n) { - return ((n + 1) & n) == 0 && n >= Group::kWidth - 1; -} +inline bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; } // PRECONDITION: // IsValidCapacity(capacity) @@ -505,152 +469,32 @@ inline void ConvertDeletedToEmptyAndFullToDeleted( ctrl[capacity] = kSentinel; } -// Rounds up the capacity to the next power of 2 minus 1 and ensures it is -// greater or equal to Group::kWidth - 1. +// Rounds up the capacity to the next power of 2 minus 1, with a minimum of 1. inline size_t NormalizeCapacity(size_t n) { - constexpr size_t kMinCapacity = Group::kWidth - 1; - return n <= kMinCapacity - ? kMinCapacity - : (std::numeric_limits<size_t>::max)() >> LeadingZeros(n); + return n ? ~size_t{} >> LeadingZeros(n) : 1; } -// The node_handle concept from C++17. -// We specialize node_handle for sets and maps. node_handle_base holds the -// common API of both. -template <typename Policy, typename Alloc> -class node_handle_base { - protected: - using PolicyTraits = hash_policy_traits<Policy>; - 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 <typename, typename, typename, typename> - 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<slot_type*>(std::addressof(slot_space_)); - } - allocator_type* alloc() { return std::addressof(*alloc_); } - - private: - absl::optional<allocator_type> alloc_; - mutable absl::aligned_storage_t<sizeof(slot_type), alignof(slot_type)> - slot_space_; -}; - -// For sets. -template <typename Policy, typename Alloc, typename = void> -class node_handle : public node_handle_base<Policy, Alloc> { - 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 <typename, typename, typename, typename> - friend class raw_hash_set; - - node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {} -}; - -// For maps. -template <typename Policy, typename Alloc> -class node_handle<Policy, Alloc, absl::void_t<typename Policy::mapped_type>> - : public node_handle_base<Policy, Alloc> { - using Base = typename node_handle::node_handle_base; - - public: - using key_type = typename Policy::key_type; - using mapped_type = typename Policy::mapped_type; - - constexpr node_handle() {} - - auto key() const -> decltype(Base::PolicyTraits::key(this->slot())) { - return Base::PolicyTraits::key(this->slot()); - } - - mapped_type& mapped() const { - return Base::PolicyTraits::value( - &Base::PolicyTraits::element(this->slot())); +// We use 7/8th as maximum load factor. +// For 16-wide groups, that gives an average of two empty slots per group. +inline size_t CapacityToGrowth(size_t capacity) { + assert(IsValidCapacity(capacity)); + // `capacity*7/8` + if (Group::kWidth == 8 && capacity == 7) { + // x-x/8 does not work when x==7. + return 6; } - - private: - template <typename, typename, typename, typename> - 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 <class Iterator, class NodeType> -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 <bool is_transparent> -struct KeyArg { - // Transparent. Forward `K`. - template <typename K, typename key_type> - using type = K; -}; - -template <> -struct KeyArg<false> { - // Not transparent. Always use `key_type`. - template <typename K, typename key_type> - using type = key_type; -}; + return capacity - capacity / 8; +} +// From desired "growth" to a lowerbound of the necessary capacity. +// Might not be a valid one and required NormalizeCapacity(). +inline size_t GrowthToLowerboundCapacity(size_t growth) { + // `growth*8/7` + if (Group::kWidth == 8 && growth == 7) { + // x+(x-1)/7 does not work when x==7. + return 8; + } + return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7); +} // Policy: a policy defines how to perform different operations on // the slots of the hashtable (see hash_policy_traits.h for the full interface @@ -666,14 +510,14 @@ struct KeyArg<false> { // if they are equal, false if they are not. If two keys compare equal, then // their hash values as defined by Hash MUST be equal. // -// Allocator: an Allocator [http://devdocs.io/cpp/concept/allocator] with which +// Allocator: an Allocator [https://devdocs.io/cpp/concept/allocator] with which // the storage of the hashtable will be allocated and the elements will be // constructed and destroyed. template <class Policy, class Hash, class Eq, class Alloc> class raw_hash_set { using PolicyTraits = hash_policy_traits<Policy>; - using KeyArgImpl = container_internal::KeyArg<IsTransparent<Eq>::value && - IsTransparent<Hash>::value>; + using KeyArgImpl = + KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>; public: using init_type = typename PolicyTraits::init_type; @@ -814,7 +658,11 @@ class raw_hash_set { } ctrl_t* ctrl_ = nullptr; - slot_type* slot_; + // To avoid uninitialized member warnigs, put slot_ in an anonymous union. + // The member is not initialized on singleton and end iterators. + union { + slot_type* slot_; + }; }; class const_iterator { @@ -854,7 +702,8 @@ class raw_hash_set { iterator inner_; }; - using node_type = container_internal::node_handle<Policy, Alloc>; + using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>; + using insert_return_type = InsertReturnType<iterator, node_type>; raw_hash_set() noexcept( std::is_nothrow_default_constructible<hasher>::value&& @@ -867,7 +716,7 @@ class raw_hash_set { : ctrl_(EmptyGroup()), settings_(0, hash, eq, alloc) { if (bucket_count) { capacity_ = NormalizeCapacity(bucket_count); - growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor); + reset_growth_left(); initialize_slots(); } } @@ -909,8 +758,8 @@ class raw_hash_set { // that accept std::initializer_list<T> and std::initializer_list<init_type>. // This is advantageous for performance. // - // // Turns {"abc", "def"} into std::initializer_list<std::string>, then copies - // // the strings into the set. + // // Turns {"abc", "def"} into std::initializer_list<std::string>, then + // // copies the strings into the set. // std::unordered_set<std::string> s = {"abc", "def"}; // // // Turns {"abc", "def"} into std::initializer_list<const char*>, then @@ -973,9 +822,10 @@ class raw_hash_set { // than a full `insert`. for (const auto& v : that) { const size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, v); - const size_t i = find_first_non_full(hash); - set_ctrl(i, H2(hash)); - emplace_at(i, v); + auto target = find_first_non_full(hash); + set_ctrl(target.offset, H2(hash)); + emplace_at(target.offset, v); + infoz_.RecordInsert(hash, target.probe_length); } size_ = that.size(); growth_left() -= that.size(); @@ -989,6 +839,7 @@ class raw_hash_set { slots_(absl::exchange(that.slots_, nullptr)), size_(absl::exchange(that.size_, 0)), capacity_(absl::exchange(that.capacity_, 0)), + infoz_(absl::exchange(that.infoz_, HashtablezInfoHandle())), // Hash, equality and allocator are copied instead of moved because // `that` must be left valid. If Hash is std::function<Key>, moving it // would create a nullptr functor that cannot be called. @@ -1009,6 +860,7 @@ class raw_hash_set { std::swap(size_, that.size_); std::swap(capacity_, that.capacity_); std::swap(growth_left(), that.growth_left()); + std::swap(infoz_, that.infoz_); } else { reserve(that.size()); // Note: this will copy elements of dense_set and unordered_set instead of @@ -1058,7 +910,7 @@ class raw_hash_set { size_t capacity() const { return capacity_; } size_t max_size() const { return (std::numeric_limits<size_t>::max)(); } - void clear() { + ABSL_ATTRIBUTE_REINITIALIZES void clear() { // Iterating over this container is O(bucket_count()). When bucket_count() // is much greater than size(), iteration becomes prohibitively expensive. // For clear() it is more important to reuse the allocated array when the @@ -1076,9 +928,10 @@ class raw_hash_set { } size_ = 0; reset_ctrl(); - growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor); + reset_growth_left(); } assert(empty()); + infoz_.RecordStorageChanged(0, capacity_); } // This overload kicks in when the argument is an rvalue of insertable and @@ -1117,7 +970,7 @@ class raw_hash_set { // This overload kicks in when the argument is an rvalue of init_type. Its // purpose is to handle brace-init-list arguments. // - // flat_hash_set<std::string, int> s; + // flat_hash_map<std::string, int> s; // s.insert({"abc", 42}); std::pair<iterator, bool> insert(init_type&& value) { return emplace(std::move(value)); @@ -1158,13 +1011,14 @@ class raw_hash_set { insert(ilist.begin(), ilist.end()); } - insert_return_type<iterator, node_type> insert(node_type&& node) { + insert_return_type insert(node_type&& node) { if (!node) return {end(), false, node_type()}; - const auto& elem = PolicyTraits::element(node.slot()); + const auto& elem = PolicyTraits::element(CommonAccess::GetSlot(node)); auto res = PolicyTraits::apply( - InsertSlot<false>{*this, std::move(*node.slot())}, elem); + InsertSlot<false>{*this, std::move(*CommonAccess::GetSlot(node))}, + elem); if (res.second) { - node.reset(); + CommonAccess::Reset(&node); return {res.first, true, node_type()}; } else { return {res.first, false, std::move(node)}; @@ -1328,7 +1182,8 @@ class raw_hash_set { } node_type extract(const_iterator position) { - node_type node(alloc_ref(), position.inner_.slot_); + auto node = + CommonAccess::Transfer<node_type>(alloc_ref(), position.inner_.slot_); erase_meta_only(position); return node; } @@ -1353,6 +1208,7 @@ class raw_hash_set { swap(growth_left(), that.growth_left()); swap(hash_ref(), that.hash_ref()); swap(eq_ref(), that.eq_ref()); + swap(infoz_, that.infoz_); if (AllocTraits::propagate_on_container_swap::value) { swap(alloc_ref(), that.alloc_ref()); } else { @@ -1363,17 +1219,21 @@ class raw_hash_set { void rehash(size_t n) { if (n == 0 && capacity_ == 0) return; - if (n == 0 && size_ == 0) return destroy_slots(); - auto m = NormalizeCapacity(std::max(n, NumSlotsFast(size()))); + if (n == 0 && size_ == 0) { + destroy_slots(); + infoz_.RecordStorageChanged(0, 0); + return; + } + // bitor is a faster way of doing `max` here. We will round up to the next + // power-of-2-minus-1, so bitor is good enough. + auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size())); // n == 0 unconditionally rehashes as per the standard. if (n == 0 || m > capacity_) { resize(m); } } - void reserve(size_t n) { - rehash(NumSlotsFast(n)); - } + void reserve(size_t n) { rehash(GrowthToLowerboundCapacity(n)); } // Extension API: support for heterogeneous keys. // @@ -1551,13 +1411,6 @@ class raw_hash_set { slot_type&& slot; }; - // Computes std::ceil(n / kMaxLoadFactor). Faster than calling std::ceil. - static inline size_t NumSlotsFast(size_t n) { - return static_cast<size_t>( - (n * kMaxLoadFactorDenominator + (kMaxLoadFactorNumerator - 1)) / - kMaxLoadFactorNumerator); - } - // "erases" the object from the container, except that it doesn't actually // destroy the object. It only updates all the metadata of the class. // This can be used in conjunction with Policy::transfer to move the object to @@ -1580,17 +1433,34 @@ class raw_hash_set { set_ctrl(index, was_never_full ? kEmpty : kDeleted); growth_left() += was_never_full; + infoz_.RecordErase(); } void initialize_slots() { assert(capacity_); + // Folks with custom allocators often make unwarranted assumptions about the + // behavior of their classes vis-a-vis trivial destructability and what + // calls they will or wont make. Avoid sampling for people with custom + // allocators to get us out of this mess. This is not a hard guarantee but + // a workaround while we plan the exact guarantee we want to provide. + // + // People are often sloppy with the exact type of their allocator (sometimes + // it has an extra const or is missing the pair, but rebinds made it work + // anyway). To avoid the ambiguity, we work off SlotAlloc which we have + // bound more carefully. + if (std::is_same<SlotAlloc, std::allocator<slot_type>>::value && + slots_ == nullptr) { + infoz_ = Sample(); + } + auto layout = MakeLayout(capacity_); char* mem = static_cast<char*>( Allocate<Layout::Alignment()>(&alloc_ref(), layout.AllocSize())); ctrl_ = reinterpret_cast<ctrl_t*>(layout.template Pointer<0>(mem)); slots_ = layout.template Pointer<1>(mem); reset_ctrl(); - growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor) - size_; + reset_growth_left(); + infoz_.RecordStorageChanged(size_, capacity_); } void destroy_slots() { @@ -1619,11 +1489,14 @@ class raw_hash_set { capacity_ = new_capacity; initialize_slots(); + size_t total_probe_length = 0; for (size_t i = 0; i != old_capacity; ++i) { if (IsFull(old_ctrl[i])) { size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, PolicyTraits::element(old_slots + i)); - size_t new_i = find_first_non_full(hash); + auto target = find_first_non_full(hash); + size_t new_i = target.offset; + total_probe_length += target.probe_length; set_ctrl(new_i, H2(hash)); PolicyTraits::transfer(&alloc_ref(), slots_ + new_i, old_slots + i); } @@ -1635,10 +1508,12 @@ class raw_hash_set { Deallocate<Layout::Alignment()>(&alloc_ref(), old_ctrl, layout.AllocSize()); } + infoz_.RecordRehash(total_probe_length); } void drop_deletes_without_resize() ABSL_ATTRIBUTE_NOINLINE { assert(IsValidCapacity(capacity_)); + assert(!is_small()); // Algorithm: // - mark all DELETED slots as EMPTY // - mark all FULL slots as DELETED @@ -1658,12 +1533,15 @@ class raw_hash_set { ConvertDeletedToEmptyAndFullToDeleted(ctrl_, capacity_); typename std::aligned_storage<sizeof(slot_type), alignof(slot_type)>::type raw; + size_t total_probe_length = 0; slot_type* slot = reinterpret_cast<slot_type*>(&raw); for (size_t i = 0; i != capacity_; ++i) { if (!IsDeleted(ctrl_[i])) continue; size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, PolicyTraits::element(slots_ + i)); - size_t new_i = find_first_non_full(hash); + auto target = find_first_non_full(hash); + size_t new_i = target.offset; + total_probe_length += target.probe_length; // Verify if the old and new i fall within the same group wrt the hash. // If they do, we don't need to move the object as it falls already in the @@ -1695,13 +1573,14 @@ class raw_hash_set { --i; // repeat } } - growth_left() = static_cast<size_t>(capacity_ * kMaxLoadFactor) - size_; + reset_growth_left(); + infoz_.RecordRehash(total_probe_length); } void rehash_and_grow_if_necessary() { if (capacity_ == 0) { - resize(Group::kWidth - 1); - } else if (size() <= kMaxLoadFactor / 2 * capacity_) { + resize(1); + } else if (size() <= CapacityToGrowth(capacity()) / 2) { // Squash DELETED without growing if there is enough capacity. drop_deletes_without_resize(); } else { @@ -1736,24 +1615,26 @@ class raw_hash_set { // - the input is already a set // - there are enough slots // - the element with the hash is not in the table - size_t find_first_non_full(size_t hash) { + struct FindInfo { + size_t offset; + size_t probe_length; + }; + FindInfo find_first_non_full(size_t hash) { auto seq = probe(hash); while (true) { Group g{ctrl_ + seq.offset()}; auto mask = g.MatchEmptyOrDeleted(); if (mask) { #if !defined(NDEBUG) - // We want to force small tables to have random entries too, so - // in debug build we will randomly insert in either the front or back of + // We want to add entropy even when ASLR is not enabled. + // In debug build we will randomly insert in either the front or back of // the group. // TODO(kfm,sbenza): revisit after we do unconditional mixing - if (ShouldInsertBackwards(hash, ctrl_)) - return seq.offset(mask.HighestBitSet()); - else - return seq.offset(mask.LowestBitSet()); -#else - return seq.offset(mask.LowestBitSet()); + if (!is_small() && ShouldInsertBackwards(hash, ctrl_)) { + return {seq.offset(mask.HighestBitSet()), seq.index()}; + } #endif + return {seq.offset(mask.LowestBitSet()), seq.index()}; } assert(seq.index() < capacity_ && "full table!"); seq.next(); @@ -1792,15 +1673,17 @@ class raw_hash_set { } size_t prepare_insert(size_t hash) ABSL_ATTRIBUTE_NOINLINE { - size_t target = find_first_non_full(hash); - if (ABSL_PREDICT_FALSE(growth_left() == 0 && !IsDeleted(ctrl_[target]))) { + auto target = find_first_non_full(hash); + if (ABSL_PREDICT_FALSE(growth_left() == 0 && + !IsDeleted(ctrl_[target.offset]))) { rehash_and_grow_if_necessary(); target = find_first_non_full(hash); } ++size_; - growth_left() -= IsEmpty(ctrl_[target]); - set_ctrl(target, H2(hash)); - return target; + growth_left() -= IsEmpty(ctrl_[target.offset]); + set_ctrl(target.offset, H2(hash)); + infoz_.RecordInsert(hash, target.probe_length); + return target.offset; } // Constructs the value in the space pointed by the iterator. This only works @@ -1838,6 +1721,10 @@ class raw_hash_set { SanitizerPoisonMemoryRegion(slots_, sizeof(slot_type) * capacity_); } + void reset_growth_left() { + growth_left() = CapacityToGrowth(capacity()) - size_; + } + // Sets the control byte, and if `i < Group::kWidth`, set the cloned byte at // the end too. void set_ctrl(size_t i, ctrl_t h) { @@ -1850,11 +1737,28 @@ class raw_hash_set { } ctrl_[i] = h; - ctrl_[((i - Group::kWidth) & capacity_) + Group::kWidth] = h; + ctrl_[((i - Group::kWidth) & capacity_) + 1 + + ((Group::kWidth - 1) & capacity_)] = h; } size_t& growth_left() { return settings_.template get<0>(); } + // The representation of the object has two modes: + // - small: For capacities < kWidth-1 + // - large: For the rest. + // + // Differences: + // - In small mode we are able to use the whole capacity. The extra control + // bytes give us at least one "empty" control byte to stop the iteration. + // This is important to make 1 a valid capacity. + // + // - In small mode only the first `capacity()` control bytes after the + // sentinel are valid. The rest contain dummy kEmpty values that do not + // represent a real slot. This is important to take into account on + // find_first_non_full(), where we never try ShouldInsertBackwards() for + // small tables. + bool is_small() const { return capacity_ < Group::kWidth - 1; } + hasher& hash_ref() { return settings_.template get<1>(); } const hasher& hash_ref() const { return settings_.template get<1>(); } key_equal& eq_ref() { return settings_.template get<2>(); } @@ -1864,12 +1768,6 @@ class raw_hash_set { return settings_.template get<3>(); } - // On average each group has 2 empty slot (for the vectorized case). - static constexpr int64_t kMaxLoadFactorNumerator = 14; - static constexpr int64_t kMaxLoadFactorDenominator = 16; - static constexpr float kMaxLoadFactor = - 1.0 * kMaxLoadFactorNumerator / kMaxLoadFactorDenominator; - // TODO(alkis): Investigate removing some of these fields: // - ctrl/slots can be derived from each other // - size can be moved into the slot array @@ -1877,6 +1775,7 @@ class raw_hash_set { slot_type* slots_ = nullptr; // [capacity * slot_type] size_t size_ = 0; // number of full slots size_t capacity_ = 0; // total number of slots + HashtablezInfoHandle infoz_; absl::container_internal::CompressedTuple<size_t /* growth_left */, hasher, key_equal, allocator_type> settings_{0, hasher{}, key_equal{}, allocator_type{}}; @@ -1929,10 +1828,9 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> { } static size_t LowerBoundAllocatedByteSize(size_t size) { - size_t capacity = container_internal::NormalizeCapacity( - std::ceil(size / Set::kMaxLoadFactor)); + size_t capacity = GrowthToLowerboundCapacity(size); if (capacity == 0) return 0; - auto layout = Set::MakeLayout(capacity); + auto layout = Set::MakeLayout(NormalizeCapacity(capacity)); size_t m = layout.AllocSize(); size_t per_slot = Traits::space_used(static_cast<const Slot*>(nullptr)); if (per_slot != ~size_t{}) { @@ -1944,7 +1842,7 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> { } // namespace hashtable_debug_internal } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_SET_H_ diff --git a/absl/container/internal/raw_hash_set_allocator_test.cc b/absl/container/internal/raw_hash_set_allocator_test.cc index f5779d62..5188b3ae 100644 --- a/absl/container/internal/raw_hash_set_allocator_test.cc +++ b/absl/container/internal/raw_hash_set_allocator_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "absl/container/internal/tracked.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -426,5 +426,5 @@ TEST_F(PropagateOnAll, Swap) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 302f9758..2783f5c4 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -35,7 +35,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { struct RawHashSetTestOnlyAccess { @@ -49,18 +49,47 @@ namespace { using ::testing::DoubleNear; using ::testing::ElementsAre; +using ::testing::Ge; +using ::testing::Lt; using ::testing::Optional; using ::testing::Pair; using ::testing::UnorderedElementsAre; TEST(Util, NormalizeCapacity) { - constexpr size_t kMinCapacity = Group::kWidth - 1; - EXPECT_EQ(kMinCapacity, NormalizeCapacity(0)); - EXPECT_EQ(kMinCapacity, NormalizeCapacity(1)); - EXPECT_EQ(kMinCapacity, NormalizeCapacity(2)); - EXPECT_EQ(kMinCapacity, NormalizeCapacity(kMinCapacity)); - EXPECT_EQ(kMinCapacity * 2 + 1, NormalizeCapacity(kMinCapacity + 1)); - EXPECT_EQ(kMinCapacity * 2 + 1, NormalizeCapacity(kMinCapacity + 2)); + EXPECT_EQ(1, NormalizeCapacity(0)); + EXPECT_EQ(1, NormalizeCapacity(1)); + EXPECT_EQ(3, NormalizeCapacity(2)); + EXPECT_EQ(3, NormalizeCapacity(3)); + EXPECT_EQ(7, NormalizeCapacity(4)); + EXPECT_EQ(7, NormalizeCapacity(7)); + EXPECT_EQ(15, NormalizeCapacity(8)); + EXPECT_EQ(15, NormalizeCapacity(15)); + EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 1)); + EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 2)); +} + +TEST(Util, GrowthAndCapacity) { + // Verify that GrowthToCapacity gives the minimum capacity that has enough + // growth. + for (size_t growth = 0; growth < 10000; ++growth) { + SCOPED_TRACE(growth); + size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth)); + // The capacity is large enough for `growth` + EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth)); + if (growth != 0 && capacity > 1) { + // There is no smaller capacity that works. + EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth)); + } + } + + for (size_t capacity = Group::kWidth - 1; capacity < 10000; + capacity = 2 * capacity + 1) { + SCOPED_TRACE(capacity); + size_t growth = CapacityToGrowth(capacity); + EXPECT_THAT(growth, Lt(capacity)); + EXPECT_LE(GrowthToLowerboundCapacity(growth), capacity); + EXPECT_EQ(NormalizeCapacity(GrowthToLowerboundCapacity(growth)), capacity); + } } TEST(Util, probe_seq) { @@ -107,14 +136,14 @@ TEST(BitMask, WithShift) { } TEST(BitMask, LeadingTrailing) { - EXPECT_EQ((BitMask<uint32_t, 16>(0b0001101001000000).LeadingZeros()), 3); - EXPECT_EQ((BitMask<uint32_t, 16>(0b0001101001000000).TrailingZeros()), 6); + EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).LeadingZeros()), 3); + EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).TrailingZeros()), 6); - EXPECT_EQ((BitMask<uint32_t, 16>(0b0000000000000001).LeadingZeros()), 15); - EXPECT_EQ((BitMask<uint32_t, 16>(0b0000000000000001).TrailingZeros()), 0); + EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).LeadingZeros()), 15); + EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).TrailingZeros()), 0); - EXPECT_EQ((BitMask<uint32_t, 16>(0b1000000000000000).LeadingZeros()), 0); - EXPECT_EQ((BitMask<uint32_t, 16>(0b1000000000000000).TrailingZeros()), 15); + EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).LeadingZeros()), 0); + EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).TrailingZeros()), 15); EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).LeadingZeros()), 3); EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).TrailingZeros()), 1); @@ -315,7 +344,25 @@ struct IntTable : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>, std::equal_to<int64_t>, std::allocator<int64_t>> { using Base = typename IntTable::raw_hash_set; - IntTable() {} + using Base::Base; +}; + +template <typename T> +struct CustomAlloc : std::allocator<T> { + CustomAlloc() {} + + template <typename U> + CustomAlloc(const CustomAlloc<U>& other) {} + + template<class U> struct rebind { + using other = CustomAlloc<U>; + }; +}; + +struct CustomAllocIntTable + : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>, + std::equal_to<int64_t>, CustomAlloc<int64_t>> { + using Base = typename CustomAllocIntTable::raw_hash_set; using Base::Base; }; @@ -343,6 +390,7 @@ TEST(Table, EmptyFunctorOptimization) { size_t size; size_t capacity; size_t growth_left; + void* infoz; }; struct StatelessHash { size_t operator()(absl::string_view) const { return 0; } @@ -385,10 +433,11 @@ TEST(Table, Prefetch) { t.prefetch(2); // Do not run in debug mode, when prefetch is not implemented, or when - // sanitizers are enabled. -#if defined(NDEBUG) && defined(__GNUC__) && !defined(ADDRESS_SANITIZER) && \ - !defined(MEMORY_SANITIZER) && !defined(THREAD_SANITIZER) && \ - !defined(UNDEFINED_BEHAVIOR_SANITIZER) + // sanitizers are enabled, or on WebAssembly. +#if defined(NDEBUG) && defined(__GNUC__) && defined(__x86_64__) && \ + !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ + !defined(THREAD_SANITIZER) && !defined(UNDEFINED_BEHAVIOR_SANITIZER) && \ + !defined(__EMSCRIPTEN__) const auto now = [] { return absl::base_internal::CycleClock::Now(); }; // Make size enough to not fit in L2 cache (16.7 Mb) @@ -785,7 +834,7 @@ TEST(Table, EnsureNonQuadraticAsInRust) { TEST(Table, ClearBug) { IntTable t; constexpr size_t capacity = container_internal::Group::kWidth - 1; - constexpr size_t max_size = capacity / 2; + constexpr size_t max_size = capacity / 2 + 1; for (size_t i = 0; i < max_size; ++i) { t.insert(i); } @@ -816,6 +865,25 @@ TEST(Table, Erase) { EXPECT_TRUE(t.find(0) == t.end()); } +TEST(Table, EraseMaintainsValidIterator) { + IntTable t; + const int kNumElements = 100; + for (int i = 0; i < kNumElements; i ++) { + EXPECT_TRUE(t.emplace(i).second); + } + EXPECT_EQ(t.size(), kNumElements); + + int num_erase_calls = 0; + auto it = t.begin(); + while (it != t.end()) { + t.erase(it++); + num_erase_calls++; + } + + EXPECT_TRUE(t.empty()); + EXPECT_EQ(num_erase_calls, kNumElements); +} + // Collect N bad keys by following algorithm: // 1. Create an empty table and reserve it to 2 * N. // 2. Insert N random elements. @@ -1014,7 +1082,7 @@ ProbeStats CollectProbeStatsOnKeysXoredWithSeed(const std::vector<int64_t>& keys ExpectedStats XorSeedExpectedStats() { constexpr bool kRandomizesInserts = -#if NDEBUG +#ifdef NDEBUG false; #else // NDEBUG true; @@ -1051,6 +1119,7 @@ ExpectedStats XorSeedExpectedStats() { ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width"); return {}; } + TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) { ProbeStatsPerSize stats; std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10}; @@ -1107,7 +1176,7 @@ ProbeStats CollectProbeStatsOnLinearlyTransformedKeys( ExpectedStats LinearTransformExpectedStats() { constexpr bool kRandomizesInserts = -#if NDEBUG +#ifdef NDEBUG false; #else // NDEBUG true; @@ -1144,6 +1213,7 @@ ExpectedStats LinearTransformExpectedStats() { ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width"); return {}; } + TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) { ProbeStatsPerSize stats; std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10}; @@ -1296,37 +1366,31 @@ TEST(Table, ConstructFromInitList) { TEST(Table, CopyConstruct) { IntTable t; - t.max_load_factor(.321f); t.emplace(0); EXPECT_EQ(1, t.size()); { IntTable u(t); EXPECT_EQ(1, u.size()); - EXPECT_EQ(t.max_load_factor(), u.max_load_factor()); EXPECT_THAT(*u.find(0), 0); } { IntTable u{t}; EXPECT_EQ(1, u.size()); - EXPECT_EQ(t.max_load_factor(), u.max_load_factor()); EXPECT_THAT(*u.find(0), 0); } { IntTable u = t; EXPECT_EQ(1, u.size()); - EXPECT_EQ(t.max_load_factor(), u.max_load_factor()); EXPECT_THAT(*u.find(0), 0); } } TEST(Table, CopyConstructWithAlloc) { StringTable t; - t.max_load_factor(.321f); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u(t, Alloc<std::pair<std::string, std::string>>()); EXPECT_EQ(1, u.size()); - EXPECT_EQ(t.max_load_factor(), u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } @@ -1344,94 +1408,75 @@ TEST(Table, AllocWithExplicitCtor) { TEST(Table, MoveConstruct) { { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u(std::move(t)); EXPECT_EQ(1, u.size()); - EXPECT_EQ(lf, u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u{std::move(t)}; EXPECT_EQ(1, u.size()); - EXPECT_EQ(lf, u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u = std::move(t); EXPECT_EQ(1, u.size()); - EXPECT_EQ(lf, u.max_load_factor()); EXPECT_THAT(*u.find("a"), Pair("a", "b")); } } TEST(Table, MoveConstructWithAlloc) { StringTable t; - t.max_load_factor(.321f); - const float lf = t.max_load_factor(); t.emplace("a", "b"); EXPECT_EQ(1, t.size()); StringTable u(std::move(t), Alloc<std::pair<std::string, std::string>>()); 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<std::pair<std::string, std::string>> v = {{"a", "b"}, {"aa", "bb"}}; + std::vector<std::pair<std::string, std::string>> v = {{"a", "b"}, + {"aa", "bb"}}; t.insert(std::begin(v), std::end(v)); StringTable u = t; EXPECT_EQ(u, t); @@ -1439,20 +1484,24 @@ TEST(Table, Equality) { TEST(Table, Equality2) { StringTable t; - std::vector<std::pair<std::string, std::string>> v1 = {{"a", "b"}, {"aa", "bb"}}; + std::vector<std::pair<std::string, std::string>> v1 = {{"a", "b"}, + {"aa", "bb"}}; t.insert(std::begin(v1), std::end(v1)); StringTable u; - std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"}, {"aa", "aa"}}; + std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"}, + {"aa", "aa"}}; u.insert(std::begin(v2), std::end(v2)); EXPECT_NE(u, t); } TEST(Table, Equality3) { StringTable t; - std::vector<std::pair<std::string, std::string>> v1 = {{"b", "b"}, {"bb", "bb"}}; + std::vector<std::pair<std::string, std::string>> v1 = {{"b", "b"}, + {"bb", "bb"}}; t.insert(std::begin(v1), std::end(v1)); StringTable u; - std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"}, {"aa", "aa"}}; + std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"}, + {"aa", "aa"}}; u.insert(std::begin(v2), std::end(v2)); EXPECT_NE(u, t); } @@ -1677,7 +1726,7 @@ TEST(Nodes, ExtractInsert) { EXPECT_FALSE(node.empty()); StringTable t2; - auto res = t2.insert(std::move(node)); + StringTable::insert_return_type res = t2.insert(std::move(node)); EXPECT_TRUE(res.inserted); EXPECT_THAT(*res.position, Pair(k0, "")); EXPECT_FALSE(res.node); @@ -1707,80 +1756,74 @@ TEST(Nodes, ExtractInsert) { EXPECT_FALSE(node); } -StringTable MakeSimpleTable(size_t size) { - StringTable t; - for (size_t i = 0; i < size; ++i) t.emplace(std::string(1, 'A' + i), ""); +IntTable MakeSimpleTable(size_t size) { + IntTable t; + while (t.size() < size) t.insert(t.size()); return t; } -std::string OrderOfIteration(const StringTable& t) { - std::string order; - for (auto& p : t) order += p.first; - return order; +std::vector<int> OrderOfIteration(const IntTable& t) { + return {t.begin(), t.end()}; } +// These IterationOrderChanges tests depend on non-deterministic behavior. +// We are injecting non-determinism from the pointer of the table, but do so in +// a way that only the page matters. We have to retry enough times to make sure +// we are touching different memory pages to cause the ordering to change. +// We also need to keep the old tables around to avoid getting the same memory +// blocks over and over. TEST(Table, IterationOrderChangesByInstance) { - // Needs to be more than kWidth elements to be able to affect order. - const StringTable reference = MakeSimpleTable(20); - - // Since order is non-deterministic we can't just try once and verify. - // We'll try until we find that order changed. It should not take many tries - // for that. - // Important: we have to keep the old tables around. Otherwise tcmalloc will - // just give us the same blocks and we would be doing the same order again. - std::vector<StringTable> garbage; - for (int i = 0; i < 10; ++i) { - auto trial = MakeSimpleTable(20); - if (OrderOfIteration(trial) != OrderOfIteration(reference)) { - // We are done. - return; + for (size_t size : {2, 6, 12, 20}) { + const auto reference_table = MakeSimpleTable(size); + const auto reference = OrderOfIteration(reference_table); + + std::vector<IntTable> tables; + bool found_difference = false; + for (int i = 0; !found_difference && i < 5000; ++i) { + tables.push_back(MakeSimpleTable(size)); + found_difference = OrderOfIteration(tables.back()) != reference; + } + if (!found_difference) { + FAIL() + << "Iteration order remained the same across many attempts with size " + << size; } - garbage.push_back(std::move(trial)); } - FAIL(); } TEST(Table, IterationOrderChangesOnRehash) { - // Since order is non-deterministic we can't just try once and verify. - // We'll try until we find that order changed. It should not take many tries - // for that. - // Important: we have to keep the old tables around. Otherwise tcmalloc will - // just give us the same blocks and we would be doing the same order again. - std::vector<StringTable> garbage; - for (int i = 0; i < 10; ++i) { - // Needs to be more than kWidth elements to be able to affect order. - StringTable t = MakeSimpleTable(20); - const std::string reference = OrderOfIteration(t); + std::vector<IntTable> garbage; + for (int i = 0; i < 5000; ++i) { + auto t = MakeSimpleTable(20); + const auto reference = OrderOfIteration(t); // Force rehash to the same size. t.rehash(0); - std::string trial = OrderOfIteration(t); + auto trial = OrderOfIteration(t); if (trial != reference) { // We are done. return; } garbage.push_back(std::move(t)); } - FAIL(); + FAIL() << "Iteration order remained the same across many attempts."; } -TEST(Table, IterationOrderChangesForSmallTables) { - // Since order is non-deterministic we can't just try once and verify. - // We'll try until we find that order changed. - // Important: we have to keep the old tables around. Otherwise tcmalloc will - // just give us the same blocks and we would be doing the same order again. - StringTable reference_table = MakeSimpleTable(5); - const std::string reference = OrderOfIteration(reference_table); - std::vector<StringTable> garbage; - for (int i = 0; i < 50; ++i) { - StringTable t = MakeSimpleTable(5); - std::string trial = OrderOfIteration(t); - if (trial != reference) { - // We are done. - return; - } - garbage.push_back(std::move(t)); - } - FAIL() << "Iteration order remained the same across many attempts."; +// Verify that pointers are invalidated as soon as a second element is inserted. +// This prevents dependency on pointer stability on small tables. +TEST(Table, UnstablePointers) { + IntTable table; + + const auto addr = [&](int i) { + return reinterpret_cast<uintptr_t>(&*table.find(i)); + }; + + table.insert(0); + const uintptr_t old_ptr = addr(0); + + // This causes a rehash. + table.insert(1); + + EXPECT_NE(old_ptr, addr(0)); } // Confirm that we assert if we try to erase() end(). @@ -1799,9 +1842,52 @@ TEST(TableDeathTest, EraseOfEndAsserts) { EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg); } +TEST(RawHashSamplerTest, Sample) { + // Enable the feature even if the prod default is off. + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(100); + + auto& sampler = HashtablezSampler::Global(); + size_t start_size = 0; + start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; }); + + std::vector<IntTable> tables; + for (int i = 0; i < 1000000; ++i) { + tables.emplace_back(); + tables.back().insert(1); + } + size_t end_size = 0; + end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; }); + + EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()), + 0.01, 0.005); +} + +TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) { + // Enable the feature even if the prod default is off. + SetHashtablezEnabled(true); + SetHashtablezSampleParameter(100); + + auto& sampler = HashtablezSampler::Global(); + size_t start_size = 0; + start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; }); + + std::vector<CustomAllocIntTable> tables; + for (int i = 0; i < 1000000; ++i) { + tables.emplace_back(); + tables.back().insert(1); + } + size_t end_size = 0; + end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; }); + + EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()), + 0.00, 0.001); +} + #ifdef ADDRESS_SANITIZER TEST(Sanitizer, PoisoningUnused) { IntTable t; + t.reserve(5); // Insert something to force an allocation. int64_t& v1 = *t.insert(0).first; @@ -1826,5 +1912,5 @@ TEST(Sanitizer, PoisoningOnErase) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/test_instance_tracker.cc b/absl/container/internal/test_instance_tracker.cc index 91441729..f4b283fd 100644 --- a/absl/container/internal/test_instance_tracker.cc +++ b/absl/container/internal/test_instance_tracker.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,7 @@ #include "absl/container/internal/test_instance_tracker.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace test_internal { int BaseCountedInstance::num_instances_ = 0; int BaseCountedInstance::num_live_instances_ = 0; @@ -25,5 +25,5 @@ int BaseCountedInstance::num_swaps_ = 0; int BaseCountedInstance::num_comparisons_ = 0; } // namespace test_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/test_instance_tracker.h b/absl/container/internal/test_instance_tracker.h index 060077d0..ab7f9f22 100644 --- a/absl/container/internal/test_instance_tracker.h +++ b/absl/container/internal/test_instance_tracker.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,11 +18,13 @@ #include <cstdlib> #include <ostream> +#include "absl/types/compare.h" + namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace test_internal { -// A type that counts number of occurences of the type, the live occurrences of +// A type that counts number of occurrences of the type, the live occurrences of // the type, as well as the number of copies, moves, swaps, and comparisons that // have occurred on the type. This is used as a base class for the copyable, // copyable+movable, and movable types below that are used in actual tests. Use @@ -97,6 +99,14 @@ class BaseCountedInstance { return value_ >= x.value_; } + absl::weak_ordering compare(const BaseCountedInstance& x) const { + ++num_comparisons_; + return value_ < x.value_ + ? absl::weak_ordering::less + : value_ == x.value_ ? absl::weak_ordering::equivalent + : absl::weak_ordering::greater; + } + int value() const { if (!is_live_) std::abort(); return value_; @@ -258,7 +268,7 @@ class MovableOnlyInstance : public BaseCountedInstance { }; } // namespace test_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ diff --git a/absl/container/internal/test_instance_tracker_test.cc b/absl/container/internal/test_instance_tracker_test.cc index 0ae57636..1c6a4fa7 100644 --- a/absl/container/internal/test_instance_tracker_test.cc +++ b/absl/container/internal/test_instance_tracker_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -174,6 +174,8 @@ TEST(TestInstanceTracker, Comparisons) { EXPECT_EQ(5, tracker.comparisons()); EXPECT_FALSE(one >= two); EXPECT_EQ(6, tracker.comparisons()); + EXPECT_TRUE(one.compare(two) < 0); // NOLINT + EXPECT_EQ(7, tracker.comparisons()); tracker.ResetCopiesMovesSwaps(); EXPECT_EQ(0, tracker.comparisons()); diff --git a/absl/container/internal/tracked.h b/absl/container/internal/tracked.h index f72c46ea..e9e6b95d 100644 --- a/absl/container/internal/tracked.h +++ b/absl/container/internal/tracked.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include <utility> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { // A class that tracks its copies and moves so that it can be queried in tests. @@ -74,7 +74,7 @@ class Tracked { }; } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_TRACKED_H_ diff --git a/absl/container/internal/unordered_map_constructor_test.h b/absl/container/internal/unordered_map_constructor_test.h index 14ceeecb..b64b5520 100644 --- a/absl/container/internal/unordered_map_constructor_test.h +++ b/absl/container/internal/unordered_map_constructor_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,13 +24,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class UnordMap> class ConstructorTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(ConstructorTest); +TYPED_TEST_SUITE_P(ConstructorTest); TYPED_TEST_P(ConstructorTest, NoArgs) { TypeParam m; @@ -84,8 +84,28 @@ TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { +template <typename T> +struct is_std_unordered_map : std::false_type {}; + +template <typename... T> +struct is_std_unordered_map<std::unordered_map<T...>> : std::true_type {}; + #if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +using has_cxx14_std_apis = std::true_type; +#else +using has_cxx14_std_apis = std::false_type; +#endif + +template <typename T> +using expect_cxx14_apis = + absl::disjunction<absl::negation<is_std_unordered_map<T>>, + has_cxx14_std_apis>; + +template <typename TypeParam> +void BucketCountAllocTest(std::false_type) {} + +template <typename TypeParam> +void BucketCountAllocTest(std::true_type) { using A = typename TypeParam::allocator_type; A alloc(0); TypeParam m(123, alloc); @@ -93,11 +113,17 @@ TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { EXPECT_TRUE(m.empty()); EXPECT_THAT(m, ::testing::UnorderedElementsAre()); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { + BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); +} + +template <typename TypeParam> +void BucketCountHashAllocTest(std::false_type) {} + +template <typename TypeParam> +void BucketCountHashAllocTest(std::true_type) { using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; H hasher; @@ -108,18 +134,38 @@ TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { EXPECT_TRUE(m.empty()); EXPECT_THAT(m, ::testing::UnorderedElementsAre()); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, BucketAlloc) { +TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { + BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); +} + #if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +using has_alloc_std_constructors = std::true_type; +#else +using has_alloc_std_constructors = std::false_type; +#endif + +template <typename T> +using expect_alloc_constructors = + absl::disjunction<absl::negation<is_std_unordered_map<T>>, + has_alloc_std_constructors>; + +template <typename TypeParam> +void AllocTest(std::false_type) {} + +template <typename TypeParam> +void AllocTest(std::true_type) { using A = typename TypeParam::allocator_type; A alloc(0); TypeParam m(alloc); EXPECT_EQ(m.get_allocator(), alloc); EXPECT_TRUE(m.empty()); EXPECT_THAT(m, ::testing::UnorderedElementsAre()); -#endif +} + +TYPED_TEST_P(ConstructorTest, Alloc) { + AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); } TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { @@ -141,8 +187,11 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +template <typename TypeParam> +void InputIteratorBucketAllocTest(std::false_type) {} + +template <typename TypeParam> +void InputIteratorBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using A = typename TypeParam::allocator_type; A alloc(0); @@ -153,11 +202,17 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { + InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); +} + +template <typename TypeParam> +void InputIteratorBucketHashAllocTest(std::false_type) {} + +template <typename TypeParam> +void InputIteratorBucketHashAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; @@ -171,7 +226,10 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { + InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); } TYPED_TEST_P(ConstructorTest, CopyConstructor) { @@ -191,8 +249,11 @@ TYPED_TEST_P(ConstructorTest, CopyConstructor) { EXPECT_EQ(m, n); } -TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { -#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +template <typename TypeParam> +void CopyConstructorAllocTest(std::false_type) {} + +template <typename TypeParam> +void CopyConstructorAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -207,7 +268,10 @@ TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_NE(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); -#endif +} + +TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { + CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); } // TODO(alkis): Test non-propagating allocators on copy constructors. @@ -230,8 +294,11 @@ TYPED_TEST_P(ConstructorTest, MoveConstructor) { EXPECT_EQ(m, n); } -TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { -#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +template <typename TypeParam> +void MoveConstructorAllocTest(std::false_type) {} + +template <typename TypeParam> +void MoveConstructorAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -247,7 +314,10 @@ TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_NE(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); -#endif +} + +TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { + MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); } // TODO(alkis): Test non-propagating allocators on move constructors. @@ -270,8 +340,11 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +template <typename TypeParam> +void InitializerListBucketAllocTest(std::false_type) {} + +template <typename TypeParam> +void InitializerListBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using A = typename TypeParam::allocator_type; hash_internal::Generator<T> gen; @@ -281,11 +354,17 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { -#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) +TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { + InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); +} + +template <typename TypeParam> +void InitializerListBucketHashAllocTest(std::false_type) {} + +template <typename TypeParam> +void InitializerListBucketHashAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; @@ -298,7 +377,10 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif +} + +TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { + InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); } TYPED_TEST_P(ConstructorTest, Assignment) { @@ -391,17 +473,17 @@ TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { REGISTER_TYPED_TEST_CASE_P( ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, - BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, - BucketAlloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, + BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, + InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc, MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc, InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment, - MoveAssignment, AssignmentFromInitializerList, - AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting, + MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting, + MoveAssignmentOverwritesExisting, AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_ diff --git a/absl/container/internal/unordered_map_lookup_test.h b/absl/container/internal/unordered_map_lookup_test.h index d767aa8d..9ad78a79 100644 --- a/absl/container/internal/unordered_map_lookup_test.h +++ b/absl/container/internal/unordered_map_lookup_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,13 +21,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class UnordMap> class LookupTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(LookupTest); +TYPED_TEST_SUITE_P(LookupTest); TYPED_TEST_P(LookupTest, At) { using T = hash_internal::GeneratedType<TypeParam>; @@ -111,7 +111,7 @@ REGISTER_TYPED_TEST_CASE_P(LookupTest, At, OperatorBracket, Count, Find, EqualRange); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_ diff --git a/absl/container/internal/unordered_map_members_test.h b/absl/container/internal/unordered_map_members_test.h new file mode 100644 index 00000000..c4600405 --- /dev/null +++ b/absl/container/internal/unordered_map_members_test.h @@ -0,0 +1,87 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ + +#include <type_traits> +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +template <class UnordMap> +class MembersTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(MembersTest); + +template <typename T> +void UseType() {} + +TYPED_TEST_P(MembersTest, Typedefs) { + EXPECT_TRUE((std::is_same<std::pair<const typename TypeParam::key_type, + typename TypeParam::mapped_type>, + typename TypeParam::value_type>())); + EXPECT_TRUE((absl::conjunction< + absl::negation<std::is_signed<typename TypeParam::size_type>>, + std::is_integral<typename TypeParam::size_type>>())); + EXPECT_TRUE((absl::conjunction< + std::is_signed<typename TypeParam::difference_type>, + std::is_integral<typename TypeParam::difference_type>>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval<const typename TypeParam::hasher&>()( + std::declval<const typename TypeParam::key_type&>())), + size_t>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval<const typename TypeParam::key_equal&>()( + std::declval<const typename TypeParam::key_type&>(), + std::declval<const typename TypeParam::key_type&>())), + bool>())); + EXPECT_TRUE((std::is_same<typename TypeParam::allocator_type::value_type, + typename TypeParam::value_type>())); + EXPECT_TRUE((std::is_same<typename TypeParam::value_type&, + typename TypeParam::reference>())); + EXPECT_TRUE((std::is_same<const typename TypeParam::value_type&, + typename TypeParam::const_reference>())); + EXPECT_TRUE((std::is_same<typename std::allocator_traits< + typename TypeParam::allocator_type>::pointer, + typename TypeParam::pointer>())); + EXPECT_TRUE( + (std::is_same<typename std::allocator_traits< + typename TypeParam::allocator_type>::const_pointer, + typename TypeParam::const_pointer>())); +} + +TYPED_TEST_P(MembersTest, SimpleFunctions) { + EXPECT_GT(TypeParam().max_size(), 0); +} + +TYPED_TEST_P(MembersTest, BeginEnd) { + TypeParam t = {typename TypeParam::value_type{}}; + EXPECT_EQ(t.begin(), t.cbegin()); + EXPECT_EQ(t.end(), t.cend()); + EXPECT_NE(t.begin(), t.end()); + EXPECT_NE(t.cbegin(), t.cend()); +} + +REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ diff --git a/absl/container/internal/unordered_map_modifiers_test.h b/absl/container/internal/unordered_map_modifiers_test.h index 5d7f1fe3..89dd7894 100644 --- a/absl/container/internal/unordered_map_modifiers_test.h +++ b/absl/container/internal/unordered_map_modifiers_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,13 +21,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class UnordMap> class ModifiersTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(ModifiersTest); +TYPED_TEST_SUITE_P(ModifiersTest); TYPED_TEST_P(ModifiersTest, Clear) { using T = hash_internal::GeneratedType<TypeParam>; @@ -269,7 +269,7 @@ REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, Erase, EraseRange, EraseKey, Swap); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_ diff --git a/absl/container/internal/unordered_map_test.cc b/absl/container/internal/unordered_map_test.cc index 548f69f7..51a90af8 100644 --- a/absl/container/internal/unordered_map_test.cc +++ b/absl/container/internal/unordered_map_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,10 +16,11 @@ #include "absl/container/internal/unordered_map_constructor_test.h" #include "absl/container/internal/unordered_map_lookup_test.h" +#include "absl/container/internal/unordered_map_members_test.h" #include "absl/container/internal/unordered_map_modifiers_test.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -30,11 +31,12 @@ using MapTypes = ::testing::Types< StatefulTestingEqual, Alloc<std::pair<const std::string, std::string>>>>; -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, ConstructorTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, LookupTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedMap, ModifiersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ConstructorTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, LookupTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, MembersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ModifiersTest, MapTypes); } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/internal/unordered_set_constructor_test.h b/absl/container/internal/unordered_set_constructor_test.h index f370b249..ac73a896 100644 --- a/absl/container/internal/unordered_set_constructor_test.h +++ b/absl/container/internal/unordered_set_constructor_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,21 +16,23 @@ #define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_ #include <algorithm> +#include <unordered_set> #include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/container/internal/hash_generator_testing.h" #include "absl/container/internal/hash_policy_testing.h" +#include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class UnordMap> class ConstructorTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(ConstructorTest); +TYPED_TEST_SUITE_P(ConstructorTest); TYPED_TEST_P(ConstructorTest, NoArgs) { TypeParam m; @@ -92,8 +94,28 @@ TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) { EXPECT_GE(cm.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { +template <typename T> +struct is_std_unordered_set : std::false_type {}; + +template <typename... T> +struct is_std_unordered_set<std::unordered_set<T...>> : std::true_type {}; + #if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +using has_cxx14_std_apis = std::true_type; +#else +using has_cxx14_std_apis = std::false_type; +#endif + +template <typename T> +using expect_cxx14_apis = + absl::disjunction<absl::negation<is_std_unordered_set<T>>, + has_cxx14_std_apis>; + +template <typename TypeParam> +void BucketCountAllocTest(std::false_type) {} + +template <typename TypeParam> +void BucketCountAllocTest(std::true_type) { using A = typename TypeParam::allocator_type; A alloc(0); TypeParam m(123, alloc); @@ -101,11 +123,17 @@ TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { EXPECT_TRUE(m.empty()); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +TYPED_TEST_P(ConstructorTest, BucketCountAlloc) { + BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); +} + +template <typename TypeParam> +void BucketCountHashAllocTest(std::false_type) {} + +template <typename TypeParam> +void BucketCountHashAllocTest(std::true_type) { using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; H hasher; @@ -116,18 +144,38 @@ TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { EXPECT_TRUE(m.empty()); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, BucketAlloc) { +TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) { + BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); +} + #if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +using has_alloc_std_constructors = std::true_type; +#else +using has_alloc_std_constructors = std::false_type; +#endif + +template <typename T> +using expect_alloc_constructors = + absl::disjunction<absl::negation<is_std_unordered_set<T>>, + has_alloc_std_constructors>; + +template <typename TypeParam> +void AllocTest(std::false_type) {} + +template <typename TypeParam> +void AllocTest(std::true_type) { using A = typename TypeParam::allocator_type; A alloc(0); TypeParam m(alloc); EXPECT_EQ(m.get_allocator(), alloc); EXPECT_TRUE(m.empty()); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); -#endif +} + +TYPED_TEST_P(ConstructorTest, Alloc) { + AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); } TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { @@ -149,8 +197,11 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +template <typename TypeParam> +void InputIteratorBucketAllocTest(std::false_type) {} + +template <typename TypeParam> +void InputIteratorBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using A = typename TypeParam::allocator_type; A alloc(0); @@ -161,11 +212,17 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) { + InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); +} + +template <typename TypeParam> +void InputIteratorBucketHashAllocTest(std::false_type) {} + +template <typename TypeParam> +void InputIteratorBucketHashAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; @@ -179,7 +236,10 @@ TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif +} + +TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) { + InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); } TYPED_TEST_P(ConstructorTest, CopyConstructor) { @@ -197,10 +257,14 @@ TYPED_TEST_P(ConstructorTest, CopyConstructor) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_EQ(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); + EXPECT_NE(TypeParam(0, hasher, equal, alloc), n); } -TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { -#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +template <typename TypeParam> +void CopyConstructorAllocTest(std::false_type) {} + +template <typename TypeParam> +void CopyConstructorAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -215,7 +279,10 @@ TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_NE(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); -#endif +} + +TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) { + CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); } // TODO(alkis): Test non-propagating allocators on copy constructors. @@ -238,8 +305,11 @@ TYPED_TEST_P(ConstructorTest, MoveConstructor) { EXPECT_EQ(m, n); } -TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { -#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS +template <typename TypeParam> +void MoveConstructorAllocTest(std::false_type) {} + +template <typename TypeParam> +void MoveConstructorAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -255,7 +325,10 @@ TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { EXPECT_EQ(m.key_eq(), n.key_eq()); EXPECT_NE(m.get_allocator(), n.get_allocator()); EXPECT_EQ(m, n); -#endif +} + +TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) { + MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); } // TODO(alkis): Test non-propagating allocators on move constructors. @@ -278,8 +351,11 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) { EXPECT_GE(m.bucket_count(), 123); } -TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +template <typename TypeParam> +void InitializerListBucketAllocTest(std::false_type) {} + +template <typename TypeParam> +void InitializerListBucketAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using A = typename TypeParam::allocator_type; hash_internal::Generator<T> gen; @@ -289,11 +365,17 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { -#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) +TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) { + InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); +} + +template <typename TypeParam> +void InitializerListBucketHashAllocTest(std::false_type) {} + +template <typename TypeParam> +void InitializerListBucketHashAllocTest(std::true_type) { using T = hash_internal::GeneratedType<TypeParam>; using H = typename TypeParam::hasher; using A = typename TypeParam::allocator_type; @@ -306,10 +388,13 @@ TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { EXPECT_EQ(m.get_allocator(), alloc); EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); EXPECT_GE(m.bucket_count(), 123); -#endif } -TYPED_TEST_P(ConstructorTest, Assignment) { +TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) { + InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); +} + +TYPED_TEST_P(ConstructorTest, CopyAssignment) { using T = hash_internal::GeneratedType<TypeParam>; using H = typename TypeParam::hasher; using E = typename TypeParam::key_equal; @@ -395,17 +480,17 @@ TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) { REGISTER_TYPED_TEST_CASE_P( ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, - BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, - BucketAlloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, + BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, + InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc, MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc, - InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment, - MoveAssignment, AssignmentFromInitializerList, - AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting, + InitializerListBucketAlloc, InitializerListBucketHashAlloc, CopyAssignment, + MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting, + MoveAssignmentOverwritesExisting, AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_ diff --git a/absl/container/internal/unordered_set_lookup_test.h b/absl/container/internal/unordered_set_lookup_test.h index 9174279a..722fb1c2 100644 --- a/absl/container/internal/unordered_set_lookup_test.h +++ b/absl/container/internal/unordered_set_lookup_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,13 +21,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class UnordSet> class LookupTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(LookupTest); +TYPED_TEST_SUITE_P(LookupTest); TYPED_TEST_P(LookupTest, Count) { using T = hash_internal::GeneratedType<TypeParam>; @@ -85,7 +85,7 @@ TYPED_TEST_P(LookupTest, EqualRange) { REGISTER_TYPED_TEST_CASE_P(LookupTest, Count, Find, EqualRange); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_ diff --git a/absl/container/internal/unordered_set_members_test.h b/absl/container/internal/unordered_set_members_test.h new file mode 100644 index 00000000..756a95cb --- /dev/null +++ b/absl/container/internal/unordered_set_members_test.h @@ -0,0 +1,86 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_ +#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_ + +#include <type_traits> +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace container_internal { + +template <class UnordSet> +class MembersTest : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(MembersTest); + +template <typename T> +void UseType() {} + +TYPED_TEST_P(MembersTest, Typedefs) { + EXPECT_TRUE((std::is_same<typename TypeParam::key_type, + typename TypeParam::value_type>())); + EXPECT_TRUE((absl::conjunction< + absl::negation<std::is_signed<typename TypeParam::size_type>>, + std::is_integral<typename TypeParam::size_type>>())); + EXPECT_TRUE((absl::conjunction< + std::is_signed<typename TypeParam::difference_type>, + std::is_integral<typename TypeParam::difference_type>>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval<const typename TypeParam::hasher&>()( + std::declval<const typename TypeParam::key_type&>())), + size_t>())); + EXPECT_TRUE((std::is_convertible< + decltype(std::declval<const typename TypeParam::key_equal&>()( + std::declval<const typename TypeParam::key_type&>(), + std::declval<const typename TypeParam::key_type&>())), + bool>())); + EXPECT_TRUE((std::is_same<typename TypeParam::allocator_type::value_type, + typename TypeParam::value_type>())); + EXPECT_TRUE((std::is_same<typename TypeParam::value_type&, + typename TypeParam::reference>())); + EXPECT_TRUE((std::is_same<const typename TypeParam::value_type&, + typename TypeParam::const_reference>())); + EXPECT_TRUE((std::is_same<typename std::allocator_traits< + typename TypeParam::allocator_type>::pointer, + typename TypeParam::pointer>())); + EXPECT_TRUE( + (std::is_same<typename std::allocator_traits< + typename TypeParam::allocator_type>::const_pointer, + typename TypeParam::const_pointer>())); +} + +TYPED_TEST_P(MembersTest, SimpleFunctions) { + EXPECT_GT(TypeParam().max_size(), 0); +} + +TYPED_TEST_P(MembersTest, BeginEnd) { + TypeParam t = {typename TypeParam::value_type{}}; + EXPECT_EQ(t.begin(), t.cbegin()); + EXPECT_EQ(t.end(), t.cend()); + EXPECT_NE(t.begin(), t.end()); + EXPECT_NE(t.cbegin(), t.cend()); +} + +REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); + +} // namespace container_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MEMBERS_TEST_H_ diff --git a/absl/container/internal/unordered_set_modifiers_test.h b/absl/container/internal/unordered_set_modifiers_test.h index 0a1e9b1b..d3e534d3 100644 --- a/absl/container/internal/unordered_set_modifiers_test.h +++ b/absl/container/internal/unordered_set_modifiers_test.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,13 +21,13 @@ #include "absl/container/internal/hash_policy_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class UnordSet> class ModifiersTest : public ::testing::Test {}; -TYPED_TEST_CASE_P(ModifiersTest); +TYPED_TEST_SUITE_P(ModifiersTest); TYPED_TEST_P(ModifiersTest, Clear) { using T = hash_internal::GeneratedType<TypeParam>; @@ -184,7 +184,7 @@ REGISTER_TYPED_TEST_CASE_P(ModifiersTest, Clear, Insert, InsertHint, EraseKey, Swap); } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_ diff --git a/absl/container/internal/unordered_set_test.cc b/absl/container/internal/unordered_set_test.cc index 263059eb..2356e187 100644 --- a/absl/container/internal/unordered_set_test.cc +++ b/absl/container/internal/unordered_set_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,24 +16,26 @@ #include "absl/container/internal/unordered_set_constructor_test.h" #include "absl/container/internal/unordered_set_lookup_test.h" +#include "absl/container/internal/unordered_set_members_test.h" #include "absl/container/internal/unordered_set_modifiers_test.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { -using SetTypes = - ::testing::Types<std::unordered_set<int, StatefulTestingHash, - StatefulTestingEqual, Alloc<int>>, - std::unordered_set<std::string, StatefulTestingHash, - StatefulTestingEqual, Alloc<std::string>>>; +using SetTypes = ::testing::Types< + std::unordered_set<int, StatefulTestingHash, StatefulTestingEqual, + Alloc<int>>, + std::unordered_set<std::string, StatefulTestingHash, StatefulTestingEqual, + Alloc<std::string>>>; -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, ConstructorTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, LookupTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(UnorderedSet, ModifiersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ConstructorTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, LookupTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, MembersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ModifiersTest, SetTypes); } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index 48c7752e..addf120f 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -48,7 +48,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <class Key, class Value> class NodeHashMapPolicy; @@ -578,7 +578,7 @@ struct IsUnorderedContainer< } // namespace container_algorithm_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_NODE_HASH_MAP_H_ diff --git a/absl/container/node_hash_map_test.cc b/absl/container/node_hash_map_test.cc index 76a387b8..7ce7ca02 100644 --- a/absl/container/node_hash_map_test.cc +++ b/absl/container/node_hash_map_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,10 +17,11 @@ #include "absl/container/internal/tracked.h" #include "absl/container/internal/unordered_map_constructor_test.h" #include "absl/container/internal/unordered_map_lookup_test.h" +#include "absl/container/internal/unordered_map_members_test.h" #include "absl/container/internal/unordered_map_modifiers_test.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { @@ -35,9 +36,10 @@ using MapTypes = ::testing::Types< StatefulTestingEqual, Alloc<std::pair<const std::string, std::string>>>>; -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, ConstructorTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, LookupTest, MapTypes); -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashMap, ModifiersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ConstructorTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, LookupTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, MembersTest, MapTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ModifiersTest, MapTypes); using M = absl::node_hash_map<std::string, Tracked<int>>; @@ -216,5 +218,5 @@ TEST(NodeHashMap, MergeExtractInsert) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index c4179195..103d32d2 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -44,7 +44,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { template <typename T> struct NodeHashSetPolicy; @@ -484,7 +484,7 @@ struct IsUnorderedContainer<absl::node_hash_set<Key, Hash, KeyEqual, Allocator>> : std::true_type {}; } // namespace container_algorithm_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_CONTAINER_NODE_HASH_SET_H_ diff --git a/absl/container/node_hash_set_test.cc b/absl/container/node_hash_set_test.cc index 59f25285..65d125ed 100644 --- a/absl/container/node_hash_set_test.cc +++ b/absl/container/node_hash_set_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,10 +16,11 @@ #include "absl/container/internal/unordered_set_constructor_test.h" #include "absl/container/internal/unordered_set_lookup_test.h" +#include "absl/container/internal/unordered_set_members_test.h" #include "absl/container/internal/unordered_set_modifiers_test.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace container_internal { namespace { using ::absl::container_internal::hash_internal::Enum; @@ -30,14 +31,15 @@ using ::testing::UnorderedElementsAre; using SetTypes = ::testing::Types< node_hash_set<int, StatefulTestingHash, StatefulTestingEqual, Alloc<int>>, node_hash_set<std::string, StatefulTestingHash, StatefulTestingEqual, - Alloc<int>>, + Alloc<std::string>>, node_hash_set<Enum, StatefulTestingHash, StatefulTestingEqual, Alloc<Enum>>, node_hash_set<EnumClass, StatefulTestingHash, StatefulTestingEqual, Alloc<EnumClass>>>; -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, ConstructorTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, LookupTest, SetTypes); -INSTANTIATE_TYPED_TEST_CASE_P(NodeHashSet, ModifiersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, ConstructorTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, LookupTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, MembersTest, SetTypes); +INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashSet, ModifiersTest, SetTypes); TEST(NodeHashSet, MoveableNotCopyableCompiles) { node_hash_set<std::unique_ptr<void*>> t; @@ -101,5 +103,5 @@ TEST(NodeHashSet, MergeExtractInsert) { } // namespace } // namespace container_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/copts.bzl b/absl/copts.bzl deleted file mode 100644 index 49c4c9e0..00000000 --- a/absl/copts.bzl +++ /dev/null @@ -1,170 +0,0 @@ -"""absl specific copts. - -Flags specified here must not impact ABI. Code compiled with and without these -opts will be linked together, and in some cases headers compiled with and -without these options will be part of the same program. - -DO NOT CHANGE THIS FILE WITHOUT CHANGING THE SAME FLAG IN absl/CMake/AbseilConfigureCopts.cmake!! -""" -GCC_FLAGS = [ - "-Wall", - "-Wextra", - "-Wcast-qual", - "-Wconversion-null", - "-Wmissing-declarations", - "-Woverlength-strings", - "-Wpointer-arith", - "-Wunused-local-typedefs", - "-Wunused-result", - "-Wvarargs", - "-Wvla", # variable-length array - "-Wwrite-strings", - # Google style does not use unsigned integers, though STL containers - # have unsigned types. - "-Wno-sign-compare", -] - -GCC_TEST_FLAGS = [ - "-Wno-conversion-null", - "-Wno-missing-declarations", - "-Wno-sign-compare", - "-Wno-unused-function", - "-Wno-unused-parameter", - "-Wno-unused-private-field", -] - -# Docs on single flags is preceded by a comment. -# Docs on groups of flags is preceded by ###. - -LLVM_FLAGS = [ - "-Wall", - "-Wextra", - "-Weverything", - # Abseil does not support C++98 - "-Wno-c++98-compat-pedantic", - # Turns off all implicit conversion warnings. Most are re-enabled below. - "-Wno-conversion", - "-Wno-covered-switch-default", - "-Wno-deprecated", - "-Wno-disabled-macro-expansion", - "-Wno-double-promotion", - ### - # Turned off as they include valid C++ code. - "-Wno-comma", - "-Wno-extra-semi", - "-Wno-packed", - "-Wno-padded", - ### - # Google style does not use unsigned integers, though STL containers - # have unsigned types. - "-Wno-sign-compare", - ### - "-Wno-float-conversion", - "-Wno-float-equal", - "-Wno-format-nonliteral", - # Too aggressive: warns on Clang extensions enclosed in Clang-only - # compilation paths. - "-Wno-gcc-compat", - ### - # Some internal globals are necessary. Don't do this at home. - "-Wno-global-constructors", - "-Wno-exit-time-destructors", - ### - "-Wno-nested-anon-types", - "-Wno-non-modular-include-in-module", - "-Wno-old-style-cast", - # Warns on preferred usage of non-POD types such as string_view - "-Wno-range-loop-analysis", - "-Wno-reserved-id-macro", - "-Wno-shorten-64-to-32", - "-Wno-switch-enum", - "-Wno-thread-safety-negative", - "-Wno-undef", - "-Wno-unknown-warning-option", - "-Wno-unreachable-code", - # Causes warnings on include guards - "-Wno-unused-macros", - "-Wno-weak-vtables", - ### - # Implicit conversion warnings turned off by -Wno-conversion - # which are re-enabled below. - "-Wbitfield-enum-conversion", - "-Wbool-conversion", - "-Wconstant-conversion", - "-Wenum-conversion", - "-Wint-conversion", - "-Wliteral-conversion", - "-Wnon-literal-null-conversion", - "-Wnull-conversion", - "-Wobjc-literal-conversion", - "-Wno-sign-conversion", - "-Wstring-conversion", - ### -] - -LLVM_TEST_FLAGS = [ - "-Wno-c99-extensions", - "-Wno-missing-noreturn", - "-Wno-missing-prototypes", - "-Wno-missing-variable-declarations", - "-Wno-null-conversion", - "-Wno-shadow", - "-Wno-shift-sign-overflow", - "-Wno-sign-compare", - "-Wno-unused-function", - "-Wno-unused-member-function", - "-Wno-unused-parameter", - "-Wno-unused-private-field", - "-Wno-unused-template", - "-Wno-used-but-marked-unused", - "-Wno-zero-as-null-pointer-constant", - # gtest depends on this GNU extension being offered. - "-Wno-gnu-zero-variadic-macro-arguments", -] - -MSVC_FLAGS = [ - "/W3", - "/wd4005", # macro-redefinition - "/wd4068", # unknown pragma - "/wd4180", # qualifier applied to function type has no meaning; ignored - "/wd4244", # conversion from 'type1' to 'type2', possible loss of data - "/wd4267", # conversion from 'size_t' to 'type', possible loss of data - "/wd4800", # forcing value to bool 'true' or 'false' (performance warning) - "/DNOMINMAX", # Don't define min and max macros (windows.h) - "/DWIN32_LEAN_AND_MEAN", # Don't bloat namespace with incompatible winsock versions. - "/D_CRT_SECURE_NO_WARNINGS", # Don't warn about usage of insecure C functions. - "/D_SCL_SECURE_NO_WARNINGS", # Don't warm when the compiler encounters a function or - # variable that is marked as deprecated (same as /wd4996). - "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", # Introduced in VS 2017 15.8, - # before the member type would non-conformingly have an alignment of only alignof(max_align_t). -] - -MSVC_TEST_FLAGS = [ - "/wd4018", # signed/unsigned mismatch - "/wd4101", # unreferenced local variable - "/wd4503", # decorated name length exceeded, name was truncated -] - -# /Wall with msvc includes unhelpful warnings such as C4711, C4710, ... -ABSL_DEFAULT_COPTS = select({ - "//absl:windows": MSVC_FLAGS, - "//absl:llvm_compiler": LLVM_FLAGS, - "//conditions:default": GCC_FLAGS, -}) - -# in absence of modules (--compiler=gcc or -c opt), cc_tests leak their copts -# to their (included header) dependencies and fail to build outside absl -ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({ - "//absl:windows": MSVC_TEST_FLAGS, - "//absl:llvm_compiler": LLVM_TEST_FLAGS, - "//conditions:default": GCC_TEST_FLAGS, -}) - -ABSL_EXCEPTIONS_FLAG = select({ - "//absl:windows": ["/U_HAS_EXCEPTIONS", "/D_HAS_EXCEPTIONS=1", "/EHsc"], - "//conditions:default": ["-fexceptions"], -}) - -ABSL_EXCEPTIONS_FLAG_LINKOPTS = select({ - "//conditions:default": [], -}) diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake new file mode 100644 index 00000000..4ece4c6f --- /dev/null +++ b/absl/copts/AbseilConfigureCopts.cmake @@ -0,0 +1,60 @@ +# See absl/copts/copts.py and absl/copts/generate_copts.py +include(GENERATED_AbseilCopts) + +set(ABSL_LSAN_LINKOPTS "") +set(ABSL_HAVE_LSAN OFF) +set(ABSL_DEFAULT_LINKOPTS "") + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}") + set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}") + set(ABSL_EXCEPTIONS_FLAG "${ABSL_GCC_EXCEPTIONS_FLAGS}") + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}") +elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + # MATCHES so we get both Clang and AppleClang + if(MSVC) + # clang-cl is half MSVC, half LLVM + set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}") + set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_FLAGS};${ABSL_CLANG_CL_TEST_FLAGS}") + set(ABSL_EXCEPTIONS_FLAG "${ABSL_CLANG_CL_EXCEPTIONS_FLAGS}") + set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}") + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}") + else() + set(ABSL_DEFAULT_COPTS "${ABSL_LLVM_FLAGS}") + set(ABSL_TEST_COPTS "${ABSL_LLVM_FLAGS};${ABSL_LLVM_TEST_FLAGS}") + set(ABSL_EXCEPTIONS_FLAG "${ABSL_LLVM_EXCEPTIONS_FLAGS}") + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}") + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # AppleClang doesn't have lsan + # https://developer.apple.com/documentation/code_diagnostics + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5) + set(ABSL_LSAN_LINKOPTS "-fsanitize=leak") + set(ABSL_HAVE_LSAN ON) + endif() + endif() + endif() +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(ABSL_DEFAULT_COPTS "${ABSL_MSVC_FLAGS}") + set(ABSL_TEST_COPTS "${ABSL_MSVC_FLAGS};${ABSL_MSVC_TEST_FLAGS}") + set(ABSL_EXCEPTIONS_FLAG "${ABSL_MSVC_EXCEPTIONS_FLAGS}") + set(ABSL_DEFAULT_LINKOPTS "${ABSL_MSVC_LINKOPTS}") + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}") +else() + message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER}. Building with no default flags") + set(ABSL_DEFAULT_COPTS "") + set(ABSL_TEST_COPTS "") + set(ABSL_EXCEPTIONS_FLAG "") + set(ABSL_RANDOM_RANDEN_COPTS "") +endif() + +# This flag is used internally for Bazel builds and is kept here for consistency +set(ABSL_EXCEPTIONS_FLAG_LINKOPTS "") + +if("${CMAKE_CXX_STANDARD}" EQUAL 98) + message(FATAL_ERROR "Abseil requires at least C++11") +elseif(NOT "${CMAKE_CXX_STANDARD}") + message(STATUS "No CMAKE_CXX_STANDARD set, assuming 11") + set(ABSL_CXX_STANDARD 11) +else() + set(ABSL_CXX_STANDARD "${CMAKE_CXX_STANDARD}") +endif() diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake new file mode 100644 index 00000000..01bd40b2 --- /dev/null +++ b/absl/copts/GENERATED_AbseilCopts.cmake @@ -0,0 +1,236 @@ +# GENERATED! DO NOT MANUALLY EDIT THIS FILE. +# +# (1) Edit absl/copts/copts.py. +# (2) Run `python <path_to_absl>/copts/generate_copts.py`. + +list(APPEND ABSL_CLANG_CL_EXCEPTIONS_FLAGS + "/U_HAS_EXCEPTIONS" + "/D_HAS_EXCEPTIONS=1" + "/EHsc" +) + +list(APPEND ABSL_CLANG_CL_FLAGS + "/W3" + "-Wno-c++98-compat-pedantic" + "-Wno-conversion" + "-Wno-covered-switch-default" + "-Wno-deprecated" + "-Wno-disabled-macro-expansion" + "-Wno-double-promotion" + "-Wno-comma" + "-Wno-extra-semi" + "-Wno-extra-semi-stmt" + "-Wno-packed" + "-Wno-padded" + "-Wno-sign-compare" + "-Wno-float-conversion" + "-Wno-float-equal" + "-Wno-format-nonliteral" + "-Wno-gcc-compat" + "-Wno-global-constructors" + "-Wno-exit-time-destructors" + "-Wno-nested-anon-types" + "-Wno-non-modular-include-in-module" + "-Wno-old-style-cast" + "-Wno-range-loop-analysis" + "-Wno-reserved-id-macro" + "-Wno-shorten-64-to-32" + "-Wno-switch-enum" + "-Wno-thread-safety-negative" + "-Wno-unknown-warning-option" + "-Wno-unreachable-code" + "-Wno-unused-macros" + "-Wno-weak-vtables" + "-Wno-zero-as-null-pointer-constant" + "-Wbitfield-enum-conversion" + "-Wbool-conversion" + "-Wconstant-conversion" + "-Wenum-conversion" + "-Wint-conversion" + "-Wliteral-conversion" + "-Wnon-literal-null-conversion" + "-Wnull-conversion" + "-Wobjc-literal-conversion" + "-Wno-sign-conversion" + "-Wstring-conversion" + "/DNOMINMAX" + "/DWIN32_LEAN_AND_MEAN" + "/D_CRT_SECURE_NO_WARNINGS" + "/D_SCL_SECURE_NO_WARNINGS" + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE" +) + +list(APPEND ABSL_CLANG_CL_TEST_FLAGS + "-Wno-c99-extensions" + "-Wno-deprecated-declarations" + "-Wno-missing-noreturn" + "-Wno-missing-prototypes" + "-Wno-missing-variable-declarations" + "-Wno-null-conversion" + "-Wno-shadow" + "-Wno-shift-sign-overflow" + "-Wno-sign-compare" + "-Wno-unused-function" + "-Wno-unused-member-function" + "-Wno-unused-parameter" + "-Wno-unused-private-field" + "-Wno-unused-template" + "-Wno-used-but-marked-unused" + "-Wno-zero-as-null-pointer-constant" + "-Wno-gnu-zero-variadic-macro-arguments" +) + +list(APPEND ABSL_GCC_EXCEPTIONS_FLAGS + "-fexceptions" +) + +list(APPEND ABSL_GCC_FLAGS + "-Wall" + "-Wextra" + "-Wcast-qual" + "-Wconversion-null" + "-Wmissing-declarations" + "-Woverlength-strings" + "-Wpointer-arith" + "-Wunused-local-typedefs" + "-Wunused-result" + "-Wvarargs" + "-Wvla" + "-Wwrite-strings" + "-Wno-missing-field-initializers" + "-Wno-sign-compare" +) + +list(APPEND ABSL_GCC_TEST_FLAGS + "-Wno-conversion-null" + "-Wno-deprecated-declarations" + "-Wno-missing-declarations" + "-Wno-sign-compare" + "-Wno-unused-function" + "-Wno-unused-parameter" + "-Wno-unused-private-field" +) + +list(APPEND ABSL_LLVM_EXCEPTIONS_FLAGS + "-fexceptions" +) + +list(APPEND ABSL_LLVM_FLAGS + "-Wall" + "-Wextra" + "-Weverything" + "-Wno-c++98-compat-pedantic" + "-Wno-conversion" + "-Wno-covered-switch-default" + "-Wno-deprecated" + "-Wno-disabled-macro-expansion" + "-Wno-double-promotion" + "-Wno-comma" + "-Wno-extra-semi" + "-Wno-extra-semi-stmt" + "-Wno-packed" + "-Wno-padded" + "-Wno-sign-compare" + "-Wno-float-conversion" + "-Wno-float-equal" + "-Wno-format-nonliteral" + "-Wno-gcc-compat" + "-Wno-global-constructors" + "-Wno-exit-time-destructors" + "-Wno-nested-anon-types" + "-Wno-non-modular-include-in-module" + "-Wno-old-style-cast" + "-Wno-range-loop-analysis" + "-Wno-reserved-id-macro" + "-Wno-shorten-64-to-32" + "-Wno-switch-enum" + "-Wno-thread-safety-negative" + "-Wno-unknown-warning-option" + "-Wno-unreachable-code" + "-Wno-unused-macros" + "-Wno-weak-vtables" + "-Wno-zero-as-null-pointer-constant" + "-Wbitfield-enum-conversion" + "-Wbool-conversion" + "-Wconstant-conversion" + "-Wenum-conversion" + "-Wint-conversion" + "-Wliteral-conversion" + "-Wnon-literal-null-conversion" + "-Wnull-conversion" + "-Wobjc-literal-conversion" + "-Wno-sign-conversion" + "-Wstring-conversion" +) + +list(APPEND ABSL_LLVM_TEST_FLAGS + "-Wno-c99-extensions" + "-Wno-deprecated-declarations" + "-Wno-missing-noreturn" + "-Wno-missing-prototypes" + "-Wno-missing-variable-declarations" + "-Wno-null-conversion" + "-Wno-shadow" + "-Wno-shift-sign-overflow" + "-Wno-sign-compare" + "-Wno-unused-function" + "-Wno-unused-member-function" + "-Wno-unused-parameter" + "-Wno-unused-private-field" + "-Wno-unused-template" + "-Wno-used-but-marked-unused" + "-Wno-zero-as-null-pointer-constant" + "-Wno-gnu-zero-variadic-macro-arguments" +) + +list(APPEND ABSL_MSVC_EXCEPTIONS_FLAGS + "/U_HAS_EXCEPTIONS" + "/D_HAS_EXCEPTIONS=1" + "/EHsc" +) + +list(APPEND ABSL_MSVC_FLAGS + "/W3" + "/DNOMINMAX" + "/DWIN32_LEAN_AND_MEAN" + "/D_CRT_SECURE_NO_WARNINGS" + "/D_SCL_SECURE_NO_WARNINGS" + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE" + "/wd4005" + "/wd4068" + "/wd4180" + "/wd4244" + "/wd4267" + "/wd4503" + "/wd4800" +) + +list(APPEND ABSL_MSVC_LINKOPTS + "-ignore:4221" +) + +list(APPEND ABSL_MSVC_TEST_FLAGS + "/wd4018" + "/wd4101" + "/wd4503" + "/wd4996" + "/DNOMINMAX" +) + +list(APPEND ABSL_RANDOM_HWAES_ARM32_FLAGS + "-mfpu=neon" +) + +list(APPEND ABSL_RANDOM_HWAES_ARM64_FLAGS + "-march=armv8-a+crypto" +) + +list(APPEND ABSL_RANDOM_HWAES_MSVC_X64_FLAGS + "/O2" + "/Ob2" +) + +list(APPEND ABSL_RANDOM_HWAES_X64_FLAGS + "-maes" + "-msse4.1" +) diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl new file mode 100644 index 00000000..82f332f4 --- /dev/null +++ b/absl/copts/GENERATED_copts.bzl @@ -0,0 +1,237 @@ +"""GENERATED! DO NOT MANUALLY EDIT THIS FILE. + +(1) Edit absl/copts/copts.py. +(2) Run `python <path_to_absl>/copts/generate_copts.py`. +""" + +ABSL_CLANG_CL_EXCEPTIONS_FLAGS = [ + "/U_HAS_EXCEPTIONS", + "/D_HAS_EXCEPTIONS=1", + "/EHsc", +] + +ABSL_CLANG_CL_FLAGS = [ + "/W3", + "-Wno-c++98-compat-pedantic", + "-Wno-conversion", + "-Wno-covered-switch-default", + "-Wno-deprecated", + "-Wno-disabled-macro-expansion", + "-Wno-double-promotion", + "-Wno-comma", + "-Wno-extra-semi", + "-Wno-extra-semi-stmt", + "-Wno-packed", + "-Wno-padded", + "-Wno-sign-compare", + "-Wno-float-conversion", + "-Wno-float-equal", + "-Wno-format-nonliteral", + "-Wno-gcc-compat", + "-Wno-global-constructors", + "-Wno-exit-time-destructors", + "-Wno-nested-anon-types", + "-Wno-non-modular-include-in-module", + "-Wno-old-style-cast", + "-Wno-range-loop-analysis", + "-Wno-reserved-id-macro", + "-Wno-shorten-64-to-32", + "-Wno-switch-enum", + "-Wno-thread-safety-negative", + "-Wno-unknown-warning-option", + "-Wno-unreachable-code", + "-Wno-unused-macros", + "-Wno-weak-vtables", + "-Wno-zero-as-null-pointer-constant", + "-Wbitfield-enum-conversion", + "-Wbool-conversion", + "-Wconstant-conversion", + "-Wenum-conversion", + "-Wint-conversion", + "-Wliteral-conversion", + "-Wnon-literal-null-conversion", + "-Wnull-conversion", + "-Wobjc-literal-conversion", + "-Wno-sign-conversion", + "-Wstring-conversion", + "/DNOMINMAX", + "/DWIN32_LEAN_AND_MEAN", + "/D_CRT_SECURE_NO_WARNINGS", + "/D_SCL_SECURE_NO_WARNINGS", + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", +] + +ABSL_CLANG_CL_TEST_FLAGS = [ + "-Wno-c99-extensions", + "-Wno-deprecated-declarations", + "-Wno-missing-noreturn", + "-Wno-missing-prototypes", + "-Wno-missing-variable-declarations", + "-Wno-null-conversion", + "-Wno-shadow", + "-Wno-shift-sign-overflow", + "-Wno-sign-compare", + "-Wno-unused-function", + "-Wno-unused-member-function", + "-Wno-unused-parameter", + "-Wno-unused-private-field", + "-Wno-unused-template", + "-Wno-used-but-marked-unused", + "-Wno-zero-as-null-pointer-constant", + "-Wno-gnu-zero-variadic-macro-arguments", +] + +ABSL_GCC_EXCEPTIONS_FLAGS = [ + "-fexceptions", +] + +ABSL_GCC_FLAGS = [ + "-Wall", + "-Wextra", + "-Wcast-qual", + "-Wconversion-null", + "-Wmissing-declarations", + "-Woverlength-strings", + "-Wpointer-arith", + "-Wunused-local-typedefs", + "-Wunused-result", + "-Wvarargs", + "-Wvla", + "-Wwrite-strings", + "-Wno-missing-field-initializers", + "-Wno-sign-compare", +] + +ABSL_GCC_TEST_FLAGS = [ + "-Wno-conversion-null", + "-Wno-deprecated-declarations", + "-Wno-missing-declarations", + "-Wno-sign-compare", + "-Wno-unused-function", + "-Wno-unused-parameter", + "-Wno-unused-private-field", +] + +ABSL_LLVM_EXCEPTIONS_FLAGS = [ + "-fexceptions", +] + +ABSL_LLVM_FLAGS = [ + "-Wall", + "-Wextra", + "-Weverything", + "-Wno-c++98-compat-pedantic", + "-Wno-conversion", + "-Wno-covered-switch-default", + "-Wno-deprecated", + "-Wno-disabled-macro-expansion", + "-Wno-double-promotion", + "-Wno-comma", + "-Wno-extra-semi", + "-Wno-extra-semi-stmt", + "-Wno-packed", + "-Wno-padded", + "-Wno-sign-compare", + "-Wno-float-conversion", + "-Wno-float-equal", + "-Wno-format-nonliteral", + "-Wno-gcc-compat", + "-Wno-global-constructors", + "-Wno-exit-time-destructors", + "-Wno-nested-anon-types", + "-Wno-non-modular-include-in-module", + "-Wno-old-style-cast", + "-Wno-range-loop-analysis", + "-Wno-reserved-id-macro", + "-Wno-shorten-64-to-32", + "-Wno-switch-enum", + "-Wno-thread-safety-negative", + "-Wno-unknown-warning-option", + "-Wno-unreachable-code", + "-Wno-unused-macros", + "-Wno-weak-vtables", + "-Wno-zero-as-null-pointer-constant", + "-Wbitfield-enum-conversion", + "-Wbool-conversion", + "-Wconstant-conversion", + "-Wenum-conversion", + "-Wint-conversion", + "-Wliteral-conversion", + "-Wnon-literal-null-conversion", + "-Wnull-conversion", + "-Wobjc-literal-conversion", + "-Wno-sign-conversion", + "-Wstring-conversion", +] + +ABSL_LLVM_TEST_FLAGS = [ + "-Wno-c99-extensions", + "-Wno-deprecated-declarations", + "-Wno-missing-noreturn", + "-Wno-missing-prototypes", + "-Wno-missing-variable-declarations", + "-Wno-null-conversion", + "-Wno-shadow", + "-Wno-shift-sign-overflow", + "-Wno-sign-compare", + "-Wno-unused-function", + "-Wno-unused-member-function", + "-Wno-unused-parameter", + "-Wno-unused-private-field", + "-Wno-unused-template", + "-Wno-used-but-marked-unused", + "-Wno-zero-as-null-pointer-constant", + "-Wno-gnu-zero-variadic-macro-arguments", +] + +ABSL_MSVC_EXCEPTIONS_FLAGS = [ + "/U_HAS_EXCEPTIONS", + "/D_HAS_EXCEPTIONS=1", + "/EHsc", +] + +ABSL_MSVC_FLAGS = [ + "/W3", + "/DNOMINMAX", + "/DWIN32_LEAN_AND_MEAN", + "/D_CRT_SECURE_NO_WARNINGS", + "/D_SCL_SECURE_NO_WARNINGS", + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", + "/wd4005", + "/wd4068", + "/wd4180", + "/wd4244", + "/wd4267", + "/wd4503", + "/wd4800", +] + +ABSL_MSVC_LINKOPTS = [ + "-ignore:4221", +] + +ABSL_MSVC_TEST_FLAGS = [ + "/wd4018", + "/wd4101", + "/wd4503", + "/wd4996", + "/DNOMINMAX", +] + +ABSL_RANDOM_HWAES_ARM32_FLAGS = [ + "-mfpu=neon", +] + +ABSL_RANDOM_HWAES_ARM64_FLAGS = [ + "-march=armv8-a+crypto", +] + +ABSL_RANDOM_HWAES_MSVC_X64_FLAGS = [ + "/O2", + "/Ob2", +] + +ABSL_RANDOM_HWAES_X64_FLAGS = [ + "-maes", + "-msse4.1", +] diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl new file mode 100644 index 00000000..8c4efe77 --- /dev/null +++ b/absl/copts/configure_copts.bzl @@ -0,0 +1,89 @@ +"""absl specific copts. + +This file simply selects the correct options from the generated files. To +change Abseil copts, edit absl/copts/copts.py +""" + +load( + "//absl:copts/GENERATED_copts.bzl", + "ABSL_GCC_EXCEPTIONS_FLAGS", + "ABSL_GCC_FLAGS", + "ABSL_GCC_TEST_FLAGS", + "ABSL_LLVM_EXCEPTIONS_FLAGS", + "ABSL_LLVM_FLAGS", + "ABSL_LLVM_TEST_FLAGS", + "ABSL_MSVC_EXCEPTIONS_FLAGS", + "ABSL_MSVC_FLAGS", + "ABSL_MSVC_LINKOPTS", + "ABSL_MSVC_TEST_FLAGS", + "ABSL_RANDOM_HWAES_ARM32_FLAGS", + "ABSL_RANDOM_HWAES_ARM64_FLAGS", + "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS", + "ABSL_RANDOM_HWAES_X64_FLAGS", +) + +ABSL_DEFAULT_COPTS = select({ + "//absl:windows": ABSL_MSVC_FLAGS, + "//absl:llvm_compiler": ABSL_LLVM_FLAGS, + "//conditions:default": ABSL_GCC_FLAGS, +}) + +# in absence of modules (--compiler=gcc or -c opt), cc_tests leak their copts +# to their (included header) dependencies and fail to build outside absl +ABSL_TEST_COPTS = ABSL_DEFAULT_COPTS + select({ + "//absl:windows": ABSL_MSVC_TEST_FLAGS, + "//absl:llvm_compiler": ABSL_LLVM_TEST_FLAGS, + "//conditions:default": ABSL_GCC_TEST_FLAGS, +}) + +ABSL_EXCEPTIONS_FLAG = select({ + "//absl:windows": ABSL_MSVC_EXCEPTIONS_FLAGS, + "//absl:llvm_compiler": ABSL_LLVM_EXCEPTIONS_FLAGS, + "//conditions:default": ABSL_GCC_EXCEPTIONS_FLAGS, +}) + +ABSL_EXCEPTIONS_FLAG_LINKOPTS = select({ + "//conditions:default": [], +}) + +ABSL_DEFAULT_LINKOPTS = select({ + "//absl:windows": ABSL_MSVC_LINKOPTS, + "//conditions:default": [], +}) + +# ABSL_RANDOM_RANDEN_COPTS blaze copts flags which are required by each +# environment to build an accelerated RandenHwAes library. +ABSL_RANDOM_RANDEN_COPTS = select({ + # APPLE + ":cpu_darwin_x86_64": ABSL_RANDOM_HWAES_X64_FLAGS, + ":cpu_darwin": ABSL_RANDOM_HWAES_X64_FLAGS, + ":cpu_x64_windows_msvc": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, + ":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, + ":cpu_haswell": ABSL_RANDOM_HWAES_X64_FLAGS, + ":cpu_ppc": ["-mcrypto"], + + # Supported by default or unsupported. + "//conditions:default": [], +}) + +# absl_random_randen_copts_init: +# Initialize the config targets based on cpu, os, etc. used to select +# the required values for ABSL_RANDOM_RANDEN_COPTS +def absl_random_randen_copts_init(): + """Initialize the config_settings used by ABSL_RANDOM_RANDEN_COPTS.""" + + # CPU configs. + # These configs have consistent flags to enable HWAES intsructions. + cpu_configs = [ + "ppc", + "haswell", + "darwin_x86_64", + "darwin", + "x64_windows_msvc", + "x64_windows", + ] + for cpu in cpu_configs: + native.config_setting( + name = "cpu_%s" % cpu, + values = {"cpu": cpu}, + ) diff --git a/absl/copts/copts.py b/absl/copts/copts.py new file mode 100644 index 00000000..068abceb --- /dev/null +++ b/absl/copts/copts.py @@ -0,0 +1,214 @@ +"""Abseil compiler options. + +This is the source of truth for Abseil compiler options. To modify Abseil +compilation options: + + (1) Edit the appropriate list in this file based on the platform the flag is + needed on. + (2) Run `<path_to_absl>/copts/generate_copts.py`. + +The generated copts are consumed by configure_copts.bzl and +AbseilConfigureCopts.cmake. +""" + +# /Wall with msvc includes unhelpful warnings such as C4711, C4710, ... +MSVC_BIG_WARNING_FLAGS = [ + "/W3", +] + +LLVM_BIG_WARNING_FLAGS = [ + "-Wall", + "-Wextra", + "-Weverything", +] + +# Docs on single flags is preceded by a comment. +# Docs on groups of flags is preceded by ###. +LLVM_DISABLE_WARNINGS_FLAGS = [ + # Abseil does not support C++98 + "-Wno-c++98-compat-pedantic", + # Turns off all implicit conversion warnings. Most are re-enabled below. + "-Wno-conversion", + "-Wno-covered-switch-default", + "-Wno-deprecated", + "-Wno-disabled-macro-expansion", + "-Wno-double-promotion", + ### + # Turned off as they include valid C++ code. + "-Wno-comma", + "-Wno-extra-semi", + "-Wno-extra-semi-stmt", + "-Wno-packed", + "-Wno-padded", + ### + # Google style does not use unsigned integers, though STL containers + # have unsigned types. + "-Wno-sign-compare", + ### + "-Wno-float-conversion", + "-Wno-float-equal", + "-Wno-format-nonliteral", + # Too aggressive: warns on Clang extensions enclosed in Clang-only + # compilation paths. + "-Wno-gcc-compat", + ### + # Some internal globals are necessary. Don't do this at home. + "-Wno-global-constructors", + "-Wno-exit-time-destructors", + ### + "-Wno-nested-anon-types", + "-Wno-non-modular-include-in-module", + "-Wno-old-style-cast", + # Warns on preferred usage of non-POD types such as string_view + "-Wno-range-loop-analysis", + "-Wno-reserved-id-macro", + "-Wno-shorten-64-to-32", + "-Wno-switch-enum", + "-Wno-thread-safety-negative", + "-Wno-unknown-warning-option", + "-Wno-unreachable-code", + # Causes warnings on include guards + "-Wno-unused-macros", + "-Wno-weak-vtables", + # Causes warnings on usage of types/compare.h comparison operators. + "-Wno-zero-as-null-pointer-constant", + ### + # Implicit conversion warnings turned off by -Wno-conversion + # which are re-enabled below. + "-Wbitfield-enum-conversion", + "-Wbool-conversion", + "-Wconstant-conversion", + "-Wenum-conversion", + "-Wint-conversion", + "-Wliteral-conversion", + "-Wnon-literal-null-conversion", + "-Wnull-conversion", + "-Wobjc-literal-conversion", + "-Wno-sign-conversion", + "-Wstring-conversion", +] + +LLVM_TEST_DISABLE_WARNINGS_FLAGS = [ + "-Wno-c99-extensions", + "-Wno-deprecated-declarations", + "-Wno-missing-noreturn", + "-Wno-missing-prototypes", + "-Wno-missing-variable-declarations", + "-Wno-null-conversion", + "-Wno-shadow", + "-Wno-shift-sign-overflow", + "-Wno-sign-compare", + "-Wno-unused-function", + "-Wno-unused-member-function", + "-Wno-unused-parameter", + "-Wno-unused-private-field", + "-Wno-unused-template", + "-Wno-used-but-marked-unused", + "-Wno-zero-as-null-pointer-constant", + # gtest depends on this GNU extension being offered. + "-Wno-gnu-zero-variadic-macro-arguments", +] + +MSVC_STYLE_EXCEPTIONS_FLAGS = [ + "/U_HAS_EXCEPTIONS", + "/D_HAS_EXCEPTIONS=1", + "/EHsc" +] + +MSVC_DEFINES = [ + "/DNOMINMAX", # Don't define min and max macros (windows.h) + # Don't bloat namespace with incompatible winsock versions. + "/DWIN32_LEAN_AND_MEAN", + # Don't warn about usage of insecure C functions. + "/D_CRT_SECURE_NO_WARNINGS", + "/D_SCL_SECURE_NO_WARNINGS", + # Introduced in VS 2017 15.8, allow overaligned types in aligned_storage + "/D_ENABLE_EXTENDED_ALIGNED_STORAGE", +] + +COPT_VARS = { + "ABSL_GCC_FLAGS": [ + "-Wall", + "-Wextra", + "-Wcast-qual", + "-Wconversion-null", + "-Wmissing-declarations", + "-Woverlength-strings", + "-Wpointer-arith", + "-Wunused-local-typedefs", + "-Wunused-result", + "-Wvarargs", + "-Wvla", # variable-length array + "-Wwrite-strings", + # gcc-4.x has spurious missing field initializer warnings. + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750 + # Remove when gcc-4.x is no longer supported. + "-Wno-missing-field-initializers", + # Google style does not use unsigned integers, though STL containers + # have unsigned types. + "-Wno-sign-compare", + ], + "ABSL_GCC_TEST_FLAGS": [ + "-Wno-conversion-null", + "-Wno-deprecated-declarations", + "-Wno-missing-declarations", + "-Wno-sign-compare", + "-Wno-unused-function", + "-Wno-unused-parameter", + "-Wno-unused-private-field", + ], + "ABSL_GCC_EXCEPTIONS_FLAGS": ["-fexceptions"], + "ABSL_LLVM_FLAGS": + LLVM_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS, + "ABSL_LLVM_TEST_FLAGS": + LLVM_TEST_DISABLE_WARNINGS_FLAGS, + "ABSL_LLVM_EXCEPTIONS_FLAGS": ["-fexceptions"], + "ABSL_CLANG_CL_FLAGS": + (MSVC_BIG_WARNING_FLAGS + LLVM_DISABLE_WARNINGS_FLAGS + MSVC_DEFINES), + "ABSL_CLANG_CL_TEST_FLAGS": + LLVM_TEST_DISABLE_WARNINGS_FLAGS, + "ABSL_CLANG_CL_EXCEPTIONS_FLAGS": + MSVC_STYLE_EXCEPTIONS_FLAGS, + "ABSL_MSVC_FLAGS": + MSVC_BIG_WARNING_FLAGS + MSVC_DEFINES + [ + "/wd4005", # macro-redefinition + "/wd4068", # unknown pragma + # qualifier applied to function type has no meaning; ignored + "/wd4180", + # conversion from 'type1' to 'type2', possible loss of data + "/wd4244", + # conversion from 'size_t' to 'type', possible loss of data + "/wd4267", + # The decorated name was longer than the compiler limit + "/wd4503", + # forcing value to bool 'true' or 'false' (performance warning) + "/wd4800", + ], + "ABSL_MSVC_TEST_FLAGS": [ + "/wd4018", # signed/unsigned mismatch + "/wd4101", # unreferenced local variable + "/wd4503", # decorated name length exceeded, name was truncated + "/wd4996", # use of deprecated symbol + "/DNOMINMAX", # disable the min() and max() macros from <windows.h> + ], + "ABSL_MSVC_EXCEPTIONS_FLAGS": + MSVC_STYLE_EXCEPTIONS_FLAGS, + "ABSL_MSVC_LINKOPTS": [ + # Object file doesn't export any previously undefined symbols + "-ignore:4221", + ], + # "HWAES" is an abbreviation for "hardware AES" (AES - Advanced Encryption + # Standard). These flags are used for detecting whether or not the target + # architecture has hardware support for AES instructions which can be used + # to improve performance of some random bit generators. + "ABSL_RANDOM_HWAES_ARM64_FLAGS": ["-march=armv8-a+crypto"], + "ABSL_RANDOM_HWAES_ARM32_FLAGS": ["-mfpu=neon"], + "ABSL_RANDOM_HWAES_X64_FLAGS": [ + "-maes", + "-msse4.1", + ], + "ABSL_RANDOM_HWAES_MSVC_X64_FLAGS": [ + "/O2", # Maximize speed + "/Ob2", # Aggressive inlining + ], +} diff --git a/absl/copts/generate_copts.py b/absl/copts/generate_copts.py new file mode 100755 index 00000000..0e5dc9fa --- /dev/null +++ b/absl/copts/generate_copts.py @@ -0,0 +1,109 @@ +#!/usr/bin/python +"""Generate Abseil compile compile option configs. + +Usage: <path_to_absl>/copts/generate_copts.py + +The configs are generated from copts.py. +""" + +from os import path +import sys +from copts import COPT_VARS + + +# Helper functions +def file_header_lines(): + return [ + "GENERATED! DO NOT MANUALLY EDIT THIS FILE.", "", + "(1) Edit absl/copts/copts.py.", + "(2) Run `python <path_to_absl>/copts/generate_copts.py`." + ] + + +def flatten(*lists): + return [item for sublist in lists for item in sublist] + + +def relative_filename(filename): + return path.join(path.dirname(__file__), filename) + + +# Style classes. These contain all the syntactic styling needed to generate a +# copt file for different build tools. +class CMakeStyle(object): + """Style object for CMake copts file.""" + + def separator(self): + return "" + + def list_introducer(self, name): + return "list(APPEND " + name + + def list_closer(self): + return ")\n" + + def docstring(self): + return "\n".join((("# " + line).strip() for line in file_header_lines())) + + def filename(self): + return "GENERATED_AbseilCopts.cmake" + + +class StarlarkStyle(object): + """Style object for Starlark copts file.""" + + def separator(self): + return "," + + def list_introducer(self, name): + return name + " = [" + + def list_closer(self): + return "]\n" + + def docstring(self): + docstring_quotes = "\"\"\"" + return docstring_quotes + "\n".join( + flatten(file_header_lines(), [docstring_quotes])) + + def filename(self): + return "GENERATED_copts.bzl" + + +def copt_list(name, arg_list, style): + """Copt file generation.""" + + make_line = lambda s: " \"" + s + "\"" + style.separator() + external_str_list = [make_line(s) for s in arg_list] + + return "\n".join( + flatten( + [style.list_introducer(name)], + external_str_list, + [style.list_closer()])) + + +def generate_copt_file(style): + """Creates a generated copt file using the given style object. + + Args: + style: either StarlarkStyle() or CMakeStyle() + """ + with open(relative_filename(style.filename()), "w") as f: + f.write(style.docstring()) + f.write("\n") + for var_name, arg_list in sorted(COPT_VARS.items()): + f.write("\n") + f.write(copt_list(var_name, arg_list, style)) + + +def main(argv): + if len(argv) > 1: + raise RuntimeError("generate_copts needs no command line args") + + generate_copt_file(StarlarkStyle()) + generate_copt_file(CMakeStyle()) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index a8ebaea4..913cfafb 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,9 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -33,6 +34,7 @@ cc_library( ], hdrs = ["stacktrace.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":debugging_internal", "//absl/base", @@ -53,11 +55,13 @@ cc_library( "symbolize.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":debugging_internal", ":demangle_internal", "//absl/base", "//absl/base:core_headers", + "//absl/base:dynamic_annotations", "//absl/base:malloc_internal", ], ) @@ -66,6 +70,7 @@ cc_test( name = "symbolize_test", srcs = ["symbolize_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":stack_consumption", ":symbolize", @@ -85,6 +90,7 @@ cc_library( "internal/examine_stack.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ ":stacktrace", @@ -99,6 +105,7 @@ cc_library( srcs = ["failure_signal_handler.cc"], hdrs = ["failure_signal_handler.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":examine_stack", ":stacktrace", @@ -115,7 +122,7 @@ cc_test( linkopts = select({ "//absl:windows": [], "//conditions:default": ["-pthread"], - }), + }) + ABSL_DEFAULT_LINKOPTS, deps = [ ":failure_signal_handler", ":stacktrace", @@ -147,6 +154,7 @@ cc_library( "internal/vdso_support.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base", "//absl/base:core_headers", @@ -169,6 +177,7 @@ cc_test( name = "demangle_test", srcs = ["internal/demangle_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":demangle_internal", ":stack_consumption", @@ -181,22 +190,9 @@ cc_test( cc_library( name = "leak_check", - srcs = select({ - # The leak checking interface depends on weak function - # declarations that may not necessarily have definitions. - # Windows doesn't support this, and ios requires - # guaranteed definitions for weak symbols. - "//absl:ios": [], - "//absl:windows": [], - "//conditions:default": [ - "leak_check.cc", - ], - }), - hdrs = select({ - "//absl:ios": [], - "//absl:windows": [], - "//conditions:default": ["leak_check.h"], - }), + srcs = ["leak_check.cc"], + hdrs = ["leak_check.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = ["//absl/base:core_headers"], ) @@ -206,6 +202,7 @@ cc_library( cc_library( name = "leak_check_disable", srcs = ["leak_check_disable.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, linkstatic = 1, alwayslink = 1, ) @@ -226,6 +223,7 @@ cc_library( "//absl:llvm_compiler": ["-DLEAK_SANITIZER"], "//conditions:default": [], }), + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], ) @@ -235,6 +233,7 @@ cc_library( srcs = ["leak_check.cc"], hdrs = ["leak_check.h"], copts = ["-ULEAK_SANITIZER"], + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], ) @@ -245,7 +244,8 @@ cc_test( "//absl:llvm_compiler": ["-DABSL_EXPECT_LEAK_SANITIZER"], "//conditions:default": [], }), - linkopts = ABSL_LSAN_LINKOPTS, + linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + tags = ["notsan"], deps = [ ":leak_check_api_enabled_for_testing", "//absl/base", @@ -257,6 +257,8 @@ cc_test( name = "leak_check_no_lsan_test", srcs = ["leak_check_test.cc"], copts = ["-UABSL_EXPECT_LEAK_SANITIZER"], + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["noasan"], deps = [ ":leak_check_api_disabled_for_testing", "//absl/base", # for raw_logging @@ -271,7 +273,8 @@ cc_test( cc_test( name = "disabled_leak_check_test", srcs = ["leak_check_fail_test.cc"], - linkopts = ABSL_LSAN_LINKOPTS, + linkopts = ABSL_LSAN_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + tags = ["notsan"], deps = [ ":leak_check_api_enabled_for_testing", ":leak_check_disable", @@ -286,6 +289,7 @@ cc_library( srcs = ["internal/stack_consumption.cc"], hdrs = ["internal/stack_consumption.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ "//absl/base", @@ -297,6 +301,7 @@ cc_test( name = "stack_consumption_test", srcs = ["internal/stack_consumption_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":stack_consumption", "//absl/base", diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index f66688ba..001e2727 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -43,11 +43,14 @@ absl_cc_library( "symbolize_win32.inc" COPTS ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} DEPS absl::debugging_internal absl::demangle_internal absl::base absl::core_headers + absl::dynamic_annotations absl::malloc_internal PUBLIC ) @@ -181,9 +184,9 @@ absl_cc_library( NAME leak_check HDRS - "$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:leak_check.h>" + "leak_check.h" SRCS - "$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:leak_check.cc>" + "leak_check.cc" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -196,14 +199,11 @@ absl_cc_library( leak_check_disable SRCS "leak_check_disable.cc" + COPTS + ${ABSL_DEFAULT_COPTS} PUBLIC ) -# TODO(cohenjon) Move into the copts code. -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(ABSL_LSAN_LINKOPTS "-fsanitize=leak") -endif() - absl_cc_library( NAME leak_check_api_enabled_for_testing @@ -212,7 +212,8 @@ absl_cc_library( SRCS "leak_check.cc" COPTS - $<$<BOOL:${ABSL_USING_CLANG}>:-DLEAK_SANITIZER> + ${ABSL_DEFAULT_COPTS} + $<$<BOOL:${ABSL_HAVE_LSAN}>:-DLEAK_SANITIZER> TESTONLY ) @@ -224,6 +225,7 @@ absl_cc_library( SRCS "leak_check.cc" COPTS + ${ABSL_DEFAULT_COPTS} "-ULEAK_SANITIZER" TESTONLY ) @@ -234,7 +236,8 @@ absl_cc_test( SRCS "leak_check_test.cc" COPTS - "$<$<CXX_COMPILER_ID:Clang>:-DABSL_EXPECT_LEAK_SANITIZER>" + ${ABSL_DEFAULT_COPTS} + "$<$<BOOL:${ABSL_HAVE_LSAN}>:-DABSL_EXPECT_LEAK_SANITIZER>" LINKOPTS "${ABSL_LSAN_LINKOPTS}" DEPS @@ -249,6 +252,7 @@ absl_cc_test( SRCS "leak_check_test.cc" COPTS + ${ABSL_TEST_COPTS} "-UABSL_EXPECT_LEAK_SANITIZER" DEPS absl::leak_check_api_disabled_for_testing @@ -261,6 +265,8 @@ absl_cc_test( disabled_leak_check_test SRCS "leak_check_fail_test.cc" + COPTS + ${ABSL_TEST_COPTS} LINKOPTS "${ABSL_LSAN_LINKOPTS}" DEPS @@ -303,6 +309,8 @@ absl_cc_test( absl_cc_library( NAME debugging + COPTS + ${ABSL_DEFAULT_COPTS} DEPS absl::stacktrace absl::leak_check diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index a3a36f64..af651c72 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -47,7 +47,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options; @@ -357,5 +357,5 @@ void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) { } } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/failure_signal_handler.h b/absl/debugging/failure_signal_handler.h index 0aeb287f..87b202b0 100644 --- a/absl/debugging/failure_signal_handler.h +++ b/absl/debugging/failure_signal_handler.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -45,7 +45,7 @@ #define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // FailureSignalHandlerOptions // @@ -113,7 +113,7 @@ namespace debugging_internal { const char* FailureSignalToString(int signo); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_ diff --git a/absl/debugging/failure_signal_handler_test.cc b/absl/debugging/failure_signal_handler_test.cc index ba520910..bb2cc48e 100644 --- a/absl/debugging/failure_signal_handler_test.cc +++ b/absl/debugging/failure_signal_handler_test.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -133,16 +133,17 @@ constexpr int kFailureSignals[] = { }; std::string SignalParamToString(const ::testing::TestParamInfo<int>& info) { - std::string result = absl::debugging_internal::FailureSignalToString(info.param); + std::string result = + absl::debugging_internal::FailureSignalToString(info.param); if (result.empty()) { result = absl::StrCat(info.param); } return result; } -INSTANTIATE_TEST_CASE_P(AbslDeathTest, FailureSignalHandlerDeathTest, - ::testing::ValuesIn(kFailureSignals), - SignalParamToString); +INSTANTIATE_TEST_SUITE_P(AbslDeathTest, FailureSignalHandlerDeathTest, + ::testing::ValuesIn(kFailureSignals), + SignalParamToString); #endif // GTEST_HAS_DEATH_TEST diff --git a/absl/debugging/internal/address_is_readable.cc b/absl/debugging/internal/address_is_readable.cc index 2a83f4c8..64dd285b 100644 --- a/absl/debugging/internal/address_is_readable.cc +++ b/absl/debugging/internal/address_is_readable.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,14 +20,14 @@ #if !defined(__linux__) || defined(__ANDROID__) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // On platforms other than Linux, just return true. bool AddressIsReadable(const void* /* addr */) { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else @@ -42,7 +42,7 @@ bool AddressIsReadable(const void* /* addr */) { return true; } #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Pack a pid and two file descriptors into a 64-bit word, @@ -131,7 +131,7 @@ bool AddressIsReadable(const void *addr) { } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif diff --git a/absl/debugging/internal/address_is_readable.h b/absl/debugging/internal/address_is_readable.h index 3942f44b..dc626e5b 100644 --- a/absl/debugging/internal/address_is_readable.h +++ b/absl/debugging/internal/address_is_readable.h @@ -4,20 +4,19 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// #ifndef ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ #define ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Return whether the byte at *addr is readable, without faulting. @@ -25,7 +24,7 @@ namespace debugging_internal { bool AddressIsReadable(const void *addr); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_ADDRESS_IS_READABLE_H_ diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc index 57b9393f..976e47a6 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include <limits> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { typedef struct { @@ -750,8 +750,8 @@ static bool ParseSourceName(State *state) { // <local-source-name> ::= L <source-name> [<discriminator>] // // References: -// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 -// http://gcc.gnu.org/viewcvs?view=rev&revision=124467 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 +// https://gcc.gnu.org/viewcvs?view=rev&revision=124467 static bool ParseLocalSourceName(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; @@ -1169,6 +1169,12 @@ static bool ParseType(State *state) { } state->parse_state = copy; + // nullptr_t, i.e. decltype(nullptr). + if (ParseTwoCharToken(state, "Dn")) { + return true; + } + state->parse_state = copy; + if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && ParseType(state)) { return true; @@ -1869,5 +1875,5 @@ bool Demangle(const char *mangled, char *out, int out_size) { } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/internal/demangle.h b/absl/debugging/internal/demangle.h index 1f8722c7..20adbe9c 100644 --- a/absl/debugging/internal/demangle.h +++ b/absl/debugging/internal/demangle.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -54,7 +54,7 @@ #define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Demangle `mangled`. On success, return true and write the @@ -63,7 +63,7 @@ namespace debugging_internal { bool Demangle(const char *mangled, char *out, int out_size); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_ diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc index fa89fb80..7c50fe3a 100644 --- a/absl/debugging/internal/demangle_test.cc +++ b/absl/debugging/internal/demangle_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/memory/memory.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { namespace { @@ -82,8 +82,9 @@ TEST(Demangle, Clones) { // Tests that verify that Demangle footprint is within some limit. // They are not to be run under sanitizers as the sanitizers increase // stack consumption by about 4x. -#if defined(ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION) && \ - !ADDRESS_SANITIZER && !MEMORY_SANITIZER && !THREAD_SANITIZER +#if defined(ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION) && \ + !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ + !defined(THREAD_SANITIZER) static const char *g_mangled; static char g_demangle_buffer[4096]; @@ -177,6 +178,7 @@ static void TestOnInput(const char* input) { TEST(DemangleRegression, NegativeLength) { TestOnInput("_ZZn4"); } + TEST(DemangleRegression, DeeplyNestedArrayType) { const int depth = 100000; std::string data = "_ZStI"; @@ -189,5 +191,5 @@ TEST(DemangleRegression, DeeplyNestedArrayType) { } // namespace } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/internal/elf_mem_image.cc b/absl/debugging/internal/elf_mem_image.cc index e7e35e9c..a795117a 100644 --- a/absl/debugging/internal/elf_mem_image.cc +++ b/absl/debugging/internal/elf_mem_image.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,7 +38,7 @@ #define VERSYM_VERSION 0x7fff namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { namespace { @@ -376,7 +376,7 @@ void ElfMemImage::SymbolIterator::Update(int increment) { } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_ELF_MEM_IMAGE diff --git a/absl/debugging/internal/elf_mem_image.h b/absl/debugging/internal/elf_mem_image.h index 0adb5f5d..fcd32afe 100644 --- a/absl/debugging/internal/elf_mem_image.h +++ b/absl/debugging/internal/elf_mem_image.h @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -34,12 +34,12 @@ #define ABSL_HAVE_ELF_MEM_IMAGE 1 #endif -#if ABSL_HAVE_ELF_MEM_IMAGE +#ifdef ABSL_HAVE_ELF_MEM_IMAGE #include <link.h> // for ElfW namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // An in-memory ELF image (may not exist on disk). @@ -124,7 +124,7 @@ class ElfMemImage { }; } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_ELF_MEM_IMAGE diff --git a/absl/debugging/internal/examine_stack.cc b/absl/debugging/internal/examine_stack.cc index 4764355a..4739fbc5 100644 --- a/absl/debugging/internal/examine_stack.cc +++ b/absl/debugging/internal/examine_stack.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,7 +30,7 @@ #include "absl/debugging/symbolize.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Returns the program counter from signal context, nullptr if @@ -151,5 +151,5 @@ void DumpPCAndFrameSizesAndStackTrace( } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/internal/examine_stack.h b/absl/debugging/internal/examine_stack.h index 474fdd5e..861db75d 100644 --- a/absl/debugging/internal/examine_stack.h +++ b/absl/debugging/internal/examine_stack.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Returns the program counter from signal context, or nullptr if @@ -34,7 +34,7 @@ void DumpPCAndFrameSizesAndStackTrace( void (*writerfn)(const char*, void*), void* writerfn_arg); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_ diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc index 94b43e1d..8dfd94aa 100644 --- a/absl/debugging/internal/stack_consumption.cc +++ b/absl/debugging/internal/stack_consumption.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { namespace { @@ -168,7 +168,7 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION diff --git a/absl/debugging/internal/stack_consumption.h b/absl/debugging/internal/stack_consumption.h index 2defdf0d..1eb37eef 100644 --- a/absl/debugging/internal/stack_consumption.h +++ b/absl/debugging/internal/stack_consumption.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,7 @@ #define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1 namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Returns the stack consumption in bytes for the code exercised by @@ -39,7 +39,7 @@ namespace debugging_internal { int GetSignalHandlerStackConsumption(void (*signal_handler)(int)); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION diff --git a/absl/debugging/internal/stack_consumption_test.cc b/absl/debugging/internal/stack_consumption_test.cc index 022e508a..1c82d18a 100644 --- a/absl/debugging/internal/stack_consumption_test.cc +++ b/absl/debugging/internal/stack_consumption_test.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { namespace { @@ -44,7 +44,7 @@ TEST(SignalHandlerStackConsumptionTest, MeasuresStackConsumption) { } // namespace } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION diff --git a/absl/debugging/internal/stacktrace_aarch64-inl.inc b/absl/debugging/internal/stacktrace_aarch64-inl.inc index 2ed7ae1f..14b6e6b0 100644 --- a/absl/debugging/internal/stacktrace_aarch64-inl.inc +++ b/absl/debugging/internal/stacktrace_aarch64-inl.inc @@ -180,13 +180,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ diff --git a/absl/debugging/internal/stacktrace_arm-inl.inc b/absl/debugging/internal/stacktrace_arm-inl.inc index eb8ca77c..4f51d180 100644 --- a/absl/debugging/internal/stacktrace_arm-inl.inc +++ b/absl/debugging/internal/stacktrace_arm-inl.inc @@ -113,13 +113,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ diff --git a/absl/debugging/internal/stacktrace_config.h b/absl/debugging/internal/stacktrace_config.h index 578e4968..d4e8480a 100644 --- a/absl/debugging/internal/stacktrace_config.h +++ b/absl/debugging/internal/stacktrace_config.h @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/internal/stacktrace_generic-inl.inc b/absl/debugging/internal/stacktrace_generic-inl.inc index 823942af..39c47866 100644 --- a/absl/debugging/internal/stacktrace_generic-inl.inc +++ b/absl/debugging/internal/stacktrace_generic-inl.inc @@ -12,13 +12,47 @@ #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ #include <execinfo.h> +#include <atomic> #include <cstring> #include "absl/debugging/stacktrace.h" +// Sometimes, we can try to get a stack trace from within a stack +// trace, because we don't block signals inside this code (which would be too +// expensive: the two extra system calls per stack trace do matter here). +// That can cause a self-deadlock. +// Protect against such reentrant call by failing to get a stack trace. +// +// We use __thread here because the code here is extremely low level -- it is +// called while collecting stack traces from within malloc and mmap, and thus +// can not call anything which might call malloc or mmap itself. +static __thread int recursive = 0; + +// The stack trace function might be invoked very early in the program's +// execution (e.g. from the very first malloc if using tcmalloc). Also, the +// glibc implementation itself will trigger malloc the first time it is called. +// As such, we suppress usage of backtrace during this early stage of execution. +static std::atomic<bool> disable_stacktraces(true); // Disabled until healthy. +// Waiting until static initializers run seems to be late enough. +// This file is included into stacktrace.cc so this will only run once. +static int stacktraces_enabler = []() { + void* unused_stack[1]; + // Force the first backtrace to happen early to get the one-time shared lib + // loading (allocation) out of the way. After the first call it is much safer + // to use backtrace from a signal handler if we crash somewhere later. + backtrace(unused_stack, 1); + disable_stacktraces.store(false, std::memory_order_relaxed); + return 0; +}(); + template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, const void *ucp, int *min_dropped_frames) { + if (recursive || disable_stacktraces.load(std::memory_order_relaxed)) { + return 0; + } + ++recursive; + static_cast<void>(ucp); // Unused. static const int kStackLength = 64; void * stack[kStackLength]; @@ -46,17 +80,19 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } } + --recursive; + return result_count; } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_GENERIC_INL_H_ diff --git a/absl/debugging/internal/stacktrace_powerpc-inl.inc b/absl/debugging/internal/stacktrace_powerpc-inl.inc index aff2d516..ee6b38ff 100644 --- a/absl/debugging/internal/stacktrace_powerpc-inl.inc +++ b/absl/debugging/internal/stacktrace_powerpc-inl.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -14,8 +14,8 @@ // // Produce stack trace. I'm guessing (hoping!) the code is much like // for x86. For apple machines, at least, it seems to be; see -// http://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html -// http://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK +// https://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html +// https://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK // Linux has similar code: http://patchwork.ozlabs.org/linuxppc/patch?id=8882 #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ @@ -162,7 +162,7 @@ ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, const void *ucp, int *min_dropped_frames) { void **sp; - // Apple OS X uses an old version of gnu as -- both Darwin 7.9.0 (Panther) + // Apple macOS uses an old version of gnu as -- both Darwin 7.9.0 (Panther) // and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a // different asm syntax. I don't know quite the best way to discriminate // systems using the old as from the new one; I've gone with __APPLE__. @@ -236,13 +236,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_POWERPC_INL_H_ diff --git a/absl/debugging/internal/stacktrace_unimplemented-inl.inc b/absl/debugging/internal/stacktrace_unimplemented-inl.inc index 65345efc..b49a929a 100644 --- a/absl/debugging/internal/stacktrace_unimplemented-inl.inc +++ b/absl/debugging/internal/stacktrace_unimplemented-inl.inc @@ -12,13 +12,13 @@ static int UnwindImpl(void** /* result */, int* /* sizes */, } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_UNIMPLEMENTED_INL_H_ diff --git a/absl/debugging/internal/stacktrace_win32-inl.inc b/absl/debugging/internal/stacktrace_win32-inl.inc index 59e72785..0cd8c339 100644 --- a/absl/debugging/internal/stacktrace_win32-inl.inc +++ b/absl/debugging/internal/stacktrace_win32-inl.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -32,7 +32,7 @@ // server. // // This code is inspired by a patch from David Vitek: -// http://code.google.com/p/google-perftools/issues/detail?id=83 +// https://code.google.com/p/google-perftools/issues/detail?id=83 #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ @@ -73,13 +73,13 @@ static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return false; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_ diff --git a/absl/debugging/internal/stacktrace_x86-inl.inc b/absl/debugging/internal/stacktrace_x86-inl.inc index d29cd84b..ff0fd31f 100644 --- a/absl/debugging/internal/stacktrace_x86-inl.inc +++ b/absl/debugging/internal/stacktrace_x86-inl.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,6 +33,7 @@ #include "absl/debugging/internal/address_is_readable.h" #include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems #include "absl/debugging/stacktrace.h" + #include "absl/base/internal/raw_logging.h" #if defined(__linux__) && defined(__i386__) @@ -327,13 +328,13 @@ static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, } namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_X86_INL_INC_ diff --git a/absl/debugging/internal/symbolize.h b/absl/debugging/internal/symbolize.h index 2791105e..151bc77c 100644 --- a/absl/debugging/internal/symbolize.h +++ b/absl/debugging/internal/symbolize.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,6 @@ #include <cstddef> #include <cstdint> -#include "absl/base/port.h" // Needed for string vs std::string #ifdef ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #error ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE cannot be directly set @@ -34,7 +33,7 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // Iterates over all sections, invoking callback on each with the section name @@ -43,9 +42,9 @@ namespace debugging_internal { // Returns true on success; otherwise returns false in case of errors. // // This is not async-signal-safe. -bool ForEachSection( - int fd, const std::function<bool(const std::string& name, const ElfW(Shdr) &)>& - callback); +bool ForEachSection(int fd, + const std::function<bool(const std::string& name, + const ElfW(Shdr) &)>& callback); // Gets the section header for the given name, if it exists. Returns true on // success. Otherwise, returns false. @@ -53,13 +52,13 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, ElfW(Shdr) *out); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { struct SymbolDecoratorArgs { @@ -121,7 +120,7 @@ bool GetFileMappingHint(const void** start, const char** filename); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_INTERNAL_SYMBOLIZE_H_ diff --git a/absl/debugging/internal/vdso_support.cc b/absl/debugging/internal/vdso_support.cc index 85b52bc8..fa88e1d8 100644 --- a/absl/debugging/internal/vdso_support.cc +++ b/absl/debugging/internal/vdso_support.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,7 +38,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { ABSL_CONST_INIT @@ -188,7 +188,7 @@ static class VDSOInitHelper { } vdso_init_helper; } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_VDSO_SUPPORT diff --git a/absl/debugging/internal/vdso_support.h b/absl/debugging/internal/vdso_support.h index 035e5964..bc5fdb11 100644 --- a/absl/debugging/internal/vdso_support.h +++ b/absl/debugging/internal/vdso_support.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -53,7 +53,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { // NOTE: this class may be used from within tcmalloc, and can not @@ -150,7 +150,7 @@ class VDSOSupport { int GetCPU(); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_ELF_MEM_IMAGE diff --git a/absl/debugging/leak_check.cc b/absl/debugging/leak_check.cc index cf65280a..63e54ffa 100644 --- a/absl/debugging/leak_check.cc +++ b/absl/debugging/leak_check.cc @@ -4,13 +4,14 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + // Wrappers around lsan_interface functions. // When lsan is not linked in, these functions are not available, // therefore Abseil code which depends on these functions is conditioned on the @@ -20,14 +21,14 @@ #ifndef LEAK_SANITIZER namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { bool HaveLeakSanitizer() { return false; } void DoIgnoreLeak(const void*) { } void RegisterLivePointers(const void*, size_t) { } void UnRegisterLivePointers(const void*, size_t) { } LeakCheckDisabler::LeakCheckDisabler() { } LeakCheckDisabler::~LeakCheckDisabler() { } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else @@ -35,7 +36,7 @@ LeakCheckDisabler::~LeakCheckDisabler() { } #include <sanitizer/lsan_interface.h> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { bool HaveLeakSanitizer() { return true; } void DoIgnoreLeak(const void* ptr) { __lsan_ignore_object(ptr); } void RegisterLivePointers(const void* ptr, size_t size) { @@ -46,7 +47,7 @@ void UnRegisterLivePointers(const void* ptr, size_t size) { } LeakCheckDisabler::LeakCheckDisabler() { __lsan_disable(); } LeakCheckDisabler::~LeakCheckDisabler() { __lsan_enable(); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // LEAK_SANITIZER diff --git a/absl/debugging/leak_check.h b/absl/debugging/leak_check.h index f5e4fd87..e1215e38 100644 --- a/absl/debugging/leak_check.h +++ b/absl/debugging/leak_check.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,7 +33,7 @@ #include <cstddef> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // HaveLeakSanitizer() // @@ -105,7 +105,7 @@ void RegisterLivePointers(const void* ptr, size_t size); // `RegisterLivePointers()`, enabling leak checking of those pointers. void UnRegisterLivePointers(const void* ptr, size_t size); -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_LEAK_CHECK_H_ diff --git a/absl/debugging/leak_check_disable.cc b/absl/debugging/leak_check_disable.cc index df22c1ca..924d6e3d 100644 --- a/absl/debugging/leak_check_disable.cc +++ b/absl/debugging/leak_check_disable.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/leak_check_fail_test.cc b/absl/debugging/leak_check_fail_test.cc index bf541fe8..2887ceab 100644 --- a/absl/debugging/leak_check_fail_test.cc +++ b/absl/debugging/leak_check_fail_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/leak_check_test.cc b/absl/debugging/leak_check_test.cc index febd1ee4..93a7edd2 100644 --- a/absl/debugging/leak_check_test.cc +++ b/absl/debugging/leak_check_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/stacktrace.cc b/absl/debugging/stacktrace.cc index e991fc59..3052fb97 100644 --- a/absl/debugging/stacktrace.cc +++ b/absl/debugging/stacktrace.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -46,6 +46,7 @@ #include ABSL_STACKTRACE_INL_HEADER #else # error Cannot calculate stack trace: will need to write for your environment + # include "absl/debugging/internal/stacktrace_aarch64-inl.inc" # include "absl/debugging/internal/stacktrace_arm-inl.inc" # include "absl/debugging/internal/stacktrace_generic-inl.inc" @@ -56,7 +57,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { typedef int (*Unwinder)(void**, int*, int, int, const void*, int*); @@ -135,5 +136,5 @@ int DefaultStackUnwinder(void** pcs, int* sizes, int depth, int skip, return n; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/stacktrace.h b/absl/debugging/stacktrace.h index 7baf83bc..d7565aa4 100644 --- a/absl/debugging/stacktrace.h +++ b/absl/debugging/stacktrace.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -32,7 +32,7 @@ #define ABSL_DEBUGGING_STACKTRACE_H_ namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // GetStackFrames() // @@ -221,7 +221,7 @@ namespace debugging_internal { // working. extern bool StackTraceWorksForTest(); } // namespace debugging_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_STACKTRACE_H_ diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc index a35e24cc..24e3a7f0 100644 --- a/absl/debugging/symbolize.cc +++ b/absl/debugging/symbolize.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/debugging/symbolize.h b/absl/debugging/symbolize.h index fc606e87..ab5447c4 100644 --- a/absl/debugging/symbolize.h +++ b/absl/debugging/symbolize.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -55,7 +55,7 @@ #include "absl/debugging/internal/symbolize.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // InitializeSymbolizer() // @@ -93,7 +93,7 @@ void InitializeSymbolizer(const char* argv0); // } bool Symbolize(const void *pc, char *out, int out_size); -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_DEBUGGING_SYMBOLIZE_H_ diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index 37f77ca6..2bd7659f 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -76,7 +76,7 @@ #include "absl/debugging/internal/vdso_support.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Value of argv[0]. Used by MaybeInitializeObjFile(). static char *argv0_value = nullptr; @@ -306,7 +306,7 @@ class Symbolizer { char *tmp_buf, int tmp_buf_size); enum { - SYMBOL_BUF_SIZE = 2048, + SYMBOL_BUF_SIZE = 3072, TMP_BUF_SIZE = 1024, SYMBOL_CACHE_LINES = 128, }; @@ -763,37 +763,27 @@ FindSymbolResult Symbolizer::GetSymbolFromObjectFile( } } - // Consult a regular symbol table first. - if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum, - obj.elf_header.e_shoff, SHT_SYMTAB, &symtab, - tmp_buf, tmp_buf_size)) { - return SYMBOL_NOT_FOUND; - } - if (!ReadFromOffsetExact( - obj.fd, &strtab, sizeof(strtab), - obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { - return SYMBOL_NOT_FOUND; - } - const FindSymbolResult rc = - FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab, - opd_ptr, tmp_buf, tmp_buf_size); - if (rc != SYMBOL_NOT_FOUND) { - return rc; // Found the symbol in a regular symbol table. + // Consult a regular symbol table, then fall back to the dynamic symbol table. + for (const auto symbol_table_type : {SHT_SYMTAB, SHT_DYNSYM}) { + if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum, + obj.elf_header.e_shoff, symbol_table_type, + &symtab, tmp_buf, tmp_buf_size)) { + continue; + } + if (!ReadFromOffsetExact( + obj.fd, &strtab, sizeof(strtab), + obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { + continue; + } + const FindSymbolResult rc = + FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab, + opd_ptr, tmp_buf, tmp_buf_size); + if (rc != SYMBOL_NOT_FOUND) { + return rc; + } } - // If the symbol is not found, then consult a dynamic symbol table. - if (!GetSectionHeaderByType(obj.fd, obj.elf_header.e_shnum, - obj.elf_header.e_shoff, SHT_DYNSYM, &symtab, - tmp_buf, tmp_buf_size)) { - return SYMBOL_NOT_FOUND; - } - if (!ReadFromOffsetExact( - obj.fd, &strtab, sizeof(strtab), - obj.elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { - return SYMBOL_NOT_FOUND; - } - return FindSymbol(pc, obj.fd, out, out_size, relocation, &strtab, &symtab, - opd_ptr, tmp_buf, tmp_buf_size); + return SYMBOL_NOT_FOUND; } namespace { @@ -926,6 +916,14 @@ static const char *GetHex(const char *start, const char *end, return p; } +// Normally we are only interested in "r?x" maps. +// On the PowerPC, function pointers point to descriptors in the .opd +// section. The descriptors themselves are not executable code, so +// we need to relax the check below to "r??". +static bool ShouldUseMapping(const char *const flags) { + return flags[0] == 'r' && (kPlatformUsesOPDSections || flags[2] == 'x'); +} + // Read /proc/self/maps and run "callback" for each mmapped file found. If // "callback" returns false, stop scanning and return true. Else continue // scanning /proc/self/maps. Return true if no parse error is found. @@ -995,12 +993,8 @@ static ABSL_ATTRIBUTE_NOINLINE bool ReadAddrMap( return false; } - // Check flags. Normally we are only interested in "r-x" maps. On - // the PowerPC, function pointers point to descriptors in the .opd - // section. The descriptors themselves are not executable code. So - // we need to relax the check below to "r**". - if (memcmp(flags_start, "r-x", 3) != 0 && // Not a "r-x" map. - !(kPlatformUsesOPDSections && flags_start[0] == 'r')) { + // Check flags. + if (!ShouldUseMapping(flags_start)) { continue; // We skip this map. } ++cursor; // Skip ' '. @@ -1475,5 +1469,5 @@ bool Symbolize(const void *pc, char *out, int out_size) { return ok; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index 8029fbe9..08068c30 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -107,7 +107,8 @@ static const char *TrySymbolizeWithLimit(void *pc, int limit) { ABSL_RAW_CHECK(strnlen(heap_buffer.get(), limit) < limit, "absl::Symbolize() did not properly terminate the string"); strncpy(try_symbolize_buffer, heap_buffer.get(), - sizeof(try_symbolize_buffer)); + sizeof(try_symbolize_buffer) - 1); + try_symbolize_buffer[sizeof(try_symbolize_buffer) - 1] = '\0'; } return found ? try_symbolize_buffer : nullptr; @@ -392,16 +393,20 @@ TEST(Symbolize, ForEachSection) { extern "C" { inline void *ABSL_ATTRIBUTE_ALWAYS_INLINE inline_func() { void *pc = nullptr; -#if defined(__i386__) || defined(__x86_64__) - __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc)); +#if defined(__i386__) + __asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc)); +#elif defined(__x86_64__) + __asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc)); #endif return pc; } void *ABSL_ATTRIBUTE_NOINLINE non_inline_func() { void *pc = nullptr; -#if defined(__i386__) || defined(__x86_64__) - __asm__ __volatile__("call 1f; 1: pop %0" : "=r"(pc)); +#if defined(__i386__) + __asm__ __volatile__("call 1f;\n 1: pop %[PC]" : [ PC ] "=r"(pc)); +#elif defined(__x86_64__) + __asm__ __volatile__("leaq 0(%%rip),%[PC];\n" : [ PC ] "=r"(pc)); #endif return pc; } diff --git a/absl/debugging/symbolize_unimplemented.inc b/absl/debugging/symbolize_unimplemented.inc index 874a4228..0c1c1951 100644 --- a/absl/debugging/symbolize_unimplemented.inc +++ b/absl/debugging/symbolize_unimplemented.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace debugging_internal { @@ -27,11 +27,14 @@ bool RemoveAllSymbolDecorators(void) { return false; } bool RegisterFileMappingHint(const void *, const void *, uint64_t, const char *) { return false; } +bool GetFileMappingHint(const void **, const void **, uint64_t *, const char **) { + return false; +} } // namespace debugging_internal void InitializeSymbolizer(const char*) {} bool Symbolize(const void *, char *, int) { return false; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc index ee8fd55c..498ca2db 100644 --- a/absl/debugging/symbolize_win32.inc +++ b/absl/debugging/symbolize_win32.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,8 +16,14 @@ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx #include <windows.h> + +// MSVC header DbgHelp.h has a warning for an ignored typedef. +#pragma warning(push) +#pragma warning(disable:4091) #include <DbgHelp.h> -#pragma comment(lib, "DbgHelp") +#pragma warning(pop) + +#pragma comment(lib, "dbghelp.lib") #include <algorithm> #include <cstring> @@ -25,7 +31,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { static HANDLE process = NULL; @@ -72,5 +78,5 @@ bool Symbolize(const void *pc, char *out, int out_size) { return true; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel new file mode 100644 index 00000000..2fe61eaa --- /dev/null +++ b/absl/flags/BUILD.bazel @@ -0,0 +1,384 @@ +# +# Copyright 2019 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", + "ABSL_TEST_COPTS", +) + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +cc_library( + name = "internal", + srcs = [ + "internal/program_name.cc", + ], + hdrs = [ + "internal/path_util.h", + "internal/program_name.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [ + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "config", + srcs = [ + "usage_config.cc", + ], + hdrs = [ + "config.h", + "usage_config.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":internal", + "//absl/base:core_headers", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "marshalling", + srcs = [ + "marshalling.cc", + ], + hdrs = [ + "marshalling.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:core_headers", + "//absl/strings", + "//absl/strings:str_format", + ], +) + +cc_library( + name = "handle", + srcs = [ + "internal/commandlineflag.cc", + ], + hdrs = [ + "internal/commandlineflag.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [ + ":config", + ":marshalling", + "//absl/base", + "//absl/base:core_headers", + "//absl/strings", + "//absl/synchronization", + "//absl/types:optional", + ], +) + +cc_library( + name = "registry", + srcs = [ + "internal/registry.cc", + "internal/type_erased.cc", + ], + hdrs = [ + "internal/registry.h", + "internal/type_erased.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [ + ":config", + ":handle", + "//absl/base", + "//absl/base:core_headers", + "//absl/base:dynamic_annotations", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "flag", + srcs = [ + "flag.cc", + ], + hdrs = [ + "declare.h", + "flag.h", + "internal/flag.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":handle", + ":marshalling", + ":registry", + "//absl/base", + "//absl/base:core_headers", + "//absl/strings", + ], +) + +cc_library( + name = "usage_internal", + srcs = [ + "internal/usage.cc", + ], + hdrs = [ + "internal/usage.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [ + ":config", + ":flag", + ":handle", + ":internal", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "usage", + srcs = [ + "usage.cc", + ], + hdrs = [ + "usage.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":usage_internal", + "//absl/strings", + "//absl/synchronization", + ], +) + +cc_library( + name = "parse", + srcs = ["parse.cc"], + hdrs = [ + "internal/parse.h", + "parse.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":flag", + ":handle", + ":internal", + ":registry", + ":usage", + ":usage_internal", + "//absl/strings", + "//absl/synchronization", + ], +) + +############################################################################ +# Unit tests in alpahabetical order. + +cc_test( + name = "commandlineflag_test", + size = "small", + srcs = [ + "internal/commandlineflag_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":flag", + ":handle", + ":registry", + "//absl/memory", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "config_test", + size = "small", + srcs = [ + "config_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "flag_test", + size = "small", + srcs = [ + "flag_test.cc", + "flag_test_defs.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":flag", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "marshalling_test", + size = "small", + srcs = [ + "marshalling_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":marshalling", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "path_util_test", + size = "small", + srcs = [ + "internal/path_util_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":internal", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "parse_test", + size = "small", + srcs = [ + "parse_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":flag", + ":parse", + "//absl/base", + "//absl/base:scoped_set_env", + "//absl/strings", + "//absl/types:span", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "program_name_test", + size = "small", + srcs = [ + "internal/program_name_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":internal", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "type_erased_test", + size = "small", + srcs = [ + "internal/type_erased_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":flag", + ":registry", + "//absl/memory", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "usage_config_test", + size = "small", + srcs = [ + "usage_config_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":internal", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "usage_test", + size = "small", + srcs = [ + "internal/usage_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":flag", + ":internal", + ":parse", + ":usage", + ":usage_internal", + "//absl/memory", + "//absl/strings", + "@com_google_googletest//:gtest", + ], +) diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt new file mode 100644 index 00000000..fa1d4e17 --- /dev/null +++ b/absl/flags/CMakeLists.txt @@ -0,0 +1,346 @@ +# +# Copyright 2019 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_internal + SRCS + "internal/program_name.cc" + HDRS + "internal/path_util.h" + "internal/program_name.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::strings + absl::synchronization + PUBLIC +) + +absl_cc_library( + NAME + flags_config + SRCS + "usage_config.cc" + HDRS + "config.h" + "usage_config.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_internal + absl::core_headers + absl::strings + absl::synchronization +) + +absl_cc_library( + NAME + flags_marshalling + SRCS + "marshalling.cc" + HDRS + "marshalling.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::core_headers + absl::strings + absl::str_format +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_handle + SRCS + "internal/commandlineflag.cc" + HDRS + "internal/commandlineflag.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags_marshalling + absl::base + absl::core_headers + absl::strings + absl::synchronization + absl::optional +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_registry + SRCS + "internal/registry.cc" + "internal/type_erased.cc" + HDRS + "internal/registry.h" + "internal/type_erased.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags_handle + absl::base + absl::core_headers + absl::dynamic_annotations + absl::strings + absl::synchronization +) + +absl_cc_library( + NAME + flags + SRCS + "flag.cc" + HDRS + "declare.h" + "flag.h" + "internal/flag.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags_handle + absl::flags_marshalling + absl::flags_registry + absl::base + absl::core_headers + absl::strings +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + flags_usage_internal + SRCS + "internal/usage.cc" + HDRS + "internal/usage.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags + absl::flags_handle + absl::flags_internal + absl::strings + absl::synchronization +) + +absl_cc_library( + NAME + flags_usage + SRCS + "usage.cc" + HDRS + "usage.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_usage_internal + absl::strings + absl::synchronization +) + +absl_cc_library( + NAME + flags_parse + SRCS + "parse.cc" + HDRS + "internal/parse.h" + "parse.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_config + absl::flags + absl::flags_handle + absl::flags_internal + absl::flags_registry + absl::flags_usage + absl::strings + absl::synchronization +) + +############################################################################ +# Unit tests in alpahabetical order. + +absl_cc_test( + NAME + flags_commandlineflag_test + SRCS + "internal/commandlineflag_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags + absl::flags_handle + absl::flags_registry + absl::memory + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_config_test + SRCS + "config_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_config + gtest_main +) + +absl_cc_test( + NAME + flags_flag_test + SRCS + "flag_test.cc" + "flag_test_defs.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_marshalling_test + SRCS + "marshalling_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_marshalling + gtest_main +) + +absl_cc_test( + NAME + flags_parse_test + SRCS + "parse_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags + absl::base + absl::flags_parse + absl::scoped_set_env + absl::span + absl::strings + gmock_main +) + +absl_cc_test( + NAME + flags_path_util_test + SRCS + "internal/path_util_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_internal + gtest_main +) + +absl_cc_test( + NAME + flags_program_name_test + SRCS + "internal/program_name_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_internal + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_type_erased_test + SRCS + "internal/type_erased_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags + absl::flags_registry + absl::memory + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_usage_config_test + SRCS + "usage_config_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_config + absl::flags_internal + absl::strings + gtest_main +) + +absl_cc_test( + NAME + flags_usage_test + SRCS + "internal/usage_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::flags_config + absl::flags + absl::flags_internal + absl::flags_parse + absl::flags_usage + absl::memory + absl::strings + gtest +) diff --git a/absl/flags/config.h b/absl/flags/config.h new file mode 100644 index 00000000..a9fd97ad --- /dev/null +++ b/absl/flags/config.h @@ -0,0 +1,48 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_CONFIG_H_ +#define ABSL_FLAGS_CONFIG_H_ + +// Determine if we should strip string literals from the Flag objects. +// By default we strip string literals on mobile platforms. +#if !defined(ABSL_FLAGS_STRIP_NAMES) + +#if defined(__ANDROID__) +#define ABSL_FLAGS_STRIP_NAMES 1 + +#elif defined(__APPLE__) +#include <TargetConditionals.h> +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#define ABSL_FLAGS_STRIP_NAMES 1 +#elif defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED +#define ABSL_FLAGS_STRIP_NAMES 1 +#endif // TARGET_OS_* +#endif + +#endif // !defined(ABSL_FLAGS_STRIP_NAMES) + +#if !defined(ABSL_FLAGS_STRIP_NAMES) +// If ABSL_FLAGS_STRIP_NAMES wasn't set on the command line or above, +// the default is not to strip. +#define ABSL_FLAGS_STRIP_NAMES 0 +#endif + +#if !defined(ABSL_FLAGS_STRIP_HELP) +// By default, if we strip names, we also strip help. +#define ABSL_FLAGS_STRIP_HELP ABSL_FLAGS_STRIP_NAMES +#endif + +#endif // ABSL_FLAGS_CONFIG_H_ diff --git a/absl/flags/config_test.cc b/absl/flags/config_test.cc new file mode 100644 index 00000000..63899866 --- /dev/null +++ b/absl/flags/config_test.cc @@ -0,0 +1,61 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/config.h" + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#include "gtest/gtest.h" + +#ifndef ABSL_FLAGS_STRIP_NAMES +#error ABSL_FLAGS_STRIP_NAMES is not defined +#endif + +#ifndef ABSL_FLAGS_STRIP_HELP +#error ABSL_FLAGS_STRIP_HELP is not defined +#endif + +namespace { + +// Test that ABSL_FLAGS_STRIP_NAMES and ABSL_FLAGS_STRIP_HELP are configured how +// we expect them to be configured by default. If you override this +// configuration, this test will fail, but the code should still be safe to use. +TEST(FlagsConfigTest, Test) { +#if defined(__ANDROID__) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 1); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 1); +#elif defined(__myriad2__) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0); +#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 1); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 1); +#elif defined(TARGET_OS_EMBEDDED) && TARGET_OS_EMBEDDED + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 1); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 1); +#elif defined(__APPLE__) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0); +#elif defined(_WIN32) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0); +#elif defined(__linux__) + EXPECT_EQ(ABSL_FLAGS_STRIP_NAMES, 0); + EXPECT_EQ(ABSL_FLAGS_STRIP_HELP, 0); +#endif +} + +} // namespace diff --git a/absl/flags/declare.h b/absl/flags/declare.h new file mode 100644 index 00000000..7ef1d432 --- /dev/null +++ b/absl/flags/declare.h @@ -0,0 +1,60 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: declare.h +// ----------------------------------------------------------------------------- +// +// This file defines the ABSL_DECLARE_FLAG macro, allowing you to declare an +// `absl::Flag` for use within a translation unit. You should place this +// declaration within the header file associated with the .cc file that defines +// and owns the `Flag`. + +#ifndef ABSL_FLAGS_DECLARE_H_ +#define ABSL_FLAGS_DECLARE_H_ + +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// absl::Flag<T> represents a flag of type 'T' created by ABSL_FLAG. +template <typename T> +class Flag; + +} // namespace flags_internal + +// Flag +// +// Forward declaration of the `absl::Flag` type for use in defining the macro. +template <typename T> +using Flag = flags_internal::Flag<T>; + +} // inline namespace lts_2019_08_08 +} // namespace absl + +// ABSL_DECLARE_FLAG() +// +// This macro is a convenience for declaring use of an `absl::Flag` within a +// translation unit. This macro should be used within a header file to +// declare usage of the flag within any .cc file including that header file. +// +// The ABSL_DECLARE_FLAG(type, name) macro expands to: +// +// extern absl::Flag<type> FLAGS_name; +#define ABSL_DECLARE_FLAG(type, name) extern ::absl::Flag<type> FLAGS_##name + +#endif // ABSL_FLAGS_DECLARE_H_ diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc new file mode 100644 index 00000000..0858259b --- /dev/null +++ b/absl/flags/flag.cc @@ -0,0 +1,46 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/flag.h" + +#include <cstring> + +namespace absl { +inline namespace lts_2019_08_08 { + +// We want to validate the type mismatch between type definition and +// declaration. The lock-free implementation does not allow us to do it, +// so in debug builds we always use the slower implementation, which always +// validates the type. +#ifndef NDEBUG +#define ABSL_FLAGS_ATOMIC_GET(T) \ + T GetFlag(const absl::Flag<T>& flag) { return flag.Get(); } +#else +#define ABSL_FLAGS_ATOMIC_GET(T) \ + T GetFlag(const absl::Flag<T>& flag) { \ + T result; \ + if (flag.AtomicGet(&result)) { \ + return result; \ + } \ + return flag.Get(); \ + } +#endif + +ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET) + +#undef ABSL_FLAGS_ATOMIC_GET + +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/flag.h b/absl/flags/flag.h new file mode 100644 index 00000000..28925927 --- /dev/null +++ b/absl/flags/flag.h @@ -0,0 +1,259 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: flag.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::Flag<T>` type for holding command-line +// flag data, and abstractions to create, get and set such flag data. +// +// It is important to note that this type is **unspecified** (an implementation +// detail) and you do not construct or manipulate actual `absl::Flag<T>` +// instances. Instead, you define and declare flags using the +// `ABSL_FLAG()` and `ABSL_DECLARE_FLAG()` macros, and get and set flag values +// using the `absl::GetFlag()` and `absl::SetFlag()` functions. + +#ifndef ABSL_FLAGS_FLAG_H_ +#define ABSL_FLAGS_FLAG_H_ + +#include "absl/base/attributes.h" +#include "absl/base/casts.h" +#include "absl/flags/config.h" +#include "absl/flags/declare.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/flag.h" +#include "absl/flags/marshalling.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// Flag +// +// An `absl::Flag` holds a command-line flag value, providing a runtime +// parameter to a binary. Such flags should be defined in the global namespace +// and (preferably) in the module containing the binary's `main()` function. +// +// You should not construct and cannot use the `absl::Flag` type directly; +// instead, you should declare flags using the `ABSL_DECLARE_FLAG()` macro +// within a header file, and define your flag using `ABSL_FLAG()` within your +// header's associated `.cc` file. Such flags will be named `FLAGS_name`. +// +// Example: +// +// .h file +// +// // Declares usage of a flag named "FLAGS_count" +// ABSL_DECLARE_FLAG(int, count); +// +// .cc file +// +// // Defines a flag named "FLAGS_count" with a default `int` value of 0. +// ABSL_FLAG(int, count, 0, "Count of items to process"); +// +// No public methods of `absl::Flag<T>` are part of the Abseil Flags API. +template <typename T> +using Flag = flags_internal::Flag<T>; + +// GetFlag() +// +// Returns the value (of type `T`) of an `absl::Flag<T>` instance, by value. Do +// not construct an `absl::Flag<T>` directly and call `absl::GetFlag()`; +// instead, refer to flag's constructed variable name (e.g. `FLAGS_name`). +// Because this function returns by value and not by reference, it is +// thread-safe, but note that the operation may be expensive; as a result, avoid +// `absl::GetFlag()` within any tight loops. +// +// Example: +// +// // FLAGS_count is a Flag of type `int` +// int my_count = absl::GetFlag(FLAGS_count); +// +// // FLAGS_firstname is a Flag of type `std::string` +// std::string first_name = absl::GetFlag(FLAGS_firstname); +template <typename T> +T GetFlag(const absl::Flag<T>& flag) { +#define ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE(BIT) \ + static_assert( \ + !std::is_same<T, BIT>::value, \ + "Do not specify explicit template parameters to absl::GetFlag"); + ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE) +#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_VALIDATE + + return flag.Get(); +} + +// Overload for `GetFlag()` for types that support lock-free reads. +#define ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT(T) \ + extern T GetFlag(const absl::Flag<T>& flag); +ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT) +#undef ABSL_FLAGS_INTERNAL_LOCK_FREE_EXPORT + +// SetFlag() +// +// Sets the value of an `absl::Flag` to the value `v`. Do not construct an +// `absl::Flag<T>` directly and call `absl::SetFlag()`; instead, use the +// flag's variable name (e.g. `FLAGS_name`). This function is +// thread-safe, but is potentially expensive. Avoid setting flags in general, +// but especially within performance-critical code. +template <typename T> +void SetFlag(absl::Flag<T>* flag, const T& v) { + flag->Set(v); +} + +// Overload of `SetFlag()` to allow callers to pass in a value that is +// convertible to `T`. E.g., use this overload to pass a "const char*" when `T` +// is `std::string`. +template <typename T, typename V> +void SetFlag(absl::Flag<T>* flag, const V& v) { + T value(v); + flag->Set(value); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + + +// ABSL_FLAG() +// +// This macro defines an `absl::Flag<T>` instance of a specified type `T`: +// +// ABSL_FLAG(T, name, default_value, help); +// +// where: +// +// * `T` is a supported flag type (see the list of types in `marshalling.h`), +// * `name` designates the name of the flag (as a global variable +// `FLAGS_name`), +// * `default_value` is an expression holding the default value for this flag +// (which must be implicitly convertible to `T`), +// * `help` is the help text, which can also be an expression. +// +// This macro expands to a flag named 'FLAGS_name' of type 'T': +// +// absl::Flag<T> FLAGS_name = ...; +// +// Note that all such instances are created as global variables. +// +// For `ABSL_FLAG()` values that you wish to expose to other translation units, +// it is recommended to define those flags within the `.cc` file associated with +// the header where the flag is declared. +// +// Note: do not construct objects of type `absl::Flag<T>` directly. Only use the +// `ABSL_FLAG()` macro for such construction. +#define ABSL_FLAG(Type, name, default_value, help) \ + ABSL_FLAG_IMPL(Type, name, default_value, help) + +// ABSL_FLAG().OnUpdate() +// +// Defines a flag of type `T` with a callback attached: +// +// ABSL_FLAG(T, name, default_value, help).OnUpdate(callback); +// +// After any setting of the flag value, the callback will be called at least +// once. A rapid sequence of changes may be merged together into the same +// callback. No concurrent calls to the callback will be made for the same +// flag. Callbacks are allowed to read the current value of the flag but must +// not mutate that flag. +// +// The update mechanism guarantees "eventual consistency"; if the callback +// derives an auxiliary data structure from the flag value, it is guaranteed +// that eventually the flag value and the derived data structure will be +// consistent. +// +// Note: ABSL_FLAG.OnUpdate() does not have a public definition. Hence, this +// comment serves as its API documentation. + + +// ----------------------------------------------------------------------------- +// Implementation details below this section +// ----------------------------------------------------------------------------- + +// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_NAMES + +#if ABSL_FLAGS_STRIP_NAMES +#define ABSL_FLAG_IMPL_FLAGNAME(txt) "" +#define ABSL_FLAG_IMPL_FILENAME() "" +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar<T, false>(&flag) +#else +#define ABSL_FLAG_IMPL_FLAGNAME(txt) txt +#define ABSL_FLAG_IMPL_FILENAME() __FILE__ +#define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ + absl::flags_internal::FlagRegistrar<T, true>(&flag) +#endif + +// ABSL_FLAG_IMPL macro definition conditional on ABSL_FLAGS_STRIP_HELP + +#if ABSL_FLAGS_STRIP_HELP +#define ABSL_FLAG_IMPL_FLAGHELP(txt) absl::flags_internal::kStrippedFlagHelp +#else +#define ABSL_FLAG_IMPL_FLAGHELP(txt) txt +#endif + +#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ + static std::string AbslFlagsWrapHelp##name() { \ + return ABSL_FLAG_IMPL_FLAGHELP(txt); \ + } + +#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + static void* AbslFlagsInitFlag##name() { \ + return absl::flags_internal::MakeFromDefaultValue<Type>(default_value); \ + } + +// ABSL_FLAG_IMPL +// +// Note: Name of registrar object is not arbitrary. It is used to "grab" +// global name for FLAGS_no<flag_name> symbol, thus preventing the possibility +// of defining two flags with names foo and nofoo. +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ + ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name( \ + ABSL_FLAG_IMPL_FLAGNAME(#name), &AbslFlagsWrapHelp##name, \ + ABSL_FLAG_IMPL_FILENAME(), \ + &absl::flags_internal::FlagMarshallingOps<Type>, \ + &AbslFlagsInitFlag##name); \ + extern bool FLAGS_no##name; \ + bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) + +// ABSL_RETIRED_FLAG +// +// Designates the flag (which is usually pre-existing) as "retired." A retired +// flag is a flag that is now unused by the program, but may still be passed on +// the command line, usually by production scripts. A retired flag is ignored +// and code can't access it at runtime. +// +// This macro registers a retired flag with given name and type, with a name +// identical to the name of the original flag you are retiring. The retired +// flag's type can change over time, so that you can retire code to support a +// custom flag type. +// +// This macro has the same signature as `ABSL_FLAG`. To retire a flag, simply +// replace an `ABSL_FLAG` definition with `ABSL_RETIRED_FLAG`, leaving the +// arguments unchanged (unless of course you actually want to retire the flag +// type at this time as well). +// +// `default_value` is only used as a double check on the type. `explanation` is +// unused. +// TODO(rogeeff): Return an anonymous struct instead of bool, and place it into +// the unnamed namespace. +#define ABSL_RETIRED_FLAG(type, flagname, default_value, explanation) \ + ABSL_ATTRIBUTE_UNUSED static const bool ignored_##flagname = \ + ([] { return type(default_value); }, \ + absl::flags_internal::RetiredFlag<type>(#flagname)) + +#endif // ABSL_FLAGS_FLAG_H_ diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc new file mode 100644 index 00000000..1bcd7e96 --- /dev/null +++ b/absl/flags/flag_test.cc @@ -0,0 +1,482 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/flag.h" + +#include "gtest/gtest.h" +#include "absl/strings/match.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" + +ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag); +ABSL_DECLARE_FLAG(std::vector<std::string>, mistyped_string_flag); + +namespace { + +namespace flags = absl::flags_internal; + +std::string TestHelpMsg() { return "help"; } +template <typename T> +void* TestMakeDflt() { + return new T{}; +} +void TestCallback() {} + +template <typename T> +bool TestConstructionFor() { + constexpr flags::Flag<T> f1("f1", &TestHelpMsg, "file", + &absl::flags_internal::FlagMarshallingOps<T>, + &TestMakeDflt<T>); + EXPECT_EQ(f1.Name(), "f1"); + EXPECT_EQ(f1.Help(), "help"); + EXPECT_EQ(f1.Filename(), "file"); + + ABSL_CONST_INIT static flags::Flag<T> f2( + "f2", &TestHelpMsg, "file", &absl::flags_internal::FlagMarshallingOps<T>, + &TestMakeDflt<T>); + flags::FlagRegistrar<T, false>(&f2).OnUpdate(TestCallback); + + EXPECT_EQ(f2.Name(), "f2"); + EXPECT_EQ(f2.Help(), "help"); + EXPECT_EQ(f2.Filename(), "file"); + + return true; +} + +struct UDT { + UDT() = default; + UDT(const UDT&) = default; +}; +bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } +std::string AbslUnparseFlag(const UDT&) { return ""; } + +TEST(FlagTest, TestConstruction) { + TestConstructionFor<bool>(); + TestConstructionFor<int16_t>(); + TestConstructionFor<uint16_t>(); + TestConstructionFor<int32_t>(); + TestConstructionFor<uint32_t>(); + TestConstructionFor<int64_t>(); + TestConstructionFor<uint64_t>(); + TestConstructionFor<double>(); + TestConstructionFor<float>(); + TestConstructionFor<std::string>(); + + TestConstructionFor<UDT>(); +} + +// -------------------------------------------------------------------- + +} // namespace + +ABSL_DECLARE_FLAG(bool, test_flag_01); +ABSL_DECLARE_FLAG(int, test_flag_02); +ABSL_DECLARE_FLAG(int16_t, test_flag_03); +ABSL_DECLARE_FLAG(uint16_t, test_flag_04); +ABSL_DECLARE_FLAG(int32_t, test_flag_05); +ABSL_DECLARE_FLAG(uint32_t, test_flag_06); +ABSL_DECLARE_FLAG(int64_t, test_flag_07); +ABSL_DECLARE_FLAG(uint64_t, test_flag_08); +ABSL_DECLARE_FLAG(double, test_flag_09); +ABSL_DECLARE_FLAG(float, test_flag_10); +ABSL_DECLARE_FLAG(std::string, test_flag_11); + +namespace { + +#if !ABSL_FLAGS_STRIP_NAMES + +TEST(FlagTest, TestFlagDeclaration) { + // test that we can access flag objects. + EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01"); + EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02"); + EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03"); + EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04"); + EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05"); + EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06"); + EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07"); + EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08"); + EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09"); + EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10"); + EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11"); +} +#endif // !ABSL_FLAGS_STRIP_NAMES + +// -------------------------------------------------------------------- + +} // namespace + +ABSL_FLAG(bool, test_flag_01, true, "test flag 01"); +ABSL_FLAG(int, test_flag_02, 1234, "test flag 02"); +ABSL_FLAG(int16_t, test_flag_03, -34, "test flag 03"); +ABSL_FLAG(uint16_t, test_flag_04, 189, "test flag 04"); +ABSL_FLAG(int32_t, test_flag_05, 10765, "test flag 05"); +ABSL_FLAG(uint32_t, test_flag_06, 40000, "test flag 06"); +ABSL_FLAG(int64_t, test_flag_07, -1234567, "test flag 07"); +ABSL_FLAG(uint64_t, test_flag_08, 9876543, "test flag 08"); +ABSL_FLAG(double, test_flag_09, -9.876e-50, "test flag 09"); +ABSL_FLAG(float, test_flag_10, 1.234e12f, "test flag 10"); +ABSL_FLAG(std::string, test_flag_11, "", "test flag 11"); + +namespace { + +#if !ABSL_FLAGS_STRIP_NAMES +TEST(FlagTest, TestFlagDefinition) { + absl::string_view expected_file_name = "absl/flags/flag_test.cc"; + + EXPECT_EQ(FLAGS_test_flag_01.Name(), "test_flag_01"); + EXPECT_EQ(FLAGS_test_flag_01.Help(), "test flag 01"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_01.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_02.Name(), "test_flag_02"); + EXPECT_EQ(FLAGS_test_flag_02.Help(), "test flag 02"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_02.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_03.Name(), "test_flag_03"); + EXPECT_EQ(FLAGS_test_flag_03.Help(), "test flag 03"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_03.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_04.Name(), "test_flag_04"); + EXPECT_EQ(FLAGS_test_flag_04.Help(), "test flag 04"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_04.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_05.Name(), "test_flag_05"); + EXPECT_EQ(FLAGS_test_flag_05.Help(), "test flag 05"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_05.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_06.Name(), "test_flag_06"); + EXPECT_EQ(FLAGS_test_flag_06.Help(), "test flag 06"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_06.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_07.Name(), "test_flag_07"); + EXPECT_EQ(FLAGS_test_flag_07.Help(), "test flag 07"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_07.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_08.Name(), "test_flag_08"); + EXPECT_EQ(FLAGS_test_flag_08.Help(), "test flag 08"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_08.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_09.Name(), "test_flag_09"); + EXPECT_EQ(FLAGS_test_flag_09.Help(), "test flag 09"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_09.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_10.Name(), "test_flag_10"); + EXPECT_EQ(FLAGS_test_flag_10.Help(), "test flag 10"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_10.Filename(), expected_file_name)); + + EXPECT_EQ(FLAGS_test_flag_11.Name(), "test_flag_11"); + EXPECT_EQ(FLAGS_test_flag_11.Help(), "test flag 11"); + EXPECT_TRUE( + absl::EndsWith(FLAGS_test_flag_11.Filename(), expected_file_name)); +} +#endif // !ABSL_FLAGS_STRIP_NAMES + +// -------------------------------------------------------------------- + +TEST(FlagTest, TestDefault) { + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), ""); +} + +// -------------------------------------------------------------------- + +TEST(FlagTest, TestGetSet) { + absl::SetFlag(&FLAGS_test_flag_01, false); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), false); + + absl::SetFlag(&FLAGS_test_flag_02, 321); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 321); + + absl::SetFlag(&FLAGS_test_flag_03, 67); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), 67); + + absl::SetFlag(&FLAGS_test_flag_04, 1); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 1); + + absl::SetFlag(&FLAGS_test_flag_05, -908); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), -908); + + absl::SetFlag(&FLAGS_test_flag_06, 4001); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 4001); + + absl::SetFlag(&FLAGS_test_flag_07, -23456); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -23456); + + absl::SetFlag(&FLAGS_test_flag_08, 975310); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 975310); + + absl::SetFlag(&FLAGS_test_flag_09, 1.00001); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), 1.00001, 1e-10); + + absl::SetFlag(&FLAGS_test_flag_10, -3.54f); + EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), -3.54f, 1e-6f); + + absl::SetFlag(&FLAGS_test_flag_11, "asdf"); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "asdf"); +} + +// -------------------------------------------------------------------- + +int GetDflt1() { return 1; } + +} // namespace + +ABSL_FLAG(int, test_flag_12, GetDflt1(), "test flag 12"); +ABSL_FLAG(std::string, test_flag_13, absl::StrCat("AAA", "BBB"), + "test flag 13"); + +namespace { + +TEST(FlagTest, TestNonConstexprDefault) { + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), 1); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), "AAABBB"); +} + +// -------------------------------------------------------------------- + +} // namespace + +ABSL_FLAG(bool, test_flag_14, true, absl::StrCat("test ", "flag ", "14")); + +namespace { + +#if !ABSL_FLAGS_STRIP_HELP +TEST(FlagTest, TestNonConstexprHelp) { + EXPECT_EQ(FLAGS_test_flag_14.Help(), "test flag 14"); +} +#endif //! ABSL_FLAGS_STRIP_HELP + +// -------------------------------------------------------------------- + +int cb_test_value = -1; +void TestFlagCB(); + +} // namespace + +ABSL_FLAG(int, test_flag_with_cb, 100, "").OnUpdate(TestFlagCB); + +ABSL_FLAG(int, test_flag_with_lambda_cb, 200, "").OnUpdate([]() { + cb_test_value = absl::GetFlag(FLAGS_test_flag_with_lambda_cb) + + absl::GetFlag(FLAGS_test_flag_with_cb); +}); + +namespace { + +void TestFlagCB() { cb_test_value = absl::GetFlag(FLAGS_test_flag_with_cb); } + +// Tests side-effects of callback invocation. +TEST(FlagTest, CallbackInvocation) { + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_cb), 100); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_lambda_cb), 200); + EXPECT_EQ(cb_test_value, 300); + + absl::SetFlag(&FLAGS_test_flag_with_cb, 1); + EXPECT_EQ(cb_test_value, 1); + + absl::SetFlag(&FLAGS_test_flag_with_lambda_cb, 3); + EXPECT_EQ(cb_test_value, 4); +} + +// -------------------------------------------------------------------- + +struct CustomUDT { + CustomUDT() : a(1), b(1) {} + CustomUDT(int a_, int b_) : a(a_), b(b_) {} + + friend bool operator==(const CustomUDT& f1, const CustomUDT& f2) { + return f1.a == f2.a && f1.b == f2.b; + } + + int a; + int b; +}; +bool AbslParseFlag(absl::string_view in, CustomUDT* f, std::string*) { + std::vector<absl::string_view> parts = + absl::StrSplit(in, ':', absl::SkipWhitespace()); + + if (parts.size() != 2) return false; + + if (!absl::SimpleAtoi(parts[0], &f->a)) return false; + + if (!absl::SimpleAtoi(parts[1], &f->b)) return false; + + return true; +} +std::string AbslUnparseFlag(const CustomUDT& f) { + return absl::StrCat(f.a, ":", f.b); +} + +} // namespace + +ABSL_FLAG(CustomUDT, test_flag_15, CustomUDT(), "test flag 15"); + +namespace { + +TEST(FlagTest, TestCustomUDT) { + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(1, 1)); + absl::SetFlag(&FLAGS_test_flag_15, CustomUDT(2, 3)); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_15), CustomUDT(2, 3)); +} + +// MSVC produces link error on the type mismatch. +// Linux does not have build errors and validations work as expected. +#if 0 // !defined(_WIN32) && GTEST_HAS_DEATH_TEST + +TEST(Flagtest, TestTypeMismatchValidations) { + // For builtin types, GetFlag() only does validation in debug mode. + EXPECT_DEBUG_DEATH( + absl::GetFlag(FLAGS_mistyped_int_flag), + "Flag 'mistyped_int_flag' is defined as one type and declared " + "as another"); + EXPECT_DEATH(absl::SetFlag(&FLAGS_mistyped_int_flag, 0), + "Flag 'mistyped_int_flag' is defined as one type and declared " + "as another"); + + EXPECT_DEATH(absl::GetFlag(FLAGS_mistyped_string_flag), + "Flag 'mistyped_string_flag' is defined as one type and " + "declared as another"); + EXPECT_DEATH( + absl::SetFlag(&FLAGS_mistyped_string_flag, std::vector<std::string>{}), + "Flag 'mistyped_string_flag' is defined as one type and declared as " + "another"); +} + +#endif + +// -------------------------------------------------------------------- + +// A contrived type that offers implicit and explicit conversion from specific +// source types. +struct ConversionTestVal { + ConversionTestVal() = default; + explicit ConversionTestVal(int a_in) : a(a_in) {} + + enum class ViaImplicitConv { kTen = 10, kEleven }; + // NOLINTNEXTLINE + ConversionTestVal(ViaImplicitConv from) : a(static_cast<int>(from)) {} + + int a; +}; + +bool AbslParseFlag(absl::string_view in, ConversionTestVal* val_out, + std::string*) { + if (!absl::SimpleAtoi(in, &val_out->a)) { + return false; + } + return true; +} +std::string AbslUnparseFlag(const ConversionTestVal& val) { + return absl::StrCat(val.a); +} + +} // namespace + +// Flag default values can be specified with a value that converts to the flag +// value type implicitly. +ABSL_FLAG(ConversionTestVal, test_flag_16, + ConversionTestVal::ViaImplicitConv::kTen, "test flag 16"); + +namespace { + +TEST(FlagTest, CanSetViaImplicitConversion) { + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 10); + absl::SetFlag(&FLAGS_test_flag_16, + ConversionTestVal::ViaImplicitConv::kEleven); + EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_16).a, 11); +} + +// -------------------------------------------------------------------- + +struct NonDfltConstructible { + public: + // This constructor tests that we can initialize the flag with int value + NonDfltConstructible(int i) : value(i) {} // NOLINT + + // This constructor tests that we can't initialize the flag with char value + // but can with explicitly constructed NonDfltConstructible. + explicit NonDfltConstructible(char c) : value(100 + static_cast<int>(c)) {} + + int value; +}; + +bool AbslParseFlag(absl::string_view in, NonDfltConstructible* ndc_out, + std::string*) { + return absl::SimpleAtoi(in, &ndc_out->value); +} +std::string AbslUnparseFlag(const NonDfltConstructible& ndc) { + return absl::StrCat(ndc.value); +} + +} // namespace + +ABSL_FLAG(NonDfltConstructible, ndc_flag1, NonDfltConstructible('1'), + "Flag with non default constructible type"); +ABSL_FLAG(NonDfltConstructible, ndc_flag2, 0, + "Flag with non default constructible type"); + +namespace { + +TEST(FlagTest, TestNonDefaultConstructibleType) { + EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, '1' + 100); + EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 0); + + absl::SetFlag(&FLAGS_ndc_flag1, NonDfltConstructible('A')); + absl::SetFlag(&FLAGS_ndc_flag2, 25); + + EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, 'A' + 100); + EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 25); +} + +// -------------------------------------------------------------------- + +} // namespace + +ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr"); +ABSL_RETIRED_FLAG(int, old_int_flag, (int)std::sqrt(10), "old descr"); +ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); + +namespace { + +TEST(FlagTest, TestRetiredFlagRegistration) { + bool is_bool = false; + EXPECT_TRUE(flags::IsRetiredFlag("old_bool_flag", &is_bool)); + EXPECT_TRUE(is_bool); + EXPECT_TRUE(flags::IsRetiredFlag("old_int_flag", &is_bool)); + EXPECT_FALSE(is_bool); + EXPECT_TRUE(flags::IsRetiredFlag("old_str_flag", &is_bool)); + EXPECT_FALSE(is_bool); + EXPECT_FALSE(flags::IsRetiredFlag("some_other_flag", &is_bool)); +} + +} // namespace diff --git a/absl/flags/flag_test_defs.cc b/absl/flags/flag_test_defs.cc new file mode 100644 index 00000000..3366c580 --- /dev/null +++ b/absl/flags/flag_test_defs.cc @@ -0,0 +1,22 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file is used to test the mismatch of the flag type between definition +// and declaration. These are definitions. flag_test.cc contains declarations. +#include <string> +#include "absl/flags/flag.h" + +ABSL_FLAG(int, mistyped_int_flag, 0, ""); +ABSL_FLAG(std::string, mistyped_string_flag, "", ""); diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc new file mode 100644 index 00000000..f964165e --- /dev/null +++ b/absl/flags/internal/commandlineflag.cc @@ -0,0 +1,496 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/commandlineflag.h" + +#include <cassert> + +#include "absl/base/internal/raw_logging.h" +#include "absl/base/optimization.h" +#include "absl/flags/config.h" +#include "absl/flags/usage_config.h" +#include "absl/strings/str_cat.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// The help message indicating that the commandline flag has been +// 'stripped'. It will not show up when doing "-help" and its +// variants. The flag is stripped if ABSL_FLAGS_STRIP_HELP is set to 1 +// before including absl/flags/flag.h + +// This is used by this file, and also in commandlineflags_reporting.cc +const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001"; + +namespace { + +// Currently we only validate flag values for user-defined flag types. +bool ShouldValidateFlagValue(const CommandLineFlag& flag) { +#define DONT_VALIDATE(T) \ + if (flag.IsOfType<T>()) return false; + ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE) + DONT_VALIDATE(std::string) + DONT_VALIDATE(std::vector<std::string>) +#undef DONT_VALIDATE + + return true; +} + +} // namespace + +absl::Mutex* InitFlag(CommandLineFlag* flag) { + ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit); + absl::Mutex* mu; + + { + absl::MutexLock lock(&init_lock); + + if (flag->locks == nullptr) { // Must initialize Mutexes for this flag. + flag->locks = new flags_internal::CommandLineFlagLocks; + } + + mu = &flag->locks->primary_mu; + } + + { + absl::MutexLock lock(mu); + + if (!flag->retired && flag->def == nullptr) { + // Need to initialize def and cur fields. + flag->def = (*flag->make_init_value)(); + flag->cur = Clone(flag->op, flag->def); + UpdateCopy(flag); + flag->inited.store(true, std::memory_order_release); + flag->InvokeCallback(); + } + } + + flag->inited.store(true, std::memory_order_release); + return mu; +} + +// Ensure that the lazily initialized fields of *flag have been initialized, +// and return &flag->locks->primary_mu. +absl::Mutex* CommandLineFlag::InitFlagIfNecessary() const + LOCK_RETURNED(locks->primary_mu) { + if (!this->inited.load(std::memory_order_acquire)) { + return InitFlag(const_cast<CommandLineFlag*>(this)); + } + + // All fields initialized; this->locks is therefore safe to read. + return &this->locks->primary_mu; +} + +void CommandLineFlag::Destroy() const { + // Values are heap allocated for retired and Abseil Flags. + if (IsRetired() || IsAbseilFlag()) { + if (this->cur) Delete(this->op, this->cur); + if (this->def) Delete(this->op, this->def); + } + + delete this->locks; +} + +bool CommandLineFlag::IsModified() const { + absl::MutexLock l(InitFlagIfNecessary()); + return modified; +} + +void CommandLineFlag::SetModified(bool is_modified) { + absl::MutexLock l(InitFlagIfNecessary()); + modified = is_modified; +} + +bool CommandLineFlag::IsSpecifiedOnCommandLine() const { + absl::MutexLock l(InitFlagIfNecessary()); + return on_command_line; +} + +absl::string_view CommandLineFlag::Typename() const { + // We do not store/report type in Abseil Flags, so that user do not rely on in + // at runtime + if (IsAbseilFlag() || IsRetired()) return ""; + +#define HANDLE_V1_BUILTIN_TYPE(t) \ + if (IsOfType<t>()) { \ + return #t; \ + } + + HANDLE_V1_BUILTIN_TYPE(bool); + HANDLE_V1_BUILTIN_TYPE(int32_t); + HANDLE_V1_BUILTIN_TYPE(int64_t); + HANDLE_V1_BUILTIN_TYPE(uint64_t); + HANDLE_V1_BUILTIN_TYPE(double); +#undef HANDLE_V1_BUILTIN_TYPE + + if (IsOfType<std::string>()) { + return "string"; + } + + return ""; +} + +std::string CommandLineFlag::Filename() const { + return flags_internal::GetUsageConfig().normalize_filename(this->filename); +} + +std::string CommandLineFlag::DefaultValue() const { + absl::MutexLock l(InitFlagIfNecessary()); + + return Unparse(this->marshalling_op, this->def); +} + +std::string CommandLineFlag::CurrentValue() const { + absl::MutexLock l(InitFlagIfNecessary()); + + return Unparse(this->marshalling_op, this->cur); +} + +bool CommandLineFlag::HasValidatorFn() const { + absl::MutexLock l(InitFlagIfNecessary()); + + return this->validator != nullptr; +} + +bool CommandLineFlag::SetValidatorFn(FlagValidator fn) { + absl::MutexLock l(InitFlagIfNecessary()); + + // ok to register the same function over and over again + if (fn == this->validator) return true; + + // Can't set validator to a different function, unless reset first. + if (fn != nullptr && this->validator != nullptr) { + ABSL_INTERNAL_LOG( + WARNING, absl::StrCat("Ignoring SetValidatorFn() for flag '", Name(), + "': validate-fn already registered")); + + return false; + } + + this->validator = fn; + return true; +} + +bool CommandLineFlag::InvokeValidator(const void* value) const + EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) { + if (!this->validator) { + return true; + } + + (void)value; + + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Flag '", Name(), + "' of encapsulated type should not have a validator")); + + return false; +} + +void CommandLineFlag::SetCallback( + const flags_internal::FlagCallback mutation_callback) { + absl::MutexLock l(InitFlagIfNecessary()); + + callback = mutation_callback; + + InvokeCallback(); +} + +// If the flag has a mutation callback this function invokes it. While the +// callback is being invoked the primary flag's mutex is unlocked and it is +// re-locked back after call to callback is completed. Callback invocation is +// guarded by flag's secondary mutex instead which prevents concurrent callback +// invocation. Note that it is possible for other thread to grab the primary +// lock and update flag's value at any time during the callback invocation. +// This is by design. Callback can get a value of the flag if necessary, but it +// might be different from the value initiated the callback and it also can be +// different by the time the callback invocation is completed. +// Requires that *primary_lock be held in exclusive mode; it may be released +// and reacquired by the implementation. +void CommandLineFlag::InvokeCallback() + EXCLUSIVE_LOCKS_REQUIRED(this->locks->primary_mu) { + if (!this->callback) return; + + // The callback lock is guaranteed initialized, because *locks->primary_mu + // exists. + absl::Mutex* callback_mu = &this->locks->callback_mu; + + // When executing the callback we need the primary flag's mutex to be unlocked + // so that callback can retrieve the flag's value. + this->locks->primary_mu.Unlock(); + + { + absl::MutexLock lock(callback_mu); + this->callback(); + } + + this->locks->primary_mu.Lock(); +} + +// Attempts to parse supplied `value` string using parsing routine in the `flag` +// argument. If parsing is successful, it will try to validate that the parsed +// value is valid for the specified 'flag'. Finally this function stores the +// parsed value in 'dst' assuming it is a pointer to the flag's value type. In +// case if any error is encountered in either step, the error message is stored +// in 'err' +bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value, + std::string* err) + EXCLUSIVE_LOCKS_REQUIRED(flag->locks->primary_mu) { + void* tentative_value = Clone(flag->op, flag->def); + std::string parse_err; + if (!Parse(flag->marshalling_op, value, tentative_value, &parse_err)) { + auto type_name = flag->Typename(); + absl::string_view err_sep = parse_err.empty() ? "" : "; "; + absl::string_view typename_sep = type_name.empty() ? "" : " "; + *err = absl::StrCat("Illegal value '", value, "' specified for", + typename_sep, type_name, " flag '", flag->Name(), "'", + err_sep, parse_err); + Delete(flag->op, tentative_value); + return false; + } + + if (!flag->InvokeValidator(tentative_value)) { + *err = absl::StrCat("Failed validation of new value '", + Unparse(flag->marshalling_op, tentative_value), + "' for flag '", flag->Name(), "'"); + Delete(flag->op, tentative_value); + return false; + } + + flag->counter++; + Copy(flag->op, tentative_value, dst); + Delete(flag->op, tentative_value); + return true; +} + +// Sets the value of the flag based on specified string `value`. If the flag +// was successfully set to new value, it returns true. Otherwise, sets `err` +// to indicate the error, leaves the flag unchanged, and returns false. There +// are three ways to set the flag's value: +// * Update the current flag value +// * Update the flag's default value +// * Update the current flag value if it was never set before +// The mode is selected based on 'set_mode' parameter. +bool CommandLineFlag::SetFromString(absl::string_view value, + FlagSettingMode set_mode, + ValueSource source, std::string* err) { + if (IsRetired()) return false; + + absl::MutexLock l(InitFlagIfNecessary()); + + // Direct-access flags can be modified without going through the + // flag API. Detect such changes and update the flag->modified bit. + if (!IsAbseilFlag()) { + if (!this->modified && ChangedDirectly(this, this->cur, this->def)) { + this->modified = true; + } + } + + switch (set_mode) { + case SET_FLAGS_VALUE: { + // set or modify the flag's value + if (!TryParseLocked(this, this->cur, value, err)) return false; + this->modified = true; + UpdateCopy(this); + InvokeCallback(); + + if (source == kCommandLine) { + this->on_command_line = true; + } + break; + } + case SET_FLAG_IF_DEFAULT: { + // set the flag's value, but only if it hasn't been set by someone else + if (!this->modified) { + if (!TryParseLocked(this, this->cur, value, err)) return false; + this->modified = true; + UpdateCopy(this); + InvokeCallback(); + } else { + // TODO(rogeeff): review and fix this semantic. Currently we do not fail + // in this case if flag is modified. This is misleading since the flag's + // value is not updated even though we return true. + // *err = absl::StrCat(this->Name(), " is already set to ", + // CurrentValue(), "\n"); + // return false; + return true; + } + break; + } + case SET_FLAGS_DEFAULT: { + // modify the flag's default-value + if (!TryParseLocked(this, this->def, value, err)) return false; + + if (!this->modified) { + // Need to set both defvalue *and* current, in this case + Copy(this->op, this->def, this->cur); + UpdateCopy(this); + InvokeCallback(); + } + break; + } + default: { + // unknown set_mode + assert(false); + return false; + } + } + + return true; +} + +void CommandLineFlag::StoreAtomic(size_t size) { + int64_t t = 0; + assert(size <= sizeof(int64_t)); + memcpy(&t, this->cur, size); + this->atomic.store(t, std::memory_order_release); +} + +void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const { + std::string v = DefaultValue(); + + absl::MutexLock lock(InitFlagIfNecessary()); + + void* dst = Clone(this->op, this->def); + std::string error; + if (!flags_internal::Parse(this->marshalling_op, v, dst, &error)) { + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Flag ", Name(), " (from ", Filename(), + "): std::string form of default value '", v, + "' could not be parsed; error=", error)); + } + + // We do not compare dst to def since parsing/unparsing may make + // small changes, e.g., precision loss for floating point types. + Delete(this->op, dst); +} + +bool CommandLineFlag::ValidateDefaultValue() const { + absl::MutexLock lock(InitFlagIfNecessary()); + return InvokeValidator(this->def); +} + +bool CommandLineFlag::ValidateInputValue(absl::string_view value) const { + absl::MutexLock l(InitFlagIfNecessary()); // protect default value access + + void* obj = Clone(this->op, this->def); + std::string ignored_error; + const bool result = + flags_internal::Parse(this->marshalling_op, value, obj, &ignored_error) && + InvokeValidator(obj); + Delete(this->op, obj); + return result; +} + +const int64_t CommandLineFlag::kAtomicInit; + +void CommandLineFlag::Read(void* dst, + const flags_internal::FlagOpFn dst_op) const { + absl::ReaderMutexLock l(InitFlagIfNecessary()); + + // `dst_op` is the unmarshaling operation corresponding to the declaration + // visibile at the call site. `op` is the Flag's defined unmarshalling + // operation. They must match for this operation to be well-defined. + if (ABSL_PREDICT_FALSE(dst_op != op)) { + ABSL_INTERNAL_LOG( + ERROR, + absl::StrCat("Flag '", name, + "' is defined as one type and declared as another")); + } + CopyConstruct(op, cur, dst); +} + +void CommandLineFlag::Write(const void* src, + const flags_internal::FlagOpFn src_op) { + absl::MutexLock l(InitFlagIfNecessary()); + + // `src_op` is the marshalling operation corresponding to the declaration + // visible at the call site. `op` is the Flag's defined marshalling operation. + // They must match for this operation to be well-defined. + if (ABSL_PREDICT_FALSE(src_op != op)) { + ABSL_INTERNAL_LOG( + ERROR, + absl::StrCat("Flag '", name, + "' is defined as one type and declared as another")); + } + + if (ShouldValidateFlagValue(*this)) { + void* obj = Clone(op, src); + std::string ignored_error; + std::string src_as_str = Unparse(marshalling_op, src); + if (!Parse(marshalling_op, src_as_str, obj, &ignored_error) || + !InvokeValidator(obj)) { + ABSL_INTERNAL_LOG(ERROR, absl::StrCat("Attempt to set flag '", name, + "' to invalid value ", src_as_str)); + } + Delete(op, obj); + } + + modified = true; + counter++; + Copy(op, src, cur); + + UpdateCopy(this); + InvokeCallback(); +} + +std::string HelpText::GetHelpText() const { + if (help_function_) return help_function_(); + if (help_message_) return help_message_; + + return {}; +} + +// Update any copy of the flag value that is stored in an atomic word. +// In addition if flag has a mutation callback this function invokes it. +void UpdateCopy(CommandLineFlag* flag) { +#define STORE_ATOMIC(T) \ + else if (flag->IsOfType<T>()) { \ + flag->StoreAtomic(sizeof(T)); \ + } + + if (false) { + } + ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC) +#undef STORE_ATOMIC +} + +// Return true iff flag value was changed via direct-access. +bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) { + if (!flag->IsAbseilFlag()) { +// Need to compare values for direct-access flags. +#define CHANGED_FOR_TYPE(T) \ + if (flag->IsOfType<T>()) { \ + return *reinterpret_cast<const T*>(a) != *reinterpret_cast<const T*>(b); \ + } + + CHANGED_FOR_TYPE(bool); + CHANGED_FOR_TYPE(int32_t); + CHANGED_FOR_TYPE(int64_t); + CHANGED_FOR_TYPE(uint64_t); + CHANGED_FOR_TYPE(double); + CHANGED_FOR_TYPE(std::string); +#undef CHANGED_FOR_TYPE + } + + return false; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h new file mode 100644 index 00000000..382553d2 --- /dev/null +++ b/absl/flags/internal/commandlineflag.h @@ -0,0 +1,385 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ +#define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ + +#include <atomic> + +#include "absl/base/macros.h" +#include "absl/flags/marshalling.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/optional.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// Type-specific operations, eg., parsing, copying, etc. are provided +// by function specific to that type with a signature matching FlagOpFn. +enum FlagOp { + kDelete, + kClone, + kCopy, + kCopyConstruct, + kSizeof, + kParse, + kUnparse +}; +using FlagOpFn = void* (*)(FlagOp, const void*, void*); +using FlagMarshallingOpFn = void* (*)(FlagOp, const void*, void*, void*); + +// Options that control SetCommandLineOptionWithMode. +enum FlagSettingMode { + // update the flag's value unconditionally (can call this multiple times). + SET_FLAGS_VALUE, + // update the flag's value, but *only if* it has not yet been updated + // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef". + SET_FLAG_IF_DEFAULT, + // set the flag's default value to this. If the flag has not been updated + // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef") + // change the flag's current value to the new default value as well. + SET_FLAGS_DEFAULT +}; + +// Options that control SetFromString: Source of a value. +enum ValueSource { + // Flag is being set by value specified on a command line. + kCommandLine, + // Flag is being set by value specified in the code. + kProgrammaticChange, +}; + +// Signature for the help generation function used as an argument for the +// absl::Flag constructor. +using HelpGenFunc = std::string (*)(); + +// Signature for the function generating the initial flag value based (usually +// based on default value supplied in flag's definition) +using InitialValGenFunc = void* (*)(); + +struct CommandLineFlagInfo; + +// Signature for the mutation callback used by watched Flags +// The callback is noexcept. +// TODO(rogeeff): add noexcept after C++17 support is added. +using FlagCallback = void (*)(); + +using FlagValidator = bool (*)(); + +extern const char kStrippedFlagHelp[]; + +// The per-type function +template <typename T> +void* FlagOps(FlagOp op, const void* v1, void* v2) { + switch (op) { + case kDelete: + delete static_cast<const T*>(v1); + return nullptr; + case kClone: + return new T(*static_cast<const T*>(v1)); + case kCopy: + *static_cast<T*>(v2) = *static_cast<const T*>(v1); + return nullptr; + case kCopyConstruct: + new (v2) T(*static_cast<const T*>(v1)); + return nullptr; + case kSizeof: + return reinterpret_cast<void*>(sizeof(T)); + default: + return nullptr; + } +} + +template <typename T> +void* FlagMarshallingOps(FlagOp op, const void* v1, void* v2, void* v3) { + switch (op) { + case kParse: { + // initialize the temporary instance of type T based on current value in + // destination (which is going to be flag's default value). + T temp(*static_cast<T*>(v2)); + if (!absl::ParseFlag<T>(*static_cast<const absl::string_view*>(v1), &temp, + static_cast<std::string*>(v3))) { + return nullptr; + } + *static_cast<T*>(v2) = std::move(temp); + return v2; + } + case kUnparse: + *static_cast<std::string*>(v2) = + absl::UnparseFlag<T>(*static_cast<const T*>(v1)); + return nullptr; + default: + return nullptr; + } +} + +// Functions that invoke flag-type-specific operations. +inline void Delete(FlagOpFn op, const void* obj) { + op(flags_internal::kDelete, obj, nullptr); +} + +inline void* Clone(FlagOpFn op, const void* obj) { + return op(flags_internal::kClone, obj, nullptr); +} + +inline void Copy(FlagOpFn op, const void* src, void* dst) { + op(flags_internal::kCopy, src, dst); +} + +inline void CopyConstruct(FlagOpFn op, const void* src, void* dst) { + op(flags_internal::kCopyConstruct, src, dst); +} + +inline bool Parse(FlagMarshallingOpFn op, absl::string_view text, void* dst, + std::string* error) { + return op(flags_internal::kParse, &text, dst, error) != nullptr; +} + +inline std::string Unparse(FlagMarshallingOpFn op, const void* val) { + std::string result; + op(flags_internal::kUnparse, val, &result, nullptr); + return result; +} + +inline size_t Sizeof(FlagOpFn op) { + // This sequence of casts reverses the sequence from base::internal::FlagOps() + return static_cast<size_t>(reinterpret_cast<intptr_t>( + op(flags_internal::kSizeof, nullptr, nullptr))); +} + +// The following struct contains the locks in a CommandLineFlag struct. +// They are in a separate struct that is lazily allocated to avoid problems +// with static initialization and to avoid multiple allocations. +struct CommandLineFlagLocks { + absl::Mutex primary_mu; // protects several fields in CommandLineFlag + absl::Mutex callback_mu; // used to serialize callbacks +}; + +// Holds either a pointer to help text or a function which produces it. This is +// needed for supporting both static initialization of Flags while supporting +// the legacy registration framework. We can't use absl::variant<const char*, +// const char*(*)()> since anybody passing 0 or nullptr in to a CommandLineFlag +// would find an ambiguity. +class HelpText { + public: + static constexpr HelpText FromFunctionPointer(const HelpGenFunc fn) { + return HelpText(fn, nullptr); + } + static constexpr HelpText FromStaticCString(const char* msg) { + return HelpText(nullptr, msg); + } + + std::string GetHelpText() const; + + HelpText() = delete; + HelpText(const HelpText&) = default; + HelpText(HelpText&&) = default; + + private: + explicit constexpr HelpText(const HelpGenFunc fn, const char* msg) + : help_function_(fn), help_message_(msg) {} + + HelpGenFunc help_function_; + const char* help_message_; +}; + +// Holds all information for a flag. +struct CommandLineFlag { + constexpr CommandLineFlag( + const char* name_arg, HelpText help_text, const char* filename_arg, + const flags_internal::FlagOpFn op_arg, + const flags_internal::FlagMarshallingOpFn marshalling_op_arg, + const flags_internal::InitialValGenFunc initial_value_gen, + const bool retired_arg, void* def_arg, void* cur_arg) + : name(name_arg), + help(help_text), + filename(filename_arg), + op(op_arg), + marshalling_op(marshalling_op_arg), + make_init_value(initial_value_gen), + retired(retired_arg), + inited(false), + modified(false), + on_command_line(false), + validator(nullptr), + callback(nullptr), + def(def_arg), + cur(cur_arg), + counter(0), + atomic(kAtomicInit), + locks(nullptr) {} + + // Revert the init routine. + void Destroy() const; + + // Not copyable/assignable. + CommandLineFlag(const CommandLineFlag&) = delete; + CommandLineFlag& operator=(const CommandLineFlag&) = delete; + + absl::string_view Name() const { return name; } + std::string Help() const { return help.GetHelpText(); } + bool IsRetired() const { return this->retired; } + bool IsModified() const; + void SetModified(bool is_modified); + bool IsSpecifiedOnCommandLine() const; + // Returns true iff this is a handle to an Abseil Flag. + bool IsAbseilFlag() const { + // Set to null for V1 flags + return this->make_init_value != nullptr; + } + + absl::string_view Typename() const; + std::string Filename() const; + std::string DefaultValue() const; + std::string CurrentValue() const; + + bool HasValidatorFn() const; + bool SetValidatorFn(FlagValidator fn); + bool InvokeValidator(const void* value) const; + + // Return true iff flag has type T. + template <typename T> + inline bool IsOfType() const { + return this->op == &flags_internal::FlagOps<T>; + } + + // Attempts to retrieve the flag value. Returns value on success, + // absl::nullopt otherwise. + template <typename T> + absl::optional<T> Get() const { + if (IsRetired() || flags_internal::FlagOps<T> != this->op) + return absl::nullopt; + + T res; + Read(&res, flags_internal::FlagOps<T>); + + return res; + } + + void SetCallback(const flags_internal::FlagCallback mutation_callback); + void InvokeCallback(); + + // Sets the value of the flag based on specified std::string `value`. If the flag + // was successfully set to new value, it returns true. Otherwise, sets `error` + // to indicate the error, leaves the flag unchanged, and returns false. There + // are three ways to set the flag's value: + // * Update the current flag value + // * Update the flag's default value + // * Update the current flag value if it was never set before + // The mode is selected based on `set_mode` parameter. + bool SetFromString(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, std::string* error); + + void StoreAtomic(size_t size); + + void CheckDefaultValueParsingRoundtrip() const; + // Invoke the flag validators for old flags. + // TODO(rogeeff): implement proper validators for Abseil Flags + bool ValidateDefaultValue() const; + bool ValidateInputValue(absl::string_view value) const; + + // Constant configuration for a particular flag. + private: + const char* const name; + const HelpText help; + const char* const filename; + + protected: + const FlagOpFn op; // Type-specific handler + const FlagMarshallingOpFn marshalling_op; // Marshalling ops handler + const InitialValGenFunc make_init_value; // Makes initial value for the flag + const bool retired; // Is the flag retired? + std::atomic<bool> inited; // fields have been lazily initialized + + // Mutable state (guarded by locks->primary_mu). + bool modified; // Has flag value been modified? + bool on_command_line; // Specified on command line. + FlagValidator validator; // Validator function, or nullptr + FlagCallback callback; // Mutation callback, or nullptr + void* def; // Lazily initialized pointer to default value + void* cur; // Lazily initialized pointer to current value + int64_t counter; // Mutation counter + + // For some types, a copy of the current value is kept in an atomically + // accessible field. + static const int64_t kAtomicInit = 0xababababababababll; + std::atomic<int64_t> atomic; + + // Lazily initialized mutexes for this flag value. We cannot inline a + // SpinLock or Mutex here because those have non-constexpr constructors and + // so would prevent constant initialization of this type. + // TODO(rogeeff): fix it once Mutex has constexpr constructor + struct CommandLineFlagLocks* locks; // locks, laziliy allocated. + + // Ensure that the lazily initialized fields of *flag have been initialized, + // and return the lock which should be locked when flag's state is mutated. + absl::Mutex* InitFlagIfNecessary() const; + + // copy construct new value of flag's type in a memory referenced by dst + // based on current flag's value + void Read(void* dst, const flags_internal::FlagOpFn dst_op) const; + // updates flag's value to *src (locked) + void Write(const void* src, const flags_internal::FlagOpFn src_op); + + friend class FlagRegistry; + friend class FlagPtrMap; + friend class FlagSaverImpl; + friend void FillCommandLineFlagInfo(CommandLineFlag* flag, + CommandLineFlagInfo* result); + friend bool TryParseLocked(CommandLineFlag* flag, void* dst, + absl::string_view value, std::string* err); + friend absl::Mutex* InitFlag(CommandLineFlag* flag); +}; + +// Update any copy of the flag value that is stored in an atomic word. +// In addition if flag has a mutation callback this function invokes it. While +// callback is being invoked the primary flag's mutex is unlocked and it is +// re-locked back after call to callback is completed. Callback invocation is +// guarded by flag's secondary mutex instead which prevents concurrent callback +// invocation. Note that it is possible for other thread to grab the primary +// lock and update flag's value at any time during the callback invocation. +// This is by design. Callback can get a value of the flag if necessary, but it +// might be different from the value initiated the callback and it also can be +// different by the time the callback invocation is completed. +// Requires that *primary_lock be held in exclusive mode; it may be released +// and reacquired by the implementation. +void UpdateCopy(CommandLineFlag* flag); +// Return true iff flag value was changed via direct-access. +bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b); + +// This macro is the "source of truth" for the list of supported flag types we +// expect to perform lock free operations on. Specifically it generates code, +// a one argument macro operating on a type, supplied as a macro argument, for +// each type in the list. +#define ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(A) \ + A(bool) \ + A(short) \ + A(unsigned short) \ + A(int) \ + A(unsigned int) \ + A(long) \ + A(unsigned long) \ + A(long long) \ + A(unsigned long long) \ + A(double) \ + A(float) + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc new file mode 100644 index 00000000..f0d57adb --- /dev/null +++ b/absl/flags/internal/commandlineflag_test.cc @@ -0,0 +1,196 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/commandlineflag.h" + +#include "gtest/gtest.h" +#include "absl/flags/flag.h" +#include "absl/flags/internal/registry.h" +#include "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" + +ABSL_FLAG(int, int_flag, 201, "int_flag help"); +ABSL_FLAG(std::string, string_flag, "dflt", + absl::StrCat("string_flag", " help")); +ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help"); + +namespace { + +namespace flags = absl::flags_internal; + +class CommandLineFlagTest : public testing::Test { + protected: + void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); } + void TearDown() override { flag_saver_.reset(); } + + private: + std::unique_ptr<flags::FlagSaver> flag_saver_; +}; + +TEST_F(CommandLineFlagTest, TestAttributesAccessMethods) { + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + + ASSERT_TRUE(flag_01); + EXPECT_EQ(flag_01->Name(), "int_flag"); + EXPECT_EQ(flag_01->Help(), "int_flag help"); + EXPECT_EQ(flag_01->Typename(), ""); + EXPECT_TRUE(!flag_01->IsRetired()); + EXPECT_TRUE(flag_01->IsOfType<int>()); + EXPECT_TRUE(absl::EndsWith( + flag_01->Filename(), + "absl/flags/internal/commandlineflag_test.cc")); + + auto* flag_02 = flags::FindCommandLineFlag("string_flag"); + + ASSERT_TRUE(flag_02); + EXPECT_EQ(flag_02->Name(), "string_flag"); + EXPECT_EQ(flag_02->Help(), "string_flag help"); + EXPECT_EQ(flag_02->Typename(), ""); + EXPECT_TRUE(!flag_02->IsRetired()); + EXPECT_TRUE(flag_02->IsOfType<std::string>()); + EXPECT_TRUE(absl::EndsWith( + flag_02->Filename(), + "absl/flags/internal/commandlineflag_test.cc")); + + auto* flag_03 = flags::FindRetiredFlag("bool_retired_flag"); + + ASSERT_TRUE(flag_03); + EXPECT_EQ(flag_03->Name(), "bool_retired_flag"); + EXPECT_EQ(flag_03->Help(), ""); + EXPECT_EQ(flag_03->Typename(), ""); + EXPECT_TRUE(flag_03->IsRetired()); + EXPECT_TRUE(flag_03->IsOfType<bool>()); + EXPECT_EQ(flag_03->Filename(), "RETIRED"); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestValueAccessMethods) { + absl::SetFlag(&FLAGS_int_flag, 301); + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + + ASSERT_TRUE(flag_01); + EXPECT_EQ(flag_01->CurrentValue(), "301"); + EXPECT_EQ(flag_01->DefaultValue(), "201"); + + absl::SetFlag(&FLAGS_string_flag, "new_str_value"); + auto* flag_02 = flags::FindCommandLineFlag("string_flag"); + + ASSERT_TRUE(flag_02); + EXPECT_EQ(flag_02->CurrentValue(), "new_str_value"); + EXPECT_EQ(flag_02->DefaultValue(), "dflt"); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestSetFromStringCurrentValue) { + std::string err; + + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(flag_01->SetFromString("11", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(flag_01->SetFromString("-123", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(!flag_01->SetFromString("xyz", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); + EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'"); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(!flag_01->SetFromString("A1", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); + EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'"); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(flag_01->SetFromString("0x10", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16); + EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(flag_01->SetFromString("011", flags::SET_FLAGS_VALUE, + flags::kCommandLine, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); + EXPECT_TRUE(flag_01->IsSpecifiedOnCommandLine()); + + EXPECT_TRUE(!flag_01->SetFromString("", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'"); + + auto* flag_02 = flags::FindCommandLineFlag("string_flag"); + EXPECT_TRUE(flag_02->SetFromString("xyz", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz"); + + EXPECT_TRUE(flag_02->SetFromString("", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), ""); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestSetFromStringDefaultValue) { + std::string err; + + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + + EXPECT_TRUE(flag_01->SetFromString("111", flags::SET_FLAGS_DEFAULT, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(flag_01->DefaultValue(), "111"); + + auto* flag_02 = flags::FindCommandLineFlag("string_flag"); + + EXPECT_TRUE(flag_02->SetFromString("abc", flags::SET_FLAGS_DEFAULT, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(flag_02->DefaultValue(), "abc"); +} + +// -------------------------------------------------------------------- + +TEST_F(CommandLineFlagTest, TestSetFromStringIfDefault) { + std::string err; + + auto* flag_01 = flags::FindCommandLineFlag("int_flag"); + + EXPECT_TRUE(flag_01->SetFromString("22", flags::SET_FLAG_IF_DEFAULT, + flags::kProgrammaticChange, &err)) + << err; + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); + + EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); + // EXPECT_EQ(err, "ERROR: int_flag is already set to 22"); + + // Reset back to default value + EXPECT_TRUE(flag_01->SetFromString("201", flags::SET_FLAGS_VALUE, + flags::kProgrammaticChange, &err)); + + EXPECT_TRUE(flag_01->SetFromString("33", flags::SET_FLAG_IF_DEFAULT, + flags::kProgrammaticChange, &err)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201); + // EXPECT_EQ(err, "ERROR: int_flag is already set to 201"); +} + +} // namespace diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h new file mode 100644 index 00000000..9a5d9b4b --- /dev/null +++ b/absl/flags/internal/flag.h @@ -0,0 +1,123 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_FLAG_H_ +#define ABSL_FLAGS_INTERNAL_FLAG_H_ + +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/registry.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// This is "unspecified" implementation of absl::Flag<T> type. +template <typename T> +class Flag : public flags_internal::CommandLineFlag { + public: + constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen, + const char* filename, + const flags_internal::FlagMarshallingOpFn marshalling_op_arg, + const flags_internal::InitialValGenFunc initial_value_gen) + : flags_internal::CommandLineFlag( + name, flags_internal::HelpText::FromFunctionPointer(help_gen), + filename, &flags_internal::FlagOps<T>, marshalling_op_arg, + initial_value_gen, + /*retired_arg=*/false, /*def_arg=*/nullptr, + /*cur_arg=*/nullptr) {} + + T Get() const { + // Implementation notes: + // + // We are wrapping a union around the value of `T` to serve three purposes: + // + // 1. `U.value` has correct size and alignment for a value of type `T` + // 2. The `U.value` constructor is not invoked since U's constructor does + // not + // do it explicitly. + // 3. The `U.value` destructor is invoked since U's destructor does it + // explicitly. This makes `U` a kind of RAII wrapper around non default + // constructible value of T, which is destructed when we leave the + // scope. We do need to destroy U.value, which is constructed by + // CommandLineFlag::Read even though we left it in a moved-from state + // after std::move. + // + // All of this serves to avoid requiring `T` being default constructible. + union U { + T value; + U() {} + ~U() { value.~T(); } + }; + U u; + + this->Read(&u.value, &flags_internal::FlagOps<T>); + return std::move(u.value); + } + + bool AtomicGet(T* v) const { + const int64_t r = this->atomic.load(std::memory_order_acquire); + if (r != flags_internal::CommandLineFlag::kAtomicInit) { + memcpy(v, &r, sizeof(T)); + return true; + } + + return false; + } + + void Set(const T& v) { this->Write(&v, &flags_internal::FlagOps<T>); } +}; + +// This class facilitates Flag object registration and tail expression-based +// flag definition, for example: +// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); +template <typename T, bool do_register> +class FlagRegistrar { + public: + explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) { + if (do_register) flags_internal::RegisterCommandLineFlag(flag_); + } + + FlagRegistrar& OnUpdate(flags_internal::FlagCallback cb) && { + flag_->SetCallback(cb); + return *this; + } + + // Make the registrar "die" gracefully as a bool on a line where registration + // happens. Registrar objects are intended to live only as temporary. + operator bool() const { return true; } // NOLINT + + private: + Flag<T>* flag_; // Flag being registered (not owned). +}; + +// This struct and corresponding overload to MakeDefaultValue are used to +// facilitate usage of {} as default value in ABSL_FLAG macro. +struct EmptyBraces {}; + +template <typename T> +T* MakeFromDefaultValue(T t) { + return new T(std::move(t)); +} + +template <typename T> +T* MakeFromDefaultValue(EmptyBraces) { + return new T; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_FLAG_H_ diff --git a/absl/flags/internal/parse.h b/absl/flags/internal/parse.h new file mode 100644 index 00000000..6c79dc87 --- /dev/null +++ b/absl/flags/internal/parse.h @@ -0,0 +1,50 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_PARSE_H_ +#define ABSL_FLAGS_INTERNAL_PARSE_H_ + +#include <string> +#include <vector> + +#include "absl/flags/declare.h" + +ABSL_DECLARE_FLAG(std::vector<std::string>, flagfile); +ABSL_DECLARE_FLAG(std::vector<std::string>, fromenv); +ABSL_DECLARE_FLAG(std::vector<std::string>, tryfromenv); +ABSL_DECLARE_FLAG(std::vector<std::string>, undefok); + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +enum class ArgvListAction { kRemoveParsedArgs, kKeepParsedArgs }; +enum class UsageFlagsAction { kHandleUsage, kIgnoreUsage }; +enum class OnUndefinedFlag { + kIgnoreUndefined, + kReportUndefined, + kAbortIfUndefined +}; + +std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], + ArgvListAction arg_list_act, + UsageFlagsAction usage_flag_act, + OnUndefinedFlag on_undef_flag); + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PARSE_H_ diff --git a/absl/flags/internal/path_util.h b/absl/flags/internal/path_util.h new file mode 100644 index 00000000..623a7bc9 --- /dev/null +++ b/absl/flags/internal/path_util.h @@ -0,0 +1,62 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ +#define ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ + +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// A portable interface that returns the basename of the filename passed as an +// argument. It is similar to basename(3) +// <https://linux.die.net/man/3/basename>. +// For example: +// flags_internal::Basename("a/b/prog/file.cc") +// returns "file.cc" +// flags_internal::Basename("file.cc") +// returns "file.cc" +inline absl::string_view Basename(absl::string_view filename) { + auto last_slash_pos = filename.find_last_of("/\\"); + + return last_slash_pos == absl::string_view::npos + ? filename + : filename.substr(last_slash_pos + 1); +} + +// A portable interface that returns the directory name of the filename +// passed as an argument, including the trailing slash. +// Returns the empty string if a slash is not found in the input file name. +// For example: +// flags_internal::Package("a/b/prog/file.cc") +// returns "a/b/prog/" +// flags_internal::Package("file.cc") +// returns "" +inline absl::string_view Package(absl::string_view filename) { + auto last_slash_pos = filename.find_last_of("/\\"); + + return last_slash_pos == absl::string_view::npos + ? absl::string_view() + : filename.substr(0, last_slash_pos + 1); +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PATH_UTIL_H_ diff --git a/absl/flags/internal/path_util_test.cc b/absl/flags/internal/path_util_test.cc new file mode 100644 index 00000000..2091373c --- /dev/null +++ b/absl/flags/internal/path_util_test.cc @@ -0,0 +1,46 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/path_util.h" + +#include "gtest/gtest.h" + +namespace { + +namespace flags = absl::flags_internal; + +TEST(FlagsPathUtilTest, TestBasename) { + EXPECT_EQ(flags::Basename(""), ""); + EXPECT_EQ(flags::Basename("a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("dir/a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("dir1/dir2/a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("../dir1/dir2/a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("/dir1/dir2/a.cc"), "a.cc"); + EXPECT_EQ(flags::Basename("/dir1/dir2/../dir3/a.cc"), "a.cc"); +} + +// -------------------------------------------------------------------- + +TEST(FlagsPathUtilTest, TestPackage) { + EXPECT_EQ(flags::Package(""), ""); + EXPECT_EQ(flags::Package("a.cc"), ""); + EXPECT_EQ(flags::Package("dir/a.cc"), "dir/"); + EXPECT_EQ(flags::Package("dir1/dir2/a.cc"), "dir1/dir2/"); + EXPECT_EQ(flags::Package("../dir1/dir2/a.cc"), "../dir1/dir2/"); + EXPECT_EQ(flags::Package("/dir1/dir2/a.cc"), "/dir1/dir2/"); + EXPECT_EQ(flags::Package("/dir1/dir2/../dir3/a.cc"), "/dir1/dir2/../dir3/"); +} + +} // namespace diff --git a/absl/flags/internal/program_name.cc b/absl/flags/internal/program_name.cc new file mode 100644 index 00000000..28b8ed38 --- /dev/null +++ b/absl/flags/internal/program_name.cc @@ -0,0 +1,55 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/program_name.h" + +#include <string> + +#include "absl/flags/internal/path_util.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +ABSL_CONST_INIT static absl::Mutex program_name_guard(absl::kConstInit); +ABSL_CONST_INIT static std::string* program_name + GUARDED_BY(program_name_guard) = nullptr; + +std::string ProgramInvocationName() { + absl::MutexLock l(&program_name_guard); + + return program_name ? *program_name : "UNKNOWN"; +} + +std::string ShortProgramInvocationName() { + absl::MutexLock l(&program_name_guard); + + return program_name ? std::string(flags_internal::Basename(*program_name)) + : "UNKNOWN"; +} + +void SetProgramInvocationName(absl::string_view prog_name_str) { + absl::MutexLock l(&program_name_guard); + + if (!program_name) + program_name = new std::string(prog_name_str); + else + program_name->assign(prog_name_str.data(), prog_name_str.size()); +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/internal/program_name.h b/absl/flags/internal/program_name.h new file mode 100644 index 00000000..a2f0ca10 --- /dev/null +++ b/absl/flags/internal/program_name.h @@ -0,0 +1,49 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_ +#define ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_ + +#include <string> + +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Program name + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// Returns program invocation name or "UNKNOWN" if `SetProgramInvocationName()` +// is never called. At the moment this is always set to argv[0] as part of +// library initialization. +std::string ProgramInvocationName(); + +// Returns base name for program invocation name. For example, if +// ProgramInvocationName() == "a/b/mybinary" +// then +// ShortProgramInvocationName() == "mybinary" +std::string ShortProgramInvocationName(); + +// Sets program invocation name to a new value. Should only be called once +// during program initialization, before any threads are spawned. +void SetProgramInvocationName(absl::string_view prog_name_str); + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PROGRAM_NAME_H_ diff --git a/absl/flags/internal/program_name_test.cc b/absl/flags/internal/program_name_test.cc new file mode 100644 index 00000000..ed69218b --- /dev/null +++ b/absl/flags/internal/program_name_test.cc @@ -0,0 +1,60 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/program_name.h" + +#include "gtest/gtest.h" +#include "absl/strings/match.h" + +namespace { + +namespace flags = absl::flags_internal; + +TEST(FlagsPathUtilTest, TestInitialProgamName) { + flags::SetProgramInvocationName("absl/flags/program_name_test"); + std::string program_name = flags::ProgramInvocationName(); + for (char& c : program_name) + if (c == '\\') c = '/'; + +#if !defined(__wasm__) && !defined(__asmjs__) + const std::string expect_name = "absl/flags/program_name_test"; + const std::string expect_basename = "program_name_test"; +#else + // For targets that generate javascript or webassembly the invocation name + // has the special value below. + const std::string expect_name = "this.program"; + const std::string expect_basename = "this.program"; +#endif + + EXPECT_TRUE(absl::EndsWith(program_name, expect_name)) << program_name; + EXPECT_EQ(flags::ShortProgramInvocationName(), expect_basename); +} + +TEST(FlagsPathUtilTest, TestProgamNameInterfaces) { + flags::SetProgramInvocationName("a/my_test"); + + EXPECT_EQ(flags::ProgramInvocationName(), "a/my_test"); + EXPECT_EQ(flags::ShortProgramInvocationName(), "my_test"); + + absl::string_view not_null_terminated("absl/aaa/bbb"); + not_null_terminated = not_null_terminated.substr(1, 10); + + flags::SetProgramInvocationName(not_null_terminated); + + EXPECT_EQ(flags::ProgramInvocationName(), "bsl/aaa/bb"); + EXPECT_EQ(flags::ShortProgramInvocationName(), "bb"); +} + +} // namespace diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc new file mode 100644 index 00000000..e39264e5 --- /dev/null +++ b/absl/flags/internal/registry.cc @@ -0,0 +1,445 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/registry.h" + +#include "absl/base/dynamic_annotations.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/flags/config.h" +#include "absl/flags/usage_config.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +// -------------------------------------------------------------------- +// FlagRegistry implementation +// A FlagRegistry holds all flag objects indexed +// by their names so that if you know a flag's name you can access or +// set it. + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { +namespace { + +void DestroyFlag(CommandLineFlag* flag) NO_THREAD_SAFETY_ANALYSIS { + flag->Destroy(); + + // CommandLineFlag handle object is heap allocated for non Abseil Flags. + if (!flag->IsAbseilFlag()) { + delete flag; + } +} + +} // namespace + +// -------------------------------------------------------------------- +// FlagRegistry +// A FlagRegistry singleton object holds all flag objects indexed +// by their names so that if you know a flag's name (as a C +// string), you can access or set it. If the function is named +// FooLocked(), you must own the registry lock before calling +// the function; otherwise, you should *not* hold the lock, and +// the function will acquire it itself if needed. +// -------------------------------------------------------------------- + +class FlagRegistry { + public: + FlagRegistry() = default; + ~FlagRegistry() { + for (auto& p : flags_) { + DestroyFlag(p.second); + } + } + + // Store a flag in this registry. Takes ownership of *flag. + void RegisterFlag(CommandLineFlag* flag); + + void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_) { lock_.Lock(); } + void Unlock() UNLOCK_FUNCTION(lock_) { lock_.Unlock(); } + + // Returns the flag object for the specified name, or nullptr if not found. + // Will emit a warning if a 'retired' flag is specified. + CommandLineFlag* FindFlagLocked(absl::string_view name); + + // Returns the retired flag object for the specified name, or nullptr if not + // found or not retired. Does not emit a warning. + CommandLineFlag* FindRetiredFlagLocked(absl::string_view name); + + static FlagRegistry* GlobalRegistry(); // returns a singleton registry + + private: + friend class FlagSaverImpl; // reads all the flags in order to copy them + friend void ForEachFlagUnlocked( + std::function<void(CommandLineFlag*)> visitor); + + // The map from name to flag, for FindFlagLocked(). + using FlagMap = std::map<absl::string_view, CommandLineFlag*>; + using FlagIterator = FlagMap::iterator; + using FlagConstIterator = FlagMap::const_iterator; + FlagMap flags_; + + absl::Mutex lock_; + + // Disallow + FlagRegistry(const FlagRegistry&); + FlagRegistry& operator=(const FlagRegistry&); +}; + +FlagRegistry* FlagRegistry::GlobalRegistry() { + static FlagRegistry* global_registry = new FlagRegistry; + return global_registry; +} + +namespace { + +class FlagRegistryLock { + public: + explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); } + ~FlagRegistryLock() { fr_->Unlock(); } + + private: + FlagRegistry* const fr_; +}; + +} // namespace + +void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { + FlagRegistryLock registry_lock(this); + std::pair<FlagIterator, bool> ins = + flags_.insert(FlagMap::value_type(flag->Name(), flag)); + if (ins.second == false) { // means the name was already in the map + CommandLineFlag* old_flag = ins.first->second; + if (flag->IsRetired() != old_flag->IsRetired()) { + // All registrations must agree on the 'retired' flag. + flags_internal::ReportUsageError( + absl::StrCat( + "Retired flag '", flag->Name(), + "' was defined normally in file '", + (flag->IsRetired() ? old_flag->Filename() : flag->Filename()), + "'."), + true); + } else if (flag->op != old_flag->op) { + flags_internal::ReportUsageError( + absl::StrCat("Flag '", flag->Name(), + "' was defined more than once but with " + "differing types. Defined in files '", + old_flag->Filename(), "' and '", flag->Filename(), + "' with types '", old_flag->Typename(), "' and '", + flag->Typename(), "', respectively."), + true); + } else if (old_flag->IsRetired()) { + // Retired definitions are idempotent. Just keep the old one. + DestroyFlag(flag); + return; + } else if (old_flag->Filename() != flag->Filename()) { + flags_internal::ReportUsageError( + absl::StrCat("Flag '", flag->Name(), + "' was defined more than once (in files '", + old_flag->Filename(), "' and '", flag->Filename(), + "')."), + true); + } else { + flags_internal::ReportUsageError( + absl::StrCat( + "Something wrong with flag '", flag->Name(), "' in file '", + flag->Filename(), "'. One possibility: file '", flag->Filename(), + "' is being linked both statically and dynamically into this " + "executable. e.g. some files listed as srcs to a test and also " + "listed as srcs of some shared lib deps of the same test."), + true); + } + // All cases above are fatal, except for the retired flags. + std::exit(1); + } +} + +CommandLineFlag* FlagRegistry::FindFlagLocked(absl::string_view name) { + FlagConstIterator i = flags_.find(name); + if (i == flags_.end()) { + return nullptr; + } + + if (i->second->IsRetired()) { + flags_internal::ReportUsageError( + absl::StrCat("Accessing retired flag '", name, "'"), false); + } + + return i->second; +} + +CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) { + FlagConstIterator i = flags_.find(name); + if (i == flags_.end() || !i->second->IsRetired()) { + return nullptr; + } + + return i->second; +} + +// -------------------------------------------------------------------- +// FlagSaver +// FlagSaverImpl +// This class stores the states of all flags at construct time, +// and restores all flags to that state at destruct time. +// Its major implementation challenge is that it never modifies +// pointers in the 'main' registry, so global FLAG_* vars always +// point to the right place. +// -------------------------------------------------------------------- + +class FlagSaverImpl { + public: + // Constructs an empty FlagSaverImpl object. + FlagSaverImpl() {} + ~FlagSaverImpl() { + // reclaim memory from each of our CommandLineFlags + for (const SavedFlag& src : backup_registry_) { + Delete(src.op, src.current); + Delete(src.op, src.default_value); + } + } + + // Saves the flag states from the flag registry into this object. + // It's an error to call this more than once. + // Must be called when the registry mutex is not held. + void SaveFromRegistry() { + assert(backup_registry_.empty()); // call only once! + SavedFlag saved; + flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { + if (flag->IsRetired()) return; + + saved.name = flag->Name(); + saved.op = flag->op; + saved.marshalling_op = flag->marshalling_op; + { + absl::MutexLock l(flag->InitFlagIfNecessary()); + saved.validator = flag->validator; + saved.modified = flag->modified; + saved.on_command_line = flag->on_command_line; + saved.current = Clone(saved.op, flag->cur); + saved.default_value = Clone(saved.op, flag->def); + saved.counter = flag->counter; + } + backup_registry_.push_back(saved); + }); + } + + // Restores the saved flag states into the flag registry. We + // assume no flags were added or deleted from the registry since + // the SaveFromRegistry; if they were, that's trouble! Must be + // called when the registry mutex is not held. + void RestoreToRegistry() { + FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry(); + FlagRegistryLock frl(global_registry); + for (const SavedFlag& src : backup_registry_) { + CommandLineFlag* flag = global_registry->FindFlagLocked(src.name); + // If null, flag got deleted from registry. + if (!flag) continue; + + bool restored = false; + { + absl::MutexLock l(flag->InitFlagIfNecessary()); + flag->validator = src.validator; + flag->modified = src.modified; + flag->on_command_line = src.on_command_line; + if (flag->counter != src.counter || + ChangedDirectly(flag, src.default_value, flag->def)) { + restored = true; + Copy(src.op, src.default_value, flag->def); + } + if (flag->counter != src.counter || + ChangedDirectly(flag, src.current, flag->cur)) { + restored = true; + Copy(src.op, src.current, flag->cur); + UpdateCopy(flag); + flag->InvokeCallback(); + } + } + + if (restored) { + flag->counter++; + + // Revalidate the flag because the validator might store state based + // on the flag's value, which just changed due to the restore. + // Failing validation is ignored because it's assumed that the flag + // was valid previously and there's little that can be done about it + // here, anyway. + flag->ValidateInputValue(flag->CurrentValue()); + + ABSL_INTERNAL_LOG( + INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ", + Unparse(src.marshalling_op, src.current))); + } + } + } + + private: + struct SavedFlag { + absl::string_view name; + FlagOpFn op; + FlagMarshallingOpFn marshalling_op; + int64_t counter; + bool modified; + bool on_command_line; + bool (*validator)(); + const void* current; // nullptr after restore + const void* default_value; // nullptr after restore + }; + + std::vector<SavedFlag> backup_registry_; + + FlagSaverImpl(const FlagSaverImpl&); // no copying! + void operator=(const FlagSaverImpl&); +}; + +FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) { + impl_->SaveFromRegistry(); +} + +void FlagSaver::Ignore() { + delete impl_; + impl_ = nullptr; +} + +FlagSaver::~FlagSaver() { + if (!impl_) return; + + impl_->RestoreToRegistry(); + delete impl_; +} + +// -------------------------------------------------------------------- +// GetAllFlags() +// The main way the FlagRegistry class exposes its data. This +// returns, as strings, all the info about all the flags in +// the main registry, sorted first by filename they are defined +// in, and then by flagname. +// -------------------------------------------------------------------- + +struct FilenameFlagnameLess { + bool operator()(const CommandLineFlagInfo& a, + const CommandLineFlagInfo& b) const { + int cmp = absl::string_view(a.filename).compare(b.filename); + if (cmp != 0) return cmp < 0; + return a.name < b.name; + } +}; + +void FillCommandLineFlagInfo(CommandLineFlag* flag, + CommandLineFlagInfo* result) { + result->name = std::string(flag->Name()); + result->type = std::string(flag->Typename()); + result->description = flag->Help(); + result->filename = flag->Filename(); + + if (!flag->IsAbseilFlag()) { + if (!flag->IsModified() && ChangedDirectly(flag, flag->cur, flag->def)) { + flag->modified = true; + } + } + + result->current_value = flag->CurrentValue(); + result->default_value = flag->DefaultValue(); + result->is_default = !flag->IsModified(); + result->has_validator_fn = flag->HasValidatorFn(); + absl::MutexLock l(flag->InitFlagIfNecessary()); + result->flag_ptr = flag->IsAbseilFlag() ? nullptr : flag->cur; +} + +// -------------------------------------------------------------------- + +CommandLineFlag* FindCommandLineFlag(absl::string_view name) { + if (name.empty()) return nullptr; + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + FlagRegistryLock frl(registry); + + return registry->FindFlagLocked(name); +} + +CommandLineFlag* FindRetiredFlag(absl::string_view name) { + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + FlagRegistryLock frl(registry); + + return registry->FindRetiredFlagLocked(name); +} + +// -------------------------------------------------------------------- + +void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor) { + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + for (FlagRegistry::FlagConstIterator i = registry->flags_.begin(); + i != registry->flags_.end(); ++i) { + visitor(i->second); + } +} + +void ForEachFlag(std::function<void(CommandLineFlag*)> visitor) { + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + FlagRegistryLock frl(registry); + ForEachFlagUnlocked(visitor); +} + +// -------------------------------------------------------------------- + +void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT) { + flags_internal::ForEachFlag([&](CommandLineFlag* flag) { + if (flag->IsRetired()) return; + + CommandLineFlagInfo fi; + FillCommandLineFlagInfo(flag, &fi); + OUTPUT->push_back(fi); + }); + + // Now sort the flags, first by filename they occur in, then alphabetically + std::sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameLess()); +} + +// -------------------------------------------------------------------- + +bool RegisterCommandLineFlag(CommandLineFlag* flag) { + FlagRegistry::GlobalRegistry()->RegisterFlag(flag); + return true; +} + +// -------------------------------------------------------------------- + +bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops, + const char* name) { + auto* flag = new CommandLineFlag( + name, + /*help_text=*/absl::flags_internal::HelpText::FromStaticCString(nullptr), + /*filename_arg=*/"RETIRED", ops, marshalling_ops, + /*initial_value_gen=*/nullptr, + /*retired_arg=*/true, nullptr, nullptr); + FlagRegistry::GlobalRegistry()->RegisterFlag(flag); + return true; +} + +// -------------------------------------------------------------------- + +bool IsRetiredFlag(absl::string_view name, bool* type_is_bool) { + assert(!name.empty()); + CommandLineFlag* flag = flags_internal::FindRetiredFlag(name); + if (flag == nullptr) { + return false; + } + assert(type_is_bool); + *type_is_bool = flag->IsOfType<bool>(); + return true; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h new file mode 100644 index 00000000..d851d245 --- /dev/null +++ b/absl/flags/internal/registry.h @@ -0,0 +1,169 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_REGISTRY_H_ +#define ABSL_FLAGS_INTERNAL_REGISTRY_H_ + +#include <functional> +#include <map> +#include <string> + +#include "absl/base/macros.h" +#include "absl/flags/internal/commandlineflag.h" + +// -------------------------------------------------------------------- +// Global flags registry API. + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// CommandLineFlagInfo holds all information for a flag. +struct CommandLineFlagInfo { + std::string name; // the name of the flag + std::string type; // DO NOT use. Use flag->IsOfType<T>() instead. + std::string description; // the "help text" associated with the flag + std::string current_value; // the current value, as a std::string + std::string default_value; // the default value, as a std::string + std::string filename; // 'cleaned' version of filename holding the flag + bool has_validator_fn; // true if RegisterFlagValidator called on this flag + + bool is_default; // true if the flag has the default value and + // has not been set explicitly from the cmdline + // or via SetCommandLineOption. + + // nullptr for ABSL_FLAG. A pointer to the flag's current value + // otherwise. E.g., for DEFINE_int32(foo, ...), flag_ptr will be + // &FLAGS_foo. + const void* flag_ptr; +}; + +//----------------------------------------------------------------------------- + +void FillCommandLineFlagInfo(CommandLineFlag* flag, + CommandLineFlagInfo* result); + +//----------------------------------------------------------------------------- + +CommandLineFlag* FindCommandLineFlag(absl::string_view name); +CommandLineFlag* FindRetiredFlag(absl::string_view name); + +// Executes specified visitor for each non-retired flag in the registry. +// Requires the caller hold the registry lock. +void ForEachFlagUnlocked(std::function<void(CommandLineFlag*)> visitor); +// Executes specified visitor for each non-retired flag in the registry. While +// callback are executed, the registry is locked and can't be changed. +void ForEachFlag(std::function<void(CommandLineFlag*)> visitor); + +//----------------------------------------------------------------------------- + +// Store the list of all flags in *OUTPUT, sorted by file. +void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT); + +//----------------------------------------------------------------------------- + +bool RegisterCommandLineFlag(CommandLineFlag*); + +//----------------------------------------------------------------------------- +// Retired registrations: +// +// Retired flag registrations are treated specially. A 'retired' flag is +// provided only for compatibility with automated invocations that still +// name it. A 'retired' flag: +// - is not bound to a C++ FLAGS_ reference. +// - has a type and a value, but that value is intentionally inaccessible. +// - does not appear in --help messages. +// - is fully supported by _all_ flag parsing routines. +// - consumes args normally, and complains about type mismatches in its +// argument. +// - emits a complaint but does not die (e.g. LOG(ERROR)) if it is +// accessed by name through the flags API for parsing or otherwise. +// +// The registrations for a flag happen in an unspecified order as the +// initializers for the namespace-scope objects of a program are run. +// Any number of weak registrations for a flag can weakly define the flag. +// One non-weak registration will upgrade the flag from weak to non-weak. +// Further weak registrations of a non-weak flag are ignored. +// +// This mechanism is designed to support moving dead flags into a +// 'graveyard' library. An example migration: +// +// 0: Remove references to this FLAGS_flagname in the C++ codebase. +// 1: Register as 'retired' in old_lib. +// 2: Make old_lib depend on graveyard. +// 3: Add a redundant 'retired' registration to graveyard. +// 4: Remove the old_lib 'retired' registration. +// 5: Eventually delete the graveyard registration entirely. +// +// Returns bool to enable use in namespace-scope initializers. +// For example: +// +// static const bool dummy = base::RetiredFlag<int32_t>("myflag"); +// +// Or to declare several at once: +// +// static bool dummies[] = { +// base::RetiredFlag<std::string>("some_string_flag"), +// base::RetiredFlag<double>("some_double_flag"), +// base::RetiredFlag<int32_t>("some_int32_flag") +// }; + +// Retire flag with name "name" and type indicated by ops. +bool Retire(FlagOpFn ops, FlagMarshallingOpFn marshalling_ops, + const char* name); + +// Registered a retired flag with name 'flag_name' and type 'T'. +template <typename T> +inline bool RetiredFlag(const char* flag_name) { + return flags_internal::Retire(flags_internal::FlagOps<T>, + flags_internal::FlagMarshallingOps<T>, + flag_name); +} + +// If the flag is retired, returns true and indicates in |*type_is_bool| +// whether the type of the retired flag is a bool. +// Only to be called by code that needs to explicitly ignore retired flags. +bool IsRetiredFlag(absl::string_view name, bool* type_is_bool); + +//----------------------------------------------------------------------------- +// Saves the states (value, default value, whether the user has set +// the flag, registered validators, etc) of all flags, and restores +// them when the FlagSaver is destroyed. +// +// This class is thread-safe. However, its destructor writes to +// exactly the set of flags that have changed value during its +// lifetime, so concurrent _direct_ access to those flags +// (i.e. FLAGS_foo instead of {Get,Set}CommandLineOption()) is unsafe. + +class FlagSaver { + public: + FlagSaver(); + ~FlagSaver(); + + FlagSaver(const FlagSaver&) = delete; + void operator=(const FlagSaver&) = delete; + + // Prevents saver from restoring the saved state of flags. + void Ignore(); + + private: + class FlagSaverImpl* impl_; // we use pimpl here to keep API steady +}; + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_REGISTRY_H_ diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc new file mode 100644 index 00000000..6b91b261 --- /dev/null +++ b/absl/flags/internal/type_erased.cc @@ -0,0 +1,108 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/type_erased.h" + +#include "absl/base/internal/raw_logging.h" +#include "absl/flags/config.h" +#include "absl/flags/usage_config.h" +#include "absl/strings/str_cat.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +bool GetCommandLineOption(absl::string_view name, std::string* value) { + if (name.empty()) return false; + assert(value); + + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + if (flag == nullptr || flag->IsRetired()) { + return false; + } + + *value = flag->CurrentValue(); + return true; +} + +bool GetCommandLineFlagInfo(absl::string_view name, + CommandLineFlagInfo* OUTPUT) { + if (name.empty()) return false; + + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + if (flag == nullptr || flag->IsRetired()) { + return false; + } + + assert(OUTPUT); + FillCommandLineFlagInfo(flag, OUTPUT); + return true; +} + +CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name) { + CommandLineFlagInfo info; + if (!GetCommandLineFlagInfo(name, &info)) { + ABSL_INTERNAL_LOG(FATAL, absl::StrCat("Flag '", name, "' does not exist")); + } + return info; +} + +// -------------------------------------------------------------------- + +bool SetCommandLineOption(absl::string_view name, absl::string_view value) { + return SetCommandLineOptionWithMode(name, value, + flags_internal::SET_FLAGS_VALUE); +} + +bool SetCommandLineOptionWithMode(absl::string_view name, + absl::string_view value, + FlagSettingMode set_mode) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + + if (!flag || flag->IsRetired()) return false; + + std::string error; + if (!flag->SetFromString(value, set_mode, kProgrammaticChange, &error)) { + // Errors here are all of the form: the provided name was a recognized + // flag, but the value was invalid (bad type, or validation failed). + flags_internal::ReportUsageError(error, false); + return false; + } + + return true; +} + +// -------------------------------------------------------------------- + +bool IsValidFlagValue(absl::string_view name, absl::string_view value) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + + return flag != nullptr && + (flag->IsRetired() || flag->ValidateInputValue(value)); +} + +// -------------------------------------------------------------------- + +bool SpecifiedOnCommandLine(absl::string_view name) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + if (flag != nullptr && !flag->IsRetired()) { + return flag->IsSpecifiedOnCommandLine(); + } + return false; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/internal/type_erased.h b/absl/flags/internal/type_erased.h new file mode 100644 index 00000000..c9de6de9 --- /dev/null +++ b/absl/flags/internal/type_erased.h @@ -0,0 +1,99 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ +#define ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ + +#include <string> + +#include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/registry.h" + +// -------------------------------------------------------------------- +// Registry interfaces operating on type erased handles. + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// If a flag named "name" exists, store its current value in *OUTPUT +// and return true. Else return false without changing *OUTPUT. +// Thread-safe. +bool GetCommandLineOption(absl::string_view name, std::string* value); + +// If a flag named "name" exists, store its information in *OUTPUT +// and return true. Else return false without changing *OUTPUT. +// Thread-safe. +bool GetCommandLineFlagInfo(absl::string_view name, + CommandLineFlagInfo* OUTPUT); + +// Returns the CommandLineFlagInfo of the flagname. exit() with an +// error code if name not found. +// Thread-safe. +CommandLineFlagInfo GetCommandLineFlagInfoOrDie(absl::string_view name); + +// Set the value of the flag named "name" to value. If successful, +// returns true. If not successful (e.g., the flag was not found or +// the value is not a valid value), returns false. +// Thread-safe. +bool SetCommandLineOption(absl::string_view name, absl::string_view value); + +bool SetCommandLineOptionWithMode(absl::string_view name, + absl::string_view value, + FlagSettingMode set_mode); + +//----------------------------------------------------------------------------- + +// Returns true iff all of the following conditions are true: +// (a) "name" names a registered flag +// (b) "value" can be parsed succesfully according to the type of the flag +// (c) parsed value passes any validator associated with the flag +bool IsValidFlagValue(absl::string_view name, absl::string_view value); + +//----------------------------------------------------------------------------- + +// Returns true iff a flag named "name" was specified on the command line +// (either directly, or via one of --flagfile or --fromenv or --tryfromenv). +// +// Any non-command-line modification of the flag does not affect the +// result of this function. So for example, if a flag was passed on +// the command line but then reset via SET_FLAGS_DEFAULT, this +// function will still return true. +bool SpecifiedOnCommandLine(absl::string_view name); + +//----------------------------------------------------------------------------- + +// If a flag with specified "name" exists and has type T, store +// its current value in *dst and return true. Else return false +// without touching *dst. T must obey all of the requirements for +// types passed to DEFINE_FLAG. +template <typename T> +inline bool GetByName(absl::string_view name, T* dst) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); + if (!flag) return false; + + if (auto val = flag->Get<T>()) { + *dst = *val; + return true; + } + + return false; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_TYPE_ERASED_H_ diff --git a/absl/flags/internal/type_erased_test.cc b/absl/flags/internal/type_erased_test.cc new file mode 100644 index 00000000..ac749a60 --- /dev/null +++ b/absl/flags/internal/type_erased_test.cc @@ -0,0 +1,147 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/type_erased.h" + +#include <cmath> + +#include "gtest/gtest.h" +#include "absl/flags/flag.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" + +ABSL_FLAG(int, int_flag, 1, "int_flag help"); +ABSL_FLAG(std::string, string_flag, "dflt", "string_flag help"); +ABSL_RETIRED_FLAG(bool, bool_retired_flag, false, "bool_retired_flag help"); + +namespace { + +namespace flags = absl::flags_internal; + +class TypeErasedTest : public testing::Test { + protected: + void SetUp() override { flag_saver_ = absl::make_unique<flags::FlagSaver>(); } + void TearDown() override { flag_saver_.reset(); } + + private: + std::unique_ptr<flags::FlagSaver> flag_saver_; +}; + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestGetCommandLineOption) { + std::string value; + EXPECT_TRUE(flags::GetCommandLineOption("int_flag", &value)); + EXPECT_EQ(value, "1"); + + EXPECT_TRUE(flags::GetCommandLineOption("string_flag", &value)); + EXPECT_EQ(value, "dflt"); + + EXPECT_FALSE(flags::GetCommandLineOption("bool_retired_flag", &value)); + + EXPECT_FALSE(flags::GetCommandLineOption("unknown_flag", &value)); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestSetCommandLineOption) { + EXPECT_TRUE(flags::SetCommandLineOption("int_flag", "101")); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); + + EXPECT_TRUE(flags::SetCommandLineOption("string_flag", "asdfgh")); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); + + EXPECT_FALSE(flags::SetCommandLineOption("bool_retired_flag", "true")); + + EXPECT_FALSE(flags::SetCommandLineOption("unknown_flag", "true")); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_VALUE) { + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", + flags::SET_FLAGS_VALUE)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); + + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", + flags::SET_FLAGS_VALUE)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true", + flags::SET_FLAGS_VALUE)); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true", + flags::SET_FLAGS_VALUE)); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAG_IF_DEFAULT) { + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", + flags::SET_FLAG_IF_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); + + // This semantic is broken. We return true instead of false. Value is not + // updated. + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202", + flags::SET_FLAG_IF_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 101); + + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", + flags::SET_FLAG_IF_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true", + flags::SET_FLAG_IF_DEFAULT)); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true", + flags::SET_FLAG_IF_DEFAULT)); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestSetCommandLineOptionWithMode_SET_FLAGS_DEFAULT) { + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "101", + flags::SET_FLAGS_DEFAULT)); + + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("string_flag", "asdfgh", + flags::SET_FLAGS_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "asdfgh"); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("bool_retired_flag", "true", + flags::SET_FLAGS_DEFAULT)); + + EXPECT_FALSE(flags::SetCommandLineOptionWithMode("unknown_flag", "true", + flags::SET_FLAGS_DEFAULT)); + + // This should be successfull, since flag is still is not set + EXPECT_TRUE(flags::SetCommandLineOptionWithMode("int_flag", "202", + flags::SET_FLAG_IF_DEFAULT)); + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 202); +} + +// -------------------------------------------------------------------- + +TEST_F(TypeErasedTest, TestIsValidFlagValue) { + EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "57")); + EXPECT_TRUE(flags::IsValidFlagValue("int_flag", "-101")); + EXPECT_FALSE(flags::IsValidFlagValue("int_flag", "1.1")); + + EXPECT_TRUE(flags::IsValidFlagValue("string_flag", "#%^#%^$%DGHDG$W%adsf")); + + EXPECT_TRUE(flags::IsValidFlagValue("bool_retired_flag", "true")); +} + +} // namespace diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc new file mode 100644 index 00000000..a9a8a21e --- /dev/null +++ b/absl/flags/internal/usage.cc @@ -0,0 +1,385 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/usage.h" + +#include <map> +#include <string> + +#include "absl/flags/flag.h" +#include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/program_name.h" +#include "absl/flags/usage_config.h" +#include "absl/strings/ascii.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" + +ABSL_FLAG(bool, help, false, + "show help on important flags for this binary [tip: all flags can " + "have two dashes]"); +ABSL_FLAG(bool, helpfull, false, "show help on all flags"); +ABSL_FLAG(bool, helpshort, false, + "show help on only the main module for this program"); +ABSL_FLAG(bool, helppackage, false, + "show help on all modules in the main package"); +ABSL_FLAG(bool, version, false, "show version and build info and exit"); +ABSL_FLAG(bool, only_check_args, false, "exit after checking all flags"); +ABSL_FLAG(std::string, helpon, "", + "show help on the modules named by this flag value"); +ABSL_FLAG(std::string, helpmatch, "", + "show help on modules whose name contains the specified substr"); + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { +namespace { + +// This class is used to emit an XML element with `tag` and `text`. +// It adds opening and closing tags and escapes special characters in the text. +// For example: +// std::cout << XMLElement("title", "Milk & Cookies"); +// prints "<title>Milk & Cookies</title>" +class XMLElement { + public: + XMLElement(absl::string_view tag, absl::string_view txt) + : tag_(tag), txt_(txt) {} + + friend std::ostream& operator<<(std::ostream& out, + const XMLElement& xml_elem) { + out << "<" << xml_elem.tag_ << ">"; + + for (auto c : xml_elem.txt_) { + switch (c) { + case '"': + out << """; + break; + case '\'': + out << "'"; + break; + case '&': + out << "&"; + break; + case '<': + out << "<"; + break; + case '>': + out << ">"; + break; + default: + out << c; + break; + } + } + + return out << "</" << xml_elem.tag_ << ">"; + } + + private: + absl::string_view tag_; + absl::string_view txt_; +}; + +// -------------------------------------------------------------------- +// Helper class to pretty-print info about a flag. + +class FlagHelpPrettyPrinter { + public: + // Pretty printer holds on to the std::ostream& reference to direct an output + // to that stream. + FlagHelpPrettyPrinter(int max_line_len, std::ostream* out) + : out_(*out), + max_line_len_(max_line_len), + line_len_(0), + first_line_(true) {} + + void Write(absl::string_view str, bool wrap_line = false) { + // Empty std::string - do nothing. + if (str.empty()) return; + + std::vector<absl::string_view> tokens; + if (wrap_line) { + for (auto line : absl::StrSplit(str, absl::ByAnyChar("\n\r"))) { + if (!tokens.empty()) { + // Keep line separators in the input std::string. + tokens.push_back("\n"); + } + for (auto token : + absl::StrSplit(line, absl::ByAnyChar(" \t"), absl::SkipEmpty())) { + tokens.push_back(token); + } + } + } else { + tokens.push_back(str); + } + + for (auto token : tokens) { + bool new_line = (line_len_ == 0); + + // Respect line separators in the input std::string. + if (token == "\n") { + EndLine(); + continue; + } + + // Write the token, ending the std::string first if necessary/possible. + if (!new_line && (line_len_ + token.size() >= max_line_len_)) { + EndLine(); + new_line = true; + } + + if (new_line) { + StartLine(); + } else { + out_ << ' '; + ++line_len_; + } + + out_ << token; + line_len_ += token.size(); + } + } + + void StartLine() { + if (first_line_) { + out_ << " "; + line_len_ = 4; + first_line_ = false; + } else { + out_ << " "; + line_len_ = 6; + } + } + void EndLine() { + out_ << '\n'; + line_len_ = 0; + } + + private: + std::ostream& out_; + const int max_line_len_; + int line_len_; + bool first_line_; +}; + +void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, + std::ostream* out) { + FlagHelpPrettyPrinter printer(80, out); // Max line length is 80. + + // Flag name. + printer.Write(absl::StrCat("-", flag.Name())); + + // Flag help. + printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true); + + // Flag data type (for V1 flags only). + if (!flag.IsAbseilFlag() && !flag.IsRetired()) { + printer.Write(absl::StrCat("type: ", flag.Typename(), ";")); + } + + // The listed default value will be the actual default from the flag + // definition in the originating source file, unless the value has + // subsequently been modified using SetCommandLineOption() with mode + // SET_FLAGS_DEFAULT. + std::string dflt_val = flag.DefaultValue(); + if (flag.IsOfType<std::string>()) { + dflt_val = absl::StrCat("\"", dflt_val, "\""); + } + printer.Write(absl::StrCat("default: ", dflt_val, ";")); + + if (flag.IsModified()) { + std::string curr_val = flag.CurrentValue(); + if (flag.IsOfType<std::string>()) { + curr_val = absl::StrCat("\"", curr_val, "\""); + } + printer.Write(absl::StrCat("currently: ", curr_val, ";")); + } + + printer.EndLine(); +} + +// Shows help for every filename which matches any of the filters +// If filters are empty, shows help for every file. +// If a flag's help message has been stripped (e.g. by adding '#define +// STRIP_FLAG_HELP 1' then this flag will not be displayed by '--help' +// and its variants. +void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, + HelpFormat format, absl::string_view program_usage_message) { + if (format == HelpFormat::kHumanReadable) { + out << flags_internal::ShortProgramInvocationName() << ": " + << program_usage_message << "\n\n"; + } else { + // XML schema is not a part of our public API for now. + out << "<?xml version=\"1.0\"?>\n" + // The document. + << "<AllFlags>\n" + // The program name and usage. + << XMLElement("program", flags_internal::ShortProgramInvocationName()) + << '\n' + << XMLElement("usage", program_usage_message) << '\n'; + } + + // Map of package name to + // map of file name to + // vector of flags in the file. + // This map is used to output matching flags grouped by package and file + // name. + std::map<std::string, + std::map<std::string, + std::vector<const flags_internal::CommandLineFlag*>>> + matching_flags; + + flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { + std::string flag_filename = flag->Filename(); + + // Ignore retired flags. + if (flag->IsRetired()) return; + + // If the flag has been stripped, pretend that it doesn't exist. + if (flag->Help() == flags_internal::kStrippedFlagHelp) return; + + // Make sure flag satisfies the filter + if (!filter_cb || !filter_cb(flag_filename)) return; + + matching_flags[std::string(flags_internal::Package(flag_filename))] + [flag_filename] + .push_back(flag); + }); + + absl::string_view + package_separator; // controls blank lines between packages. + absl::string_view file_separator; // controls blank lines between files. + for (const auto& package : matching_flags) { + if (format == HelpFormat::kHumanReadable) { + out << package_separator; + package_separator = "\n\n"; + } + + file_separator = ""; + for (const auto& flags_in_file : package.second) { + if (format == HelpFormat::kHumanReadable) { + out << file_separator << " Flags from " << flags_in_file.first + << ":\n"; + file_separator = "\n"; + } + + for (const auto* flag : flags_in_file.second) { + flags_internal::FlagHelp(out, *flag, format); + } + } + } + + if (format == HelpFormat::kHumanReadable) { + if (filter_cb && matching_flags.empty()) { + out << " No modules matched: use -helpfull\n"; + } + } else { + // The end of the document. + out << "</AllFlags>\n"; + } +} + +} // namespace + +// -------------------------------------------------------------------- +// Produces the help message describing specific flag. +void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, + HelpFormat format) { + if (format == HelpFormat::kHumanReadable) + flags_internal::FlagHelpHumanReadable(flag, &out); +} + +// -------------------------------------------------------------------- +// Produces the help messages for all flags matching the filter. +// If filter is empty produces help messages for all flags. +void FlagsHelp(std::ostream& out, absl::string_view filter, HelpFormat format, + absl::string_view program_usage_message) { + flags_internal::FlagKindFilter filter_cb = [&](absl::string_view filename) { + return filter.empty() || filename.find(filter) != absl::string_view::npos; + }; + flags_internal::FlagsHelpImpl(out, filter_cb, format, program_usage_message); +} + +// -------------------------------------------------------------------- +// Checks all the 'usage' command line flags to see if any have been set. +// If so, handles them appropriately. +int HandleUsageFlags(std::ostream& out, + absl::string_view program_usage_message) { + if (absl::GetFlag(FLAGS_helpshort)) { + flags_internal::FlagsHelpImpl( + out, flags_internal::GetUsageConfig().contains_helpshort_flags, + HelpFormat::kHumanReadable, program_usage_message); + return 1; + } + + if (absl::GetFlag(FLAGS_helpfull)) { + // show all options + flags_internal::FlagsHelp(out, "", HelpFormat::kHumanReadable, + program_usage_message); + return 1; + } + + if (!absl::GetFlag(FLAGS_helpon).empty()) { + flags_internal::FlagsHelp( + out, absl::StrCat("/", absl::GetFlag(FLAGS_helpon), "."), + HelpFormat::kHumanReadable, program_usage_message); + return 1; + } + + if (!absl::GetFlag(FLAGS_helpmatch).empty()) { + flags_internal::FlagsHelp(out, absl::GetFlag(FLAGS_helpmatch), + HelpFormat::kHumanReadable, + program_usage_message); + return 1; + } + + if (absl::GetFlag(FLAGS_help)) { + flags_internal::FlagsHelpImpl( + out, flags_internal::GetUsageConfig().contains_help_flags, + HelpFormat::kHumanReadable, program_usage_message); + + out << "\nTry --helpfull to get a list of all flags.\n"; + + return 1; + } + + if (absl::GetFlag(FLAGS_helppackage)) { + flags_internal::FlagsHelpImpl( + out, flags_internal::GetUsageConfig().contains_helppackage_flags, + HelpFormat::kHumanReadable, program_usage_message); + + out << "\nTry --helpfull to get a list of all flags.\n"; + + return 1; + } + + if (absl::GetFlag(FLAGS_version)) { + if (flags_internal::GetUsageConfig().version_string) + out << flags_internal::GetUsageConfig().version_string(); + // Unlike help, we may be asking for version in a script, so return 0 + return 0; + } + + if (absl::GetFlag(FLAGS_only_check_args)) { + return 0; + } + + return -1; +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/internal/usage.h b/absl/flags/internal/usage.h new file mode 100644 index 00000000..f3794afe --- /dev/null +++ b/absl/flags/internal/usage.h @@ -0,0 +1,80 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_USAGE_H_ +#define ABSL_FLAGS_INTERNAL_USAGE_H_ + +#include <iosfwd> +#include <string> + +#include "absl/flags/declare.h" +#include "absl/flags/internal/commandlineflag.h" +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Usage reporting interfaces + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// The format to report the help messages in. +enum class HelpFormat { + kHumanReadable, +}; + +// Outputs the help message describing specific flag. +void FlagHelp(std::ostream& out, const flags_internal::CommandLineFlag& flag, + HelpFormat format = HelpFormat::kHumanReadable); + +// Produces the help messages for all flags matching the filter. A flag matches +// the filter if it is defined in a file with a filename which includes +// filter string as a substring. You can use '/' and '.' to restrict the +// matching to a specific file names. For example: +// FlagsHelp(out, "/path/to/file."); +// restricts help to only flags which resides in files named like: +// .../path/to/file.<ext> +// for any extension 'ext'. If the filter is empty this function produces help +// messages for all flags. +void FlagsHelp(std::ostream& out, absl::string_view filter, + HelpFormat format, absl::string_view program_usage_message); + +// -------------------------------------------------------------------- + +// If any of the 'usage' related command line flags (listed on the bottom of +// this file) has been set this routine produces corresponding help message in +// the specified output stream and returns: +// 0 - if "version" or "only_check_flags" flags were set and handled. +// 1 - if some other 'usage' related flag was set and handled. +// -1 - if no usage flags were set on a commmand line. +// Non negative return values are expected to be used as an exit code for a +// binary. +int HandleUsageFlags(std::ostream& out, + absl::string_view program_usage_message); + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +ABSL_DECLARE_FLAG(bool, help); +ABSL_DECLARE_FLAG(bool, helpfull); +ABSL_DECLARE_FLAG(bool, helpshort); +ABSL_DECLARE_FLAG(bool, helppackage); +ABSL_DECLARE_FLAG(bool, version); +ABSL_DECLARE_FLAG(bool, only_check_args); +ABSL_DECLARE_FLAG(std::string, helpon); +ABSL_DECLARE_FLAG(std::string, helpmatch); + +#endif // ABSL_FLAGS_INTERNAL_USAGE_H_ diff --git a/absl/flags/internal/usage_test.cc b/absl/flags/internal/usage_test.cc new file mode 100644 index 00000000..781e1d2b --- /dev/null +++ b/absl/flags/internal/usage_test.cc @@ -0,0 +1,404 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/usage.h" + +#include <sstream> + +#include "gtest/gtest.h" +#include "absl/flags/flag.h" +#include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/program_name.h" +#include "absl/flags/parse.h" +#include "absl/flags/usage.h" +#include "absl/flags/usage_config.h" +#include "absl/memory/memory.h" +#include "absl/strings/match.h" + +ABSL_FLAG(int, usage_reporting_test_flag_01, 101, + "usage_reporting_test_flag_01 help message"); +ABSL_FLAG(bool, usage_reporting_test_flag_02, false, + "usage_reporting_test_flag_02 help message"); +ABSL_FLAG(double, usage_reporting_test_flag_03, 1.03, + "usage_reporting_test_flag_03 help message"); +ABSL_FLAG(int64_t, usage_reporting_test_flag_04, 1000000000000004L, + "usage_reporting_test_flag_04 help message"); + +static const char kTestUsageMessage[] = "Custom usage message"; + +struct UDT { + UDT() = default; + UDT(const UDT&) = default; +}; +bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } +std::string AbslUnparseFlag(const UDT&) { return "UDT{}"; } + +ABSL_FLAG(UDT, usage_reporting_test_flag_05, {}, + "usage_reporting_test_flag_05 help message"); + +ABSL_FLAG( + std::string, usage_reporting_test_flag_06, {}, + "usage_reporting_test_flag_06 help message.\n" + "\n" + "Some more help.\n" + "Even more long long long long long long long long long long long long " + "help message."); + +namespace { + +namespace flags = absl::flags_internal; + +static std::string NormalizeFileName(absl::string_view fname) { +#ifdef _WIN32 + std::string normalized(fname); + std::replace(normalized.begin(), normalized.end(), '\\', '/'); + fname = normalized; +#endif + + auto absl_pos = fname.find("/absl/"); + if (absl_pos != absl::string_view::npos) { + fname = fname.substr(absl_pos + 1); + } + return std::string(fname); +} + +class UsageReportingTest : public testing::Test { + protected: + UsageReportingTest() { + // Install default config for the use on this unit test. + // Binary may install a custom config before tests are run. + absl::FlagsUsageConfig default_config; + default_config.normalize_filename = &NormalizeFileName; + absl::SetFlagsUsageConfig(default_config); + } + + private: + flags::FlagSaver flag_saver_; +}; + +// -------------------------------------------------------------------- + +using UsageReportingDeathTest = UsageReportingTest; + +TEST_F(UsageReportingDeathTest, TestSetProgramUsageMessage) { + EXPECT_EQ(absl::ProgramUsageMessage(), kTestUsageMessage); + +#ifndef _WIN32 + // TODO(rogeeff): figure out why this does not work on Windows. + EXPECT_DEATH(absl::SetProgramUsageMessage("custom usage message"), + ".*SetProgramUsageMessage\\(\\) called twice.*"); +#endif +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_01) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_01"); + std::stringstream test_buf; + + flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); + EXPECT_EQ( + test_buf.str(), + R"( -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; +)"); +} + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_02) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_02"); + std::stringstream test_buf; + + flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); + EXPECT_EQ( + test_buf.str(), + R"( -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; +)"); +} + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_03) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_03"); + std::stringstream test_buf; + + flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); + EXPECT_EQ( + test_buf.str(), + R"( -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; +)"); +} + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_04) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_04"); + std::stringstream test_buf; + + flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); + EXPECT_EQ( + test_buf.str(), + R"( -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; +)"); +} + +TEST_F(UsageReportingTest, TestFlagHelpHRF_on_flag_05) { + const auto* flag = flags::FindCommandLineFlag("usage_reporting_test_flag_05"); + std::stringstream test_buf; + + flags::FlagHelp(test_buf, *flag, flags::HelpFormat::kHumanReadable); + EXPECT_EQ( + test_buf.str(), + R"( -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestFlagsHelpHRF) { + std::string usage_test_flags_out = + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; +)"; + + std::stringstream test_buf_01; + flags::FlagsHelp(test_buf_01, "usage_test.cc", + flags::HelpFormat::kHumanReadable, kTestUsageMessage); + EXPECT_EQ(test_buf_01.str(), usage_test_flags_out); + + std::stringstream test_buf_02; + flags::FlagsHelp(test_buf_02, "flags/internal/usage_test.cc", + flags::HelpFormat::kHumanReadable, kTestUsageMessage); + EXPECT_EQ(test_buf_02.str(), usage_test_flags_out); + + std::stringstream test_buf_03; + flags::FlagsHelp(test_buf_03, "usage_test", flags::HelpFormat::kHumanReadable, + kTestUsageMessage); + EXPECT_EQ(test_buf_03.str(), usage_test_flags_out); + + std::stringstream test_buf_04; + flags::FlagsHelp(test_buf_04, "flags/invalid_file_name.cc", + flags::HelpFormat::kHumanReadable, kTestUsageMessage); + EXPECT_EQ(test_buf_04.str(), + R"(usage_test: Custom usage message + + No modules matched: use -helpfull +)"); + + std::stringstream test_buf_05; + flags::FlagsHelp(test_buf_05, "", flags::HelpFormat::kHumanReadable, + kTestUsageMessage); + std::string test_out = test_buf_05.str(); + absl::string_view test_out_str(test_out); + EXPECT_TRUE( + absl::StartsWith(test_out_str, "usage_test: Custom usage message")); + EXPECT_TRUE(absl::StrContains( + test_out_str, "Flags from absl/flags/internal/usage_test.cc:")); + EXPECT_TRUE(absl::StrContains(test_out_str, + "Flags from absl/flags/internal/usage.cc:")); + EXPECT_TRUE( + absl::StrContains(test_out_str, "-usage_reporting_test_flag_01 ")); + EXPECT_TRUE(absl::StrContains(test_out_str, "-help (show help")) + << test_out_str; +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestNoUsageFlags) { + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), -1); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_helpshort) { + absl::SetFlag(&FLAGS_helpshort, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); + EXPECT_EQ(test_buf.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_help) { + absl::SetFlag(&FLAGS_help, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); + EXPECT_EQ(test_buf.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; + +Try --helpfull to get a list of all flags. +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_helppackage) { + absl::SetFlag(&FLAGS_helppackage, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 1); + EXPECT_EQ(test_buf.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; + +Try --helpfull to get a list of all flags. +)"); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_version) { + absl::SetFlag(&FLAGS_version, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0); +#ifndef NDEBUG + EXPECT_EQ(test_buf.str(), "usage_test\nDebug build (NDEBUG not #defined)\n"); +#else + EXPECT_EQ(test_buf.str(), "usage_test\n"); +#endif +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_only_check_args) { + absl::SetFlag(&FLAGS_only_check_args, true); + + std::stringstream test_buf; + EXPECT_EQ(flags::HandleUsageFlags(test_buf, kTestUsageMessage), 0); + EXPECT_EQ(test_buf.str(), ""); +} + +// -------------------------------------------------------------------- + +TEST_F(UsageReportingTest, TestUsageFlag_helpon) { + absl::SetFlag(&FLAGS_helpon, "bla-bla"); + + std::stringstream test_buf_01; + EXPECT_EQ(flags::HandleUsageFlags(test_buf_01, kTestUsageMessage), 1); + EXPECT_EQ(test_buf_01.str(), + R"(usage_test: Custom usage message + + No modules matched: use -helpfull +)"); + + absl::SetFlag(&FLAGS_helpon, "usage_test"); + + std::stringstream test_buf_02; + EXPECT_EQ(flags::HandleUsageFlags(test_buf_02, kTestUsageMessage), 1); + EXPECT_EQ(test_buf_02.str(), + R"(usage_test: Custom usage message + + Flags from absl/flags/internal/usage_test.cc: + -usage_reporting_test_flag_01 (usage_reporting_test_flag_01 help message); + default: 101; + -usage_reporting_test_flag_02 (usage_reporting_test_flag_02 help message); + default: false; + -usage_reporting_test_flag_03 (usage_reporting_test_flag_03 help message); + default: 1.03; + -usage_reporting_test_flag_04 (usage_reporting_test_flag_04 help message); + default: 1000000000000004; + -usage_reporting_test_flag_05 (usage_reporting_test_flag_05 help message); + default: UDT{}; + -usage_reporting_test_flag_06 (usage_reporting_test_flag_06 help message. + + Some more help. + Even more long long long long long long long long long long long long help + message.); default: ""; +)"); +} + +// -------------------------------------------------------------------- + +} // namespace + +int main(int argc, char* argv[]) { + absl::GetFlag(FLAGS_undefok); // Force linking of parse.cc + flags::SetProgramInvocationName("usage_test"); + absl::SetProgramUsageMessage(kTestUsageMessage); + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/absl/flags/marshalling.cc b/absl/flags/marshalling.cc new file mode 100644 index 00000000..71b01d77 --- /dev/null +++ b/absl/flags/marshalling.cc @@ -0,0 +1,191 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/marshalling.h" + +#include <limits> + +#include "absl/base/macros.h" +#include "absl/strings/match.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// -------------------------------------------------------------------- +// AbslParseFlag specializations for boolean type. + +bool AbslParseFlag(absl::string_view text, bool* dst, std::string*) { + const char* kTrue[] = {"1", "t", "true", "y", "yes"}; + const char* kFalse[] = {"0", "f", "false", "n", "no"}; + static_assert(sizeof(kTrue) == sizeof(kFalse), "true_false_equal"); + + text = absl::StripAsciiWhitespace(text); + + for (size_t i = 0; i < ABSL_ARRAYSIZE(kTrue); ++i) { + if (absl::EqualsIgnoreCase(text, kTrue[i])) { + *dst = true; + return true; + } else if (absl::EqualsIgnoreCase(text, kFalse[i])) { + *dst = false; + return true; + } + } + return false; // didn't match a legal input +} + +// -------------------------------------------------------------------- +// AbslParseFlag for integral types. + +// Return the base to use for parsing text as an integer. Leading 0x +// puts us in base 16. But leading 0 does not put us in base 8. It +// caused too many bugs when we had that behavior. +static int NumericBase(absl::string_view text) { + const bool hex = (text.size() >= 2 && text[0] == '0' && + (text[1] == 'x' || text[1] == 'X')); + return hex ? 16 : 10; +} + +template <typename IntType> +inline bool ParseFlagImpl(absl::string_view text, IntType* dst) { + text = absl::StripAsciiWhitespace(text); + + return absl::numbers_internal::safe_strtoi_base(text, dst, NumericBase(text)); +} + +bool AbslParseFlag(absl::string_view text, short* dst, std::string*) { + int val; + if (!ParseFlagImpl(text, &val)) return false; + if (static_cast<short>(val) != val) // worked, but number out of range + return false; + *dst = static_cast<short>(val); + return true; +} + +bool AbslParseFlag(absl::string_view text, unsigned short* dst, std::string*) { + unsigned int val; + if (!ParseFlagImpl(text, &val)) return false; + if (static_cast<unsigned short>(val) != + val) // worked, but number out of range + return false; + *dst = static_cast<unsigned short>(val); + return true; +} + +bool AbslParseFlag(absl::string_view text, int* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, unsigned int* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, long* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, unsigned long* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, long long* dst, std::string*) { + return ParseFlagImpl(text, dst); +} + +bool AbslParseFlag(absl::string_view text, unsigned long long* dst, + std::string*) { + return ParseFlagImpl(text, dst); +} + +// -------------------------------------------------------------------- +// AbslParseFlag for floating point types. + +bool AbslParseFlag(absl::string_view text, float* dst, std::string*) { + return absl::SimpleAtof(text, dst); +} + +bool AbslParseFlag(absl::string_view text, double* dst, std::string*) { + return absl::SimpleAtod(text, dst); +} + +// -------------------------------------------------------------------- +// AbslParseFlag for strings. + +bool AbslParseFlag(absl::string_view text, std::string* dst, std::string*) { + dst->assign(text.data(), text.size()); + return true; +} + +// -------------------------------------------------------------------- +// AbslParseFlag for vector of strings. + +bool AbslParseFlag(absl::string_view text, std::vector<std::string>* dst, + std::string*) { + // An empty flag value corresponds to an empty vector, not a vector + // with a single, empty std::string. + if (text.empty()) { + dst->clear(); + return true; + } + *dst = absl::StrSplit(text, ',', absl::AllowEmpty()); + return true; +} + +// -------------------------------------------------------------------- +// AbslUnparseFlag specializations for various builtin flag types. + +std::string Unparse(bool v) { return v ? "true" : "false"; } +std::string Unparse(short v) { return absl::StrCat(v); } +std::string Unparse(unsigned short v) { return absl::StrCat(v); } +std::string Unparse(int v) { return absl::StrCat(v); } +std::string Unparse(unsigned int v) { return absl::StrCat(v); } +std::string Unparse(long v) { return absl::StrCat(v); } +std::string Unparse(unsigned long v) { return absl::StrCat(v); } +std::string Unparse(long long v) { return absl::StrCat(v); } +std::string Unparse(unsigned long long v) { return absl::StrCat(v); } +template <typename T> +std::string UnparseFloatingPointVal(T v) { + // digits10 is guaranteed to roundtrip correctly in std::string -> value -> std::string + // conversions, but may not be enough to represent all the values correctly. + std::string digit10_str = + absl::StrFormat("%.*g", std::numeric_limits<T>::digits10, v); + if (std::isnan(v) || std::isinf(v)) return digit10_str; + + T roundtrip_val = 0; + std::string err; + if (absl::ParseFlag(digit10_str, &roundtrip_val, &err) && + roundtrip_val == v) { + return digit10_str; + } + + // max_digits10 is the number of base-10 digits that are necessary to uniquely + // represent all distinct values. + return absl::StrFormat("%.*g", std::numeric_limits<T>::max_digits10, v); +} +std::string Unparse(float v) { return UnparseFloatingPointVal(v); } +std::string Unparse(double v) { return UnparseFloatingPointVal(v); } +std::string AbslUnparseFlag(absl::string_view v) { return std::string(v); } +std::string AbslUnparseFlag(const std::vector<std::string>& v) { + return absl::StrJoin(v, ","); +} + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/marshalling.h b/absl/flags/marshalling.h new file mode 100644 index 00000000..5598d444 --- /dev/null +++ b/absl/flags/marshalling.h @@ -0,0 +1,255 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: marshalling.h +// ----------------------------------------------------------------------------- +// +// This header file defines the API for extending Abseil flag support to +// custom types, and defines the set of overloads for fundamental types. +// +// Out of the box, the Abseil flags library supports the following types: +// +// * `bool` +// * `int16_t` +// * `uint16_t` +// * `int32_t` +// * `uint32_t` +// * `int64_t` +// * `uint64_t` +// * `float` +// * `double` +// * `std::string` +// * `std::vector<std::string>` +// +// Note that support for integral types is implemented using overloads for +// variable-width fundamental types (`short`, `int`, `long`, etc.). However, +// you should prefer the fixed-width integral types (`int32_t`, `uint64_t`, +// etc.) we've noted above within flag definitions. + +// +// In addition, several Abseil libraries provide their own custom support for +// Abseil flags. +// +// The Abseil time library provides the following support for civil time values: +// +// * `absl::CivilSecond` +// * `absl::CivilMinute` +// * `absl::CivilHour` +// * `absl::CivilDay` +// * `absl::CivilMonth` +// * `absl::CivilYear` +// +// and also provides support for the following absolute time values: +// +// * `absl::Duration` +// * `absl::Time` +// +// Additional support for Abseil types will be noted here as it is added. +// +// You can also provide your own custom flags by adding overloads for +// `AbslParseFlag()` and `AbslUnparseFlag()` to your type definitions. (See +// below.) +// +// ----------------------------------------------------------------------------- +// Adding Type Support for Abseil Flags +// ----------------------------------------------------------------------------- +// +// To add support for your user-defined type, add overloads of `AbslParseFlag()` +// and `AbslUnparseFlag()` as free (non-member) functions to your type. If `T` +// is a class type, these functions can be friend function definitions. These +// overloads must be added to the same namespace where the type is defined, so +// that they can be discovered by Argument-Dependent Lookup (ADL). +// +// Example: +// +// namespace foo { +// +// enum OutputMode { kPlainText, kHtml }; +// +// // AbslParseFlag converts from a string to OutputMode. +// // Must be in same namespace as OutputMode. +// +// // Parses an OutputMode from the command line flag value `text. Returns +// // `true` and sets `*mode` on success; returns `false` and sets `*error` +// // on failure. +// bool AbslParseFlag(absl::string_view text, +// OutputMode* mode, +// std::string* error) { +// if (text == "plaintext") { +// *mode = kPlainText; +// return true; +// } +// if (text == "html") { +// *mode = kHtml; +// return true; +// } +// *error = "unknown value for enumeration"; +// return false; +// } +// +// // AbslUnparseFlag converts from an OutputMode to a string. +// // Must be in same namespace as OutputMode. +// +// // Returns a textual flag value corresponding to the OutputMode `mode`. +// std::string AbslUnparseFlag(OutputMode mode) { +// switch (mode) { +// case kPlainText: return "plaintext"; +// case kHtml: return "html"; +// } +// return absl::StrCat(mode); +// } +// +// Notice that neither `AbslParseFlag()` nor `AbslUnparseFlag()` are class +// members, but free functions. `AbslParseFlag/AbslUnparseFlag()` overloads +// for a type should only be declared in the same file and namespace as said +// type. The proper `AbslParseFlag/AbslUnparseFlag()` implementations for a +// given type will be discovered via Argument-Dependent Lookup (ADL). +// +// `AbslParseFlag()` may need, in turn, to parse simpler constituent types +// using `absl::ParseFlag()`. For example, a custom struct `MyFlagType` +// consisting of a `std::pair<int, std::string>` would add an `AbslParseFlag()` +// overload for its `MyFlagType` like so: +// +// Example: +// +// namespace my_flag_type { +// +// struct MyFlagType { +// std::pair<int, std::string> my_flag_data; +// }; +// +// bool AbslParseFlag(absl::string_view text, MyFlagType* flag, +// std::string* err); +// +// std::string AbslUnparseFlag(const MyFlagType&); +// +// // Within the implementation, `AbslParseFlag()` will, in turn invoke +// // `absl::ParseFlag()` on its constituent `int` and `std::string` types +// // (which have built-in Abseil flag support. +// +// bool AbslParseFlag(absl::string_view text, MyFlagType* flag, +// std::string* err) { +// std::pair<absl::string_view, absl::string_view> tokens = +// absl::StrSplit(text, ','); +// if (!absl::ParseFlag(tokens.first, &flag->my_flag_data.first, err)) +// return false; +// if (!absl::ParseFlag(tokens.second, &flag->my_flag_data.second, err)) +// return false; +// return true; +// } +// +// // Similarly, for unparsing, we can simply invoke `absl::UnparseFlag()` on +// // the constituent types. +// std::string AbslUnparseFlag(const MyFlagType& flag) { +// return absl::StrCat(absl::UnparseFlag(flag.my_flag_data.first), +// ",", +// absl::UnparseFlag(flag.my_flag_data.second)); +// } +#ifndef ABSL_FLAGS_MARSHALLING_H_ +#define ABSL_FLAGS_MARSHALLING_H_ + +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +// Overloads of `AbslParseFlag()` and `AbslUnparseFlag()` for fundamental types. +bool AbslParseFlag(absl::string_view, bool*, std::string*); +bool AbslParseFlag(absl::string_view, short*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned short*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, int*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned int*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, long*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned long*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, long long*, std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, unsigned long long*, + std::string*); // NOLINT +bool AbslParseFlag(absl::string_view, float*, std::string*); +bool AbslParseFlag(absl::string_view, double*, std::string*); +bool AbslParseFlag(absl::string_view, std::string*, std::string*); +bool AbslParseFlag(absl::string_view, std::vector<std::string>*, std::string*); + +template <typename T> +bool InvokeParseFlag(absl::string_view input, T* dst, std::string* err) { + // Comment on next line provides a good compiler error message if T + // does not have AbslParseFlag(absl::string_view, T*, std::string*). + return AbslParseFlag(input, dst, err); // Is T missing AbslParseFlag? +} + +// Strings and std:: containers do not have the same overload resolution +// considerations as fundamental types. Naming these 'AbslUnparseFlag' means we +// can avoid the need for additional specializations of Unparse (below). +std::string AbslUnparseFlag(absl::string_view v); +std::string AbslUnparseFlag(const std::vector<std::string>&); + +template <typename T> +std::string Unparse(const T& v) { + // Comment on next line provides a good compiler error message if T does not + // have UnparseFlag. + return AbslUnparseFlag(v); // Is T missing AbslUnparseFlag? +} + +// Overloads for builtin types. +std::string Unparse(bool v); +std::string Unparse(short v); // NOLINT +std::string Unparse(unsigned short v); // NOLINT +std::string Unparse(int v); // NOLINT +std::string Unparse(unsigned int v); // NOLINT +std::string Unparse(long v); // NOLINT +std::string Unparse(unsigned long v); // NOLINT +std::string Unparse(long long v); // NOLINT +std::string Unparse(unsigned long long v); // NOLINT +std::string Unparse(float v); +std::string Unparse(double v); + +} // namespace flags_internal + +// ParseFlag() +// +// Parses a string value into a flag value of type `T`. Do not add overloads of +// this function for your type directly; instead, add an `AbslParseFlag()` +// free function as documented above. +// +// Some implementations of `AbslParseFlag()` for types which consist of other, +// constituent types which already have Abseil flag support, may need to call +// `absl::ParseFlag()` on those consituent string values. (See above.) +template <typename T> +inline bool ParseFlag(absl::string_view input, T* dst, std::string* error) { + return flags_internal::InvokeParseFlag(input, dst, error); +} + +// UnparseFlag() +// +// Unparses a flag value of type `T` into a string value. Do not add overloads +// of this function for your type directly; instead, add an `AbslUnparseFlag()` +// free function as documented above. +// +// Some implementations of `AbslUnparseFlag()` for types which consist of other, +// constituent types which already have Abseil flag support, may want to call +// `absl::UnparseFlag()` on those constituent types. (See above.) +template <typename T> +inline std::string UnparseFlag(const T& v) { + return flags_internal::Unparse(v); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_MARSHALLING_H_ diff --git a/absl/flags/marshalling_test.cc b/absl/flags/marshalling_test.cc new file mode 100644 index 00000000..37cd1940 --- /dev/null +++ b/absl/flags/marshalling_test.cc @@ -0,0 +1,899 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/marshalling.h" + +#include <cmath> + +#include "gtest/gtest.h" + +namespace { + +TEST(MarshallingTest, TestBoolParsing) { + std::string err; + bool value; + + // True values. + EXPECT_TRUE(absl::ParseFlag("True", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("true", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("TRUE", &value, &err)); + EXPECT_TRUE(value); + + EXPECT_TRUE(absl::ParseFlag("Yes", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("yes", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("YES", &value, &err)); + EXPECT_TRUE(value); + + EXPECT_TRUE(absl::ParseFlag("t", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("T", &value, &err)); + EXPECT_TRUE(value); + + EXPECT_TRUE(absl::ParseFlag("y", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("Y", &value, &err)); + EXPECT_TRUE(value); + + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_TRUE(value); + + // False values. + EXPECT_TRUE(absl::ParseFlag("False", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("false", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("FALSE", &value, &err)); + EXPECT_FALSE(value); + + EXPECT_TRUE(absl::ParseFlag("No", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("no", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("NO", &value, &err)); + EXPECT_FALSE(value); + + EXPECT_TRUE(absl::ParseFlag("f", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("F", &value, &err)); + EXPECT_FALSE(value); + + EXPECT_TRUE(absl::ParseFlag("n", &value, &err)); + EXPECT_FALSE(value); + EXPECT_TRUE(absl::ParseFlag("N", &value, &err)); + EXPECT_FALSE(value); + + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_FALSE(value); + + // Whitespace handling. + EXPECT_TRUE(absl::ParseFlag(" true", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag("true ", &value, &err)); + EXPECT_TRUE(value); + EXPECT_TRUE(absl::ParseFlag(" true ", &value, &err)); + EXPECT_TRUE(value); + + // Invalid input. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("11", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("tt", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt16Parsing) { + std::string err; + int16_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("-1", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("-18765", &value, &err)); + EXPECT_EQ(value, -18765); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("-001", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err)); + EXPECT_EQ(value, 564); + // TODO(rogeeff): fix below validations + EXPECT_FALSE(absl::ParseFlag("-0x7FFD", &value, &err)); + EXPECT_NE(value, -3); + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x22 ", &value, &err)); + EXPECT_EQ(value, 34); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("40000", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint16Parsing) { + std::string err; + uint16_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("001", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err)); + EXPECT_EQ(value, 564); + // TODO(rogeeff): fix below validations + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x22 ", &value, &err)); + EXPECT_EQ(value, 34); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("70000", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("-1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt32Parsing) { + std::string err; + int32_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("-1", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("-98765", &value, &err)); + EXPECT_EQ(value, -98765); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("-001", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err)); + EXPECT_EQ(value, 564); + // TODO(rogeeff): fix below validations + EXPECT_FALSE(absl::ParseFlag("-0x7FFFFFFD", &value, &err)); + EXPECT_NE(value, -3); + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x22 ", &value, &err)); + EXPECT_EQ(value, 34); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("70000000000", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint32Parsing) { + std::string err; + uint32_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0X234", &value, &err)); + EXPECT_EQ(value, 564); + EXPECT_TRUE(absl::ParseFlag("0xFFFFFFFD", &value, &err)); + EXPECT_EQ(value, 4294967293); + // TODO(rogeeff): fix below validations + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x22 ", &value, &err)); + EXPECT_EQ(value, 34); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("140000000000", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("-1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt64Parsing) { + std::string err; + int64_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("-1", &value, &err)); + EXPECT_EQ(value, -1); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("-98765", &value, &err)); + EXPECT_EQ(value, -98765); + EXPECT_TRUE(absl::ParseFlag("+3", &value, &err)); + EXPECT_EQ(value, 3); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("001", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0000100", &value, &err)); + EXPECT_EQ(value, 100); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0XFFFAAABBBCCCDDD", &value, &err)); + EXPECT_EQ(value, 1152827684197027293); + // TODO(rogeeff): fix below validation + EXPECT_FALSE(absl::ParseFlag("-0x7FFFFFFFFFFFFFFE", &value, &err)); + EXPECT_NE(value, -2); + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + EXPECT_TRUE(absl::ParseFlag(" 0x7F ", &value, &err)); + EXPECT_EQ(value, 127); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("0xFFFFFFFFFFFFFFFFFF", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUInt64Parsing) { + std::string err; + uint64_t value; + + // Decimal values. + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0", &value, &err)); + EXPECT_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("123", &value, &err)); + EXPECT_EQ(value, 123); + EXPECT_TRUE(absl::ParseFlag("+13", &value, &err)); + EXPECT_EQ(value, 13); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("001", &value, &err)); + EXPECT_EQ(value, 1); + EXPECT_TRUE(absl::ParseFlag("0000300", &value, &err)); + EXPECT_EQ(value, 300); + + // Hex values. + EXPECT_TRUE(absl::ParseFlag("0x10", &value, &err)); + EXPECT_EQ(value, 16); + EXPECT_TRUE(absl::ParseFlag("0XFFFF", &value, &err)); + EXPECT_EQ(value, 65535); + // TODO(rogeeff): fix below validation + EXPECT_FALSE(absl::ParseFlag("+0x31", &value, &err)); + EXPECT_NE(value, 49); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10 ", &value, &err)); + EXPECT_EQ(value, 10); + EXPECT_TRUE(absl::ParseFlag(" 11", &value, &err)); + EXPECT_EQ(value, 11); + EXPECT_TRUE(absl::ParseFlag(" 012 ", &value, &err)); + EXPECT_EQ(value, 12); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("0xFFFFFFFFFFFFFFFFFF", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("-1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2U", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("FFF", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestFloatParsing) { + std::string err; + float value; + + // Ordinary values. + EXPECT_TRUE(absl::ParseFlag("1.3", &value, &err)); + EXPECT_FLOAT_EQ(value, 1.3f); + EXPECT_TRUE(absl::ParseFlag("-0.1", &value, &err)); + EXPECT_DOUBLE_EQ(value, -0.1f); + EXPECT_TRUE(absl::ParseFlag("+0.01", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.01f); + + // Scientific values. + EXPECT_TRUE(absl::ParseFlag("1.2e3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.2e3f); + EXPECT_TRUE(absl::ParseFlag("9.8765402e-37", &value, &err)); + EXPECT_DOUBLE_EQ(value, 9.8765402e-37f); + EXPECT_TRUE(absl::ParseFlag("0.11e+3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.11e+3f); + EXPECT_TRUE(absl::ParseFlag("1.e-2300", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.f); + EXPECT_TRUE(absl::ParseFlag("1.e+2300", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01.6", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.6f); + EXPECT_TRUE(absl::ParseFlag("000.0001", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.0001f); + + // Trailing zero values. + EXPECT_TRUE(absl::ParseFlag("-5.1000", &value, &err)); + EXPECT_DOUBLE_EQ(value, -5.1f); + + // Exceptional values. + EXPECT_TRUE(absl::ParseFlag("NaN", &value, &err)); + EXPECT_TRUE(std::isnan(value)); + EXPECT_TRUE(absl::ParseFlag("Inf", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + + // Hex values + EXPECT_TRUE(absl::ParseFlag("0x10.23p12", &value, &err)); + EXPECT_DOUBLE_EQ(value, 66096.f); + EXPECT_TRUE(absl::ParseFlag("-0xF1.A3p-2", &value, &err)); + EXPECT_NEAR(value, -60.4092f, 5e-5f); + EXPECT_TRUE(absl::ParseFlag("+0x0.0AAp-12", &value, &err)); + EXPECT_NEAR(value, 1.01328e-05f, 5e-11f); + EXPECT_TRUE(absl::ParseFlag("0x.01p1", &value, &err)); + EXPECT_NEAR(value, 0.0078125f, 5e-8f); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10.1 ", &value, &err)); + EXPECT_DOUBLE_EQ(value, 10.1f); + EXPECT_TRUE(absl::ParseFlag(" 2.34", &value, &err)); + EXPECT_DOUBLE_EQ(value, 2.34f); + EXPECT_TRUE(absl::ParseFlag(" 5.7 ", &value, &err)); + EXPECT_DOUBLE_EQ(value, 5.7f); + EXPECT_TRUE(absl::ParseFlag(" -0xE0.F3p01 ", &value, &err)); + EXPECT_NEAR(value, -449.8984375f, 5e-8f); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2.3xxx", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("0x0.1pAA", &value, &err)); + // TODO(rogeeff): below assertion should fail + EXPECT_TRUE(absl::ParseFlag("0x0.1", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestDoubleParsing) { + std::string err; + double value; + + // Ordinary values. + EXPECT_TRUE(absl::ParseFlag("1.3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.3); + EXPECT_TRUE(absl::ParseFlag("-0.1", &value, &err)); + EXPECT_DOUBLE_EQ(value, -0.1); + EXPECT_TRUE(absl::ParseFlag("+0.01", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.01); + + // Scientific values. + EXPECT_TRUE(absl::ParseFlag("1.2e3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.2e3); + EXPECT_TRUE(absl::ParseFlag("9.00000002e-123", &value, &err)); + EXPECT_DOUBLE_EQ(value, 9.00000002e-123); + EXPECT_TRUE(absl::ParseFlag("0.11e+3", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.11e+3); + EXPECT_TRUE(absl::ParseFlag("1.e-2300", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0); + EXPECT_TRUE(absl::ParseFlag("1.e+2300", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + + // Leading zero values. + EXPECT_TRUE(absl::ParseFlag("01.6", &value, &err)); + EXPECT_DOUBLE_EQ(value, 1.6); + EXPECT_TRUE(absl::ParseFlag("000.0001", &value, &err)); + EXPECT_DOUBLE_EQ(value, 0.0001); + + // Trailing zero values. + EXPECT_TRUE(absl::ParseFlag("-5.1000", &value, &err)); + EXPECT_DOUBLE_EQ(value, -5.1); + + // Exceptional values. + EXPECT_TRUE(absl::ParseFlag("NaN", &value, &err)); + EXPECT_TRUE(std::isnan(value)); + EXPECT_TRUE(absl::ParseFlag("nan", &value, &err)); + EXPECT_TRUE(std::isnan(value)); + EXPECT_TRUE(absl::ParseFlag("Inf", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + EXPECT_TRUE(absl::ParseFlag("inf", &value, &err)); + EXPECT_TRUE(std::isinf(value)); + + // Hex values + EXPECT_TRUE(absl::ParseFlag("0x10.23p12", &value, &err)); + EXPECT_DOUBLE_EQ(value, 66096); + EXPECT_TRUE(absl::ParseFlag("-0xF1.A3p-2", &value, &err)); + EXPECT_NEAR(value, -60.4092, 5e-5); + EXPECT_TRUE(absl::ParseFlag("+0x0.0AAp-12", &value, &err)); + EXPECT_NEAR(value, 1.01328e-05, 5e-11); + EXPECT_TRUE(absl::ParseFlag("0x.01p1", &value, &err)); + EXPECT_NEAR(value, 0.0078125, 5e-8); + + // Whitespace handling + EXPECT_TRUE(absl::ParseFlag("10.1 ", &value, &err)); + EXPECT_DOUBLE_EQ(value, 10.1); + EXPECT_TRUE(absl::ParseFlag(" 2.34", &value, &err)); + EXPECT_DOUBLE_EQ(value, 2.34); + EXPECT_TRUE(absl::ParseFlag(" 5.7 ", &value, &err)); + EXPECT_DOUBLE_EQ(value, 5.7); + EXPECT_TRUE(absl::ParseFlag(" -0xE0.F3p01 ", &value, &err)); + EXPECT_NEAR(value, -449.8984375, 5e-8); + + // Invalid values. + EXPECT_FALSE(absl::ParseFlag("", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag(" ", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("--1", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\n", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("\t", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("2.3xxx", &value, &err)); + EXPECT_FALSE(absl::ParseFlag("0x0.1pAA", &value, &err)); + // TODO(rogeeff): below assertion should fail + EXPECT_TRUE(absl::ParseFlag("0x0.1", &value, &err)); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestStringParsing) { + std::string err; + std::string value; + + EXPECT_TRUE(absl::ParseFlag("", &value, &err)); + EXPECT_EQ(value, ""); + EXPECT_TRUE(absl::ParseFlag(" ", &value, &err)); + EXPECT_EQ(value, " "); + EXPECT_TRUE(absl::ParseFlag(" ", &value, &err)); + EXPECT_EQ(value, " "); + EXPECT_TRUE(absl::ParseFlag("\n", &value, &err)); + EXPECT_EQ(value, "\n"); + EXPECT_TRUE(absl::ParseFlag("\t", &value, &err)); + EXPECT_EQ(value, "\t"); + EXPECT_TRUE(absl::ParseFlag("asdfg", &value, &err)); + EXPECT_EQ(value, "asdfg"); + EXPECT_TRUE(absl::ParseFlag("asdf ghjk", &value, &err)); + EXPECT_EQ(value, "asdf ghjk"); + EXPECT_TRUE(absl::ParseFlag("a\nb\nc", &value, &err)); + EXPECT_EQ(value, "a\nb\nc"); + EXPECT_TRUE(absl::ParseFlag("asd\0fgh", &value, &err)); + EXPECT_EQ(value, "asd"); + EXPECT_TRUE(absl::ParseFlag("\\\\", &value, &err)); + EXPECT_EQ(value, "\\\\"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestVectorOfStringParsing) { + std::string err; + std::vector<std::string> value; + + EXPECT_TRUE(absl::ParseFlag("", &value, &err)); + EXPECT_EQ(value, std::vector<std::string>{}); + EXPECT_TRUE(absl::ParseFlag("1", &value, &err)); + EXPECT_EQ(value, std::vector<std::string>({"1"})); + EXPECT_TRUE(absl::ParseFlag("a,b", &value, &err)); + EXPECT_EQ(value, std::vector<std::string>({"a", "b"})); + EXPECT_TRUE(absl::ParseFlag("a,b,c,", &value, &err)); + EXPECT_EQ(value, std::vector<std::string>({"a", "b", "c", ""})); + EXPECT_TRUE(absl::ParseFlag("a,,", &value, &err)); + EXPECT_EQ(value, std::vector<std::string>({"a", "", ""})); + EXPECT_TRUE(absl::ParseFlag(",", &value, &err)); + EXPECT_EQ(value, std::vector<std::string>({"", ""})); + EXPECT_TRUE(absl::ParseFlag("a, b,c ", &value, &err)); + EXPECT_EQ(value, std::vector<std::string>({"a", " b", "c "})); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestBoolUnparsing) { + EXPECT_EQ(absl::UnparseFlag(true), "true"); + EXPECT_EQ(absl::UnparseFlag(false), "false"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt16Unparsing) { + int16_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = -1; + EXPECT_EQ(absl::UnparseFlag(value), "-1"); + value = 9876; + EXPECT_EQ(absl::UnparseFlag(value), "9876"); + value = -987; + EXPECT_EQ(absl::UnparseFlag(value), "-987"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint16Unparsing) { + uint16_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = 19876; + EXPECT_EQ(absl::UnparseFlag(value), "19876"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt32Unparsing) { + int32_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = -1; + EXPECT_EQ(absl::UnparseFlag(value), "-1"); + value = 12345; + EXPECT_EQ(absl::UnparseFlag(value), "12345"); + value = -987; + EXPECT_EQ(absl::UnparseFlag(value), "-987"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint32Unparsing) { + uint32_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = 1234500; + EXPECT_EQ(absl::UnparseFlag(value), "1234500"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestInt64Unparsing) { + int64_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = -1; + EXPECT_EQ(absl::UnparseFlag(value), "-1"); + value = 123456789L; + EXPECT_EQ(absl::UnparseFlag(value), "123456789"); + value = -987654321L; + EXPECT_EQ(absl::UnparseFlag(value), "-987654321"); + value = 0x7FFFFFFFFFFFFFFF; + EXPECT_EQ(absl::UnparseFlag(value), "9223372036854775807"); + value = 0xFFFFFFFFFFFFFFFF; + EXPECT_EQ(absl::UnparseFlag(value), "-1"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestUint64Unparsing) { + uint64_t value; + + value = 1; + EXPECT_EQ(absl::UnparseFlag(value), "1"); + value = 0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = 123456789L; + EXPECT_EQ(absl::UnparseFlag(value), "123456789"); + value = 0xFFFFFFFFFFFFFFFF; + EXPECT_EQ(absl::UnparseFlag(value), "18446744073709551615"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestFloatUnparsing) { + float value; + + value = 1.1f; + EXPECT_EQ(absl::UnparseFlag(value), "1.1"); + value = 0.01f; + EXPECT_EQ(absl::UnparseFlag(value), "0.01"); + value = 1.23e-2f; + EXPECT_EQ(absl::UnparseFlag(value), "0.0123"); + value = -0.71f; + EXPECT_EQ(absl::UnparseFlag(value), "-0.71"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestDoubleUnparsing) { + double value; + + value = 1.1; + EXPECT_EQ(absl::UnparseFlag(value), "1.1"); + value = 0.01; + EXPECT_EQ(absl::UnparseFlag(value), "0.01"); + value = 1.23e-2; + EXPECT_EQ(absl::UnparseFlag(value), "0.0123"); + value = -0.71; + EXPECT_EQ(absl::UnparseFlag(value), "-0.71"); + value = -0; + EXPECT_EQ(absl::UnparseFlag(value), "0"); + value = std::nan(""); + EXPECT_EQ(absl::UnparseFlag(value), "nan"); + value = std::numeric_limits<double>::infinity(); + EXPECT_EQ(absl::UnparseFlag(value), "inf"); +} + +// -------------------------------------------------------------------- + +TEST(MarshallingTest, TestStringUnparsing) { + EXPECT_EQ(absl::UnparseFlag(""), ""); + EXPECT_EQ(absl::UnparseFlag(" "), " "); + EXPECT_EQ(absl::UnparseFlag("qwerty"), "qwerty"); + EXPECT_EQ(absl::UnparseFlag("ASDFGH"), "ASDFGH"); + EXPECT_EQ(absl::UnparseFlag("\n\t "), "\n\t "); +} + +// -------------------------------------------------------------------- + +template <typename T> +void TestRoundtrip(T v) { + T new_v; + std::string err; + EXPECT_TRUE(absl::ParseFlag(absl::UnparseFlag(v), &new_v, &err)); + EXPECT_EQ(new_v, v); +} + +TEST(MarshallingTest, TestFloatRoundTrip) { + TestRoundtrip(0.1f); + TestRoundtrip(0.12f); + TestRoundtrip(0.123f); + TestRoundtrip(0.1234f); + TestRoundtrip(0.12345f); + TestRoundtrip(0.123456f); + TestRoundtrip(0.1234567f); + TestRoundtrip(0.12345678f); + + TestRoundtrip(0.1e20f); + TestRoundtrip(0.12e20f); + TestRoundtrip(0.123e20f); + TestRoundtrip(0.1234e20f); + TestRoundtrip(0.12345e20f); + TestRoundtrip(0.123456e20f); + TestRoundtrip(0.1234567e20f); + TestRoundtrip(0.12345678e20f); + + TestRoundtrip(0.1e-20f); + TestRoundtrip(0.12e-20f); + TestRoundtrip(0.123e-20f); + TestRoundtrip(0.1234e-20f); + TestRoundtrip(0.12345e-20f); + TestRoundtrip(0.123456e-20f); + TestRoundtrip(0.1234567e-20f); + TestRoundtrip(0.12345678e-20f); +} + +TEST(MarshallingTest, TestDoubleRoundTrip) { + TestRoundtrip(0.1); + TestRoundtrip(0.12); + TestRoundtrip(0.123); + TestRoundtrip(0.1234); + TestRoundtrip(0.12345); + TestRoundtrip(0.123456); + TestRoundtrip(0.1234567); + TestRoundtrip(0.12345678); + TestRoundtrip(0.123456789); + TestRoundtrip(0.1234567891); + TestRoundtrip(0.12345678912); + TestRoundtrip(0.123456789123); + TestRoundtrip(0.1234567891234); + TestRoundtrip(0.12345678912345); + TestRoundtrip(0.123456789123456); + TestRoundtrip(0.1234567891234567); + TestRoundtrip(0.12345678912345678); + + TestRoundtrip(0.1e50); + TestRoundtrip(0.12e50); + TestRoundtrip(0.123e50); + TestRoundtrip(0.1234e50); + TestRoundtrip(0.12345e50); + TestRoundtrip(0.123456e50); + TestRoundtrip(0.1234567e50); + TestRoundtrip(0.12345678e50); + TestRoundtrip(0.123456789e50); + TestRoundtrip(0.1234567891e50); + TestRoundtrip(0.12345678912e50); + TestRoundtrip(0.123456789123e50); + TestRoundtrip(0.1234567891234e50); + TestRoundtrip(0.12345678912345e50); + TestRoundtrip(0.123456789123456e50); + TestRoundtrip(0.1234567891234567e50); + TestRoundtrip(0.12345678912345678e50); + + TestRoundtrip(0.1e-50); + TestRoundtrip(0.12e-50); + TestRoundtrip(0.123e-50); + TestRoundtrip(0.1234e-50); + TestRoundtrip(0.12345e-50); + TestRoundtrip(0.123456e-50); + TestRoundtrip(0.1234567e-50); + TestRoundtrip(0.12345678e-50); + TestRoundtrip(0.123456789e-50); + TestRoundtrip(0.1234567891e-50); + TestRoundtrip(0.12345678912e-50); + TestRoundtrip(0.123456789123e-50); + TestRoundtrip(0.1234567891234e-50); + TestRoundtrip(0.12345678912345e-50); + TestRoundtrip(0.123456789123456e-50); + TestRoundtrip(0.1234567891234567e-50); + TestRoundtrip(0.12345678912345678e-50); +} + +} // namespace diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc new file mode 100644 index 00000000..fd80a0c8 --- /dev/null +++ b/absl/flags/parse.cc @@ -0,0 +1,755 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/parse.h" + +#include <stdlib.h> + +#include <fstream> +#include <iostream> +#include <tuple> + +#ifdef _WIN32 +#include <windows.h> +#endif + +#include "absl/flags/flag.h" +#include "absl/flags/internal/program_name.h" +#include "absl/flags/internal/registry.h" +#include "absl/flags/internal/usage.h" +#include "absl/flags/usage.h" +#include "absl/flags/usage_config.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/strip.h" +#include "absl/synchronization/mutex.h" + +// -------------------------------------------------------------------- + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { +namespace { + +ABSL_CONST_INIT absl::Mutex processing_checks_guard(absl::kConstInit); + +ABSL_CONST_INIT bool flagfile_needs_processing + GUARDED_BY(processing_checks_guard) = false; +ABSL_CONST_INIT bool fromenv_needs_processing + GUARDED_BY(processing_checks_guard) = false; +ABSL_CONST_INIT bool tryfromenv_needs_processing + GUARDED_BY(processing_checks_guard) = false; + +} // namespace +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +ABSL_FLAG(std::vector<std::string>, flagfile, {}, + "comma-separated list of files to load flags from") + .OnUpdate([]() { + if (absl::GetFlag(FLAGS_flagfile).empty()) return; + + absl::MutexLock l(&absl::flags_internal::processing_checks_guard); + + // Setting this flag twice before it is handled most likely an internal + // error and should be reviewed by developers. + if (absl::flags_internal::flagfile_needs_processing) { + ABSL_INTERNAL_LOG(WARNING, "flagfile set twice before it is handled"); + } + + absl::flags_internal::flagfile_needs_processing = true; + }); +ABSL_FLAG(std::vector<std::string>, fromenv, {}, + "comma-separated list of flags to set from the environment" + " [use 'export FLAGS_flag1=value']") + .OnUpdate([]() { + if (absl::GetFlag(FLAGS_fromenv).empty()) return; + + absl::MutexLock l(&absl::flags_internal::processing_checks_guard); + + // Setting this flag twice before it is handled most likely an internal + // error and should be reviewed by developers. + if (absl::flags_internal::fromenv_needs_processing) { + ABSL_INTERNAL_LOG(WARNING, "fromenv set twice before it is handled."); + } + + absl::flags_internal::fromenv_needs_processing = true; + }); +ABSL_FLAG(std::vector<std::string>, tryfromenv, {}, + "comma-separated list of flags to try to set from the environment if " + "present") + .OnUpdate([]() { + if (absl::GetFlag(FLAGS_tryfromenv).empty()) return; + + absl::MutexLock l(&absl::flags_internal::processing_checks_guard); + + // Setting this flag twice before it is handled most likely an internal + // error and should be reviewed by developers. + if (absl::flags_internal::tryfromenv_needs_processing) { + ABSL_INTERNAL_LOG(WARNING, + "tryfromenv set twice before it is handled."); + } + + absl::flags_internal::tryfromenv_needs_processing = true; + }); + +ABSL_FLAG(std::vector<std::string>, undefok, {}, + "comma-separated list of flag names that it is okay to specify " + "on the command line even if the program does not define a flag " + "with that name"); + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +namespace { + +class ArgsList { + public: + ArgsList() : next_arg_(0) {} + ArgsList(int argc, char* argv[]) : args_(argv, argv + argc), next_arg_(0) {} + explicit ArgsList(const std::vector<std::string>& args) + : args_(args), next_arg_(0) {} + + // Returns success status: true if parsing successful, false otherwise. + bool ReadFromFlagfile(const std::string& flag_file_name); + + int Size() const { return args_.size() - next_arg_; } + int FrontIndex() const { return next_arg_; } + absl::string_view Front() const { return args_[next_arg_]; } + void PopFront() { next_arg_++; } + + private: + std::vector<std::string> args_; + int next_arg_; +}; + +bool ArgsList::ReadFromFlagfile(const std::string& flag_file_name) { + std::ifstream flag_file(flag_file_name); + + if (!flag_file) { + flags_internal::ReportUsageError( + absl::StrCat("Can't open flagfile ", flag_file_name), true); + + return false; + } + + // This argument represents fake argv[0], which should be present in all arg + // lists. + args_.push_back(""); + + std::string line; + bool success = true; + + while (std::getline(flag_file, line)) { + absl::string_view stripped = absl::StripLeadingAsciiWhitespace(line); + + if (stripped.empty() || stripped[0] == '#') { + // Comment or empty line; just ignore. + continue; + } + + if (stripped[0] == '-') { + if (stripped == "--") { + flags_internal::ReportUsageError( + "Flagfile can't contain position arguments or --", true); + + success = false; + break; + } + + args_.push_back(std::string(stripped)); + continue; + } + + flags_internal::ReportUsageError( + absl::StrCat("Unexpected line in the flagfile ", flag_file_name, ": ", + line), + true); + + success = false; + } + + return success; +} + +// -------------------------------------------------------------------- + +// Reads the environment variable with name `name` and stores results in +// `value`. If variable is not present in environment returns false, otherwise +// returns true. +bool GetEnvVar(const char* var_name, std::string* var_value) { +#ifdef _WIN32 + char buf[1024]; + auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf)); + if (get_res >= sizeof(buf)) { + return false; + } + + if (get_res == 0) { + return false; + } + + *var_value = std::string(buf, get_res); +#else + const char* val = ::getenv(var_name); + if (val == nullptr) { + return false; + } + + *var_value = val; +#endif + + return true; +} + +// -------------------------------------------------------------------- + +// Returns: +// Flag name or empty if arg= -- +// Flag value after = in --flag=value (empty if --foo) +// "Is empty value" status. True if arg= --foo=, false otherwise. This is +// required to separate --foo from --foo=. +// For example: +// arg return values +// "--foo=bar" -> {"foo", "bar", false}. +// "--foo" -> {"foo", "", false}. +// "--foo=" -> {"foo", "", true}. +std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue( + absl::string_view arg) { + // Allow -foo and --foo + absl::ConsumePrefix(&arg, "-"); + + if (arg.empty()) { + return std::make_tuple("", "", false); + } + + auto equal_sign_pos = arg.find("="); + + absl::string_view flag_name = arg.substr(0, equal_sign_pos); + + absl::string_view value; + bool is_empty_value = false; + + if (equal_sign_pos != absl::string_view::npos) { + value = arg.substr(equal_sign_pos + 1); + is_empty_value = value.empty(); + } + + return std::make_tuple(flag_name, value, is_empty_value); +} + +// -------------------------------------------------------------------- + +// Returns: +// found flag or nullptr +// is negative in case of --nofoo +std::tuple<CommandLineFlag*, bool> LocateFlag(absl::string_view flag_name) { + CommandLineFlag* flag = flags_internal::FindCommandLineFlag(flag_name); + bool is_negative = false; + + if (!flag && absl::ConsumePrefix(&flag_name, "no")) { + flag = flags_internal::FindCommandLineFlag(flag_name); + is_negative = true; + } + + return std::make_tuple(flag, is_negative); +} + +// -------------------------------------------------------------------- + +// Verify that default values of typed flags must be convertible to string and +// back. +void CheckDefaultValuesParsingRoundtrip() { +#ifndef NDEBUG + flags_internal::ForEachFlag([&](CommandLineFlag* flag) { + if (flag->IsRetired()) return; + +#define IGNORE_TYPE(T) \ + if (flag->IsOfType<T>()) return; + + ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(IGNORE_TYPE) + IGNORE_TYPE(std::string) + IGNORE_TYPE(std::vector<std::string>) +#undef IGNORE_TYPE + + flag->CheckDefaultValueParsingRoundtrip(); + }); +#endif +} + +// -------------------------------------------------------------------- + +// Returns success status, which is true if we successfully read all flag files, +// in which case new ArgLists are appended to the input_args in a reverse order +// of file names in the input flagfiles list. This order ensures that flags from +// the first flagfile in the input list are processed before the second flagfile +// etc. +bool ReadFlagfiles(const std::vector<std::string>& flagfiles, + std::vector<ArgsList>* input_args) { + bool success = true; + for (auto it = flagfiles.rbegin(); it != flagfiles.rend(); ++it) { + ArgsList al; + + if (al.ReadFromFlagfile(*it)) { + input_args->push_back(al); + } else { + success = false; + } + } + + return success; +} + +// Returns success status, which is true if were able to locate all environment +// variables correctly or if fail_on_absent_in_env is false. The environment +// variable names are expected to be of the form `FLAGS_<flag_name>`, where +// `flag_name` is a string from the input flag_names list. If successful we +// append a single ArgList at the end of the input_args. +bool ReadFlagsFromEnv(const std::vector<std::string>& flag_names, + std::vector<ArgsList>* input_args, + bool fail_on_absent_in_env) { + bool success = true; + std::vector<std::string> args; + + // This argument represents fake argv[0], which should be present in all arg + // lists. + args.push_back(""); + + for (const auto& flag_name : flag_names) { + // Avoid infinite recursion. + if (flag_name == "fromenv" || flag_name == "tryfromenv") { + flags_internal::ReportUsageError( + absl::StrCat("Infinite recursion on flag ", flag_name), true); + + success = false; + continue; + } + + const std::string envname = absl::StrCat("FLAGS_", flag_name); + std::string envval; + if (!GetEnvVar(envname.c_str(), &envval)) { + if (fail_on_absent_in_env) { + flags_internal::ReportUsageError( + absl::StrCat(envname, " not found in environment"), true); + + success = false; + } + + continue; + } + + args.push_back(absl::StrCat("--", flag_name, "=", envval)); + } + + if (success) { + input_args->emplace_back(args); + } + + return success; +} + +// -------------------------------------------------------------------- + +// Returns success status, which is true if were able to handle all generator +// flags (flagfile, fromenv, tryfromemv) successfully. +bool HandleGeneratorFlags(std::vector<ArgsList>* input_args, + std::vector<std::string>* flagfile_value) { + bool success = true; + + absl::MutexLock l(&flags_internal::processing_checks_guard); + + // flagfile could have been set either on a command line or + // programmatically before invoking ParseCommandLine. Note that we do not + // actually process arguments specified in the flagfile, but instead + // create a secondary arguments list to be processed along with the rest + // of the comamnd line arguments. Since we always the process most recently + // created list of arguments first, this will result in flagfile argument + // being processed before any other argument in the command line. If + // FLAGS_flagfile contains more than one file name we create multiple new + // levels of arguments in a reverse order of file names. Thus we always + // process arguments from first file before arguments containing in a + // second file, etc. If flagfile contains another + // --flagfile inside of it, it will produce new level of arguments and + // processed before the rest of the flagfile. We are also collecting all + // flagfiles set on original command line. Unlike the rest of the flags, + // this flag can be set multiple times and is expected to be handled + // multiple times. We are collecting them all into a single list and set + // the value of FLAGS_flagfile to that value at the end of the parsing. + if (flags_internal::flagfile_needs_processing) { + auto flagfiles = absl::GetFlag(FLAGS_flagfile); + + if (input_args->size() == 1) { + flagfile_value->insert(flagfile_value->end(), flagfiles.begin(), + flagfiles.end()); + } + + success &= ReadFlagfiles(flagfiles, input_args); + + flags_internal::flagfile_needs_processing = false; + } + + // Similar to flagfile fromenv/tryfromemv can be set both + // programmatically and at runtime on a command line. Unlike flagfile these + // can't be recursive. + if (flags_internal::fromenv_needs_processing) { + auto flags_list = absl::GetFlag(FLAGS_fromenv); + + success &= ReadFlagsFromEnv(flags_list, input_args, true); + + flags_internal::fromenv_needs_processing = false; + } + + if (flags_internal::tryfromenv_needs_processing) { + auto flags_list = absl::GetFlag(FLAGS_tryfromenv); + + success &= ReadFlagsFromEnv(flags_list, input_args, false); + + flags_internal::tryfromenv_needs_processing = false; + } + + return success; +} + +// -------------------------------------------------------------------- + +void ResetGeneratorFlags(const std::vector<std::string>& flagfile_value) { + // Setting flagfile to the value which collates all the values set on a + // command line and programmatically. So if command line looked like + // --flagfile=f1 --flagfile=f2 the final value of the FLAGS_flagfile flag is + // going to be {"f1", "f2"} + if (!flagfile_value.empty()) { + absl::SetFlag(&FLAGS_flagfile, flagfile_value); + absl::MutexLock l(&flags_internal::processing_checks_guard); + flags_internal::flagfile_needs_processing = false; + } + + // fromenv/tryfromenv are set to <undefined> value. + if (!absl::GetFlag(FLAGS_fromenv).empty()) { + absl::SetFlag(&FLAGS_fromenv, {}); + } + if (!absl::GetFlag(FLAGS_tryfromenv).empty()) { + absl::SetFlag(&FLAGS_tryfromenv, {}); + } + + absl::MutexLock l(&flags_internal::processing_checks_guard); + flags_internal::fromenv_needs_processing = false; + flags_internal::tryfromenv_needs_processing = false; +} + +// -------------------------------------------------------------------- + +// Returns: +// success status +// deduced value +// We are also mutating curr_list in case if we need to get a hold of next +// argument in the input. +std::tuple<bool, absl::string_view> DeduceFlagValue(const CommandLineFlag& flag, + absl::string_view value, + bool is_negative, + bool is_empty_value, + ArgsList* curr_list) { + // Value is either an argument suffix after `=` in "--foo=<value>" + // or separate argument in case of "--foo" "<value>". + + // boolean flags have these forms: + // --foo + // --nofoo + // --foo=true + // --foo=false + // --nofoo=<value> is not supported + // --foo <value> is not supported + + // non boolean flags have these forms: + // --foo=<value> + // --foo <value> + // --nofoo is not supported + + if (flag.IsOfType<bool>()) { + if (value.empty()) { + if (is_empty_value) { + // "--bool_flag=" case + flags_internal::ReportUsageError( + absl::StrCat( + "Missing the value after assignment for the boolean flag '", + flag.Name(), "'"), + true); + return std::make_tuple(false, ""); + } + + // "--bool_flag" case + value = is_negative ? "0" : "1"; + } else if (is_negative) { + // "--nobool_flag=Y" case + flags_internal::ReportUsageError( + absl::StrCat("Negative form with assignment is not valid for the " + "boolean flag '", + flag.Name(), "'"), + true); + return std::make_tuple(false, ""); + } + } else if (is_negative) { + // "--noint_flag=1" case + flags_internal::ReportUsageError( + absl::StrCat("Negative form is not valid for the flag '", flag.Name(), + "'"), + true); + return std::make_tuple(false, ""); + } else if (value.empty() && (!is_empty_value)) { + if (curr_list->Size() == 1) { + // "--int_flag" case + flags_internal::ReportUsageError( + absl::StrCat("Missing the value for the flag '", flag.Name(), "'"), + true); + return std::make_tuple(false, ""); + } + + // "--int_flag" "10" case + curr_list->PopFront(); + value = curr_list->Front(); + + // Heuristic to detect the case where someone treats a std::string arg + // like a bool or just forgets to pass a value: + // --my_string_var --foo=bar + // We look for a flag of std::string type, whose value begins with a + // dash and corresponds to known flag or standalone --. + if (value[0] == '-' && flag.IsOfType<std::string>()) { + auto maybe_flag_name = std::get<0>(SplitNameAndValue(value.substr(1))); + + if (maybe_flag_name.empty() || + std::get<0>(LocateFlag(maybe_flag_name)) != nullptr) { + // "--string_flag" "--known_flag" case + ABSL_INTERNAL_LOG( + WARNING, + absl::StrCat("Did you really mean to set flag '", flag.Name(), + "' to the value '", value, "'?")); + } + } + } + + return std::make_tuple(true, value); +} + +// -------------------------------------------------------------------- + +bool CanIgnoreUndefinedFlag(absl::string_view flag_name) { + auto undefok = absl::GetFlag(FLAGS_undefok); + if (std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) { + return true; + } + + if (absl::ConsumePrefix(&flag_name, "no") && + std::find(undefok.begin(), undefok.end(), flag_name) != undefok.end()) { + return true; + } + + return false; +} + +} // namespace + +// -------------------------------------------------------------------- + +std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], + ArgvListAction arg_list_act, + UsageFlagsAction usage_flag_act, + OnUndefinedFlag on_undef_flag) { + ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]"); + + // This routine does not return anything since we abort on failure. + CheckDefaultValuesParsingRoundtrip(); + + std::vector<std::string> flagfile_value; + + std::vector<ArgsList> input_args; + input_args.push_back(ArgsList(argc, argv)); + + std::vector<char*> output_args; + std::vector<char*> positional_args; + output_args.reserve(argc); + + // This is the list of undefined flags. The element of the list is the pair + // consisting of boolean indicating if flag came from command line (vs from + // some flag file we've read) and flag name. + // TODO(rogeeff): Eliminate the first element in the pair after cleanup. + std::vector<std::pair<bool, std::string>> undefined_flag_names; + + // Set program invocation name if it is not set before. + if (ProgramInvocationName() == "UNKNOWN") { + flags_internal::SetProgramInvocationName(argv[0]); + } + output_args.push_back(argv[0]); + + // Iterate through the list of the input arguments. First level are arguments + // originated from argc/argv. Following levels are arguments originated from + // recursive parsing of flagfile(s). + bool success = true; + while (!input_args.empty()) { + // 10. First we process the built-in generator flags. + success &= HandleGeneratorFlags(&input_args, &flagfile_value); + + // 30. Select top-most (most recent) arguments list. If it is empty drop it + // and re-try. + ArgsList& curr_list = input_args.back(); + + curr_list.PopFront(); + + if (curr_list.Size() == 0) { + input_args.pop_back(); + continue; + } + + // 40. Pick up the front remaining argument in the current list. If current + // stack of argument lists contains only one element - we are processing an + // argument from the original argv. + absl::string_view arg(curr_list.Front()); + bool arg_from_argv = input_args.size() == 1; + + // 50. If argument does not start with - or is just "-" - this is + // positional argument. + if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) { + ABSL_INTERNAL_CHECK(arg_from_argv, + "Flagfile cannot contain positional argument"); + + positional_args.push_back(argv[curr_list.FrontIndex()]); + continue; + } + + if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs)) { + output_args.push_back(argv[curr_list.FrontIndex()]); + } + + // 60. Split the current argument on '=' to figure out the argument + // name and value. If flag name is empty it means we've got "--". value + // can be empty either if there were no '=' in argument std::string at all or + // an argument looked like "--foo=". In a latter case is_empty_value is + // true. + absl::string_view flag_name; + absl::string_view value; + bool is_empty_value = false; + + std::tie(flag_name, value, is_empty_value) = SplitNameAndValue(arg); + + // 70. "--" alone means what it does for GNU: stop flags parsing. We do + // not support positional arguments in flagfiles, so we just drop them. + if (flag_name.empty()) { + ABSL_INTERNAL_CHECK(arg_from_argv, + "Flagfile cannot contain positional argument"); + + curr_list.PopFront(); + break; + } + + // 80. Locate the flag based on flag name. Handle both --foo and --nofoo + CommandLineFlag* flag = nullptr; + bool is_negative = false; + std::tie(flag, is_negative) = LocateFlag(flag_name); + + if (flag == nullptr) { + if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) { + undefined_flag_names.emplace_back(arg_from_argv, + std::string(flag_name)); + } + continue; + } + + // 90. Deduce flag's value (from this or next argument) + auto curr_index = curr_list.FrontIndex(); + bool value_success = true; + std::tie(value_success, value) = + DeduceFlagValue(*flag, value, is_negative, is_empty_value, &curr_list); + success &= value_success; + + // If above call consumed an argument, it was a standalone value + if (arg_from_argv && (arg_list_act == ArgvListAction::kKeepParsedArgs) && + (curr_index != curr_list.FrontIndex())) { + output_args.push_back(argv[curr_list.FrontIndex()]); + } + + // 100. Set the located flag to a new new value, unless it is retired. + // Setting retired flag fails, but we ignoring it here. + if (flag->IsRetired()) continue; + + std::string error; + if (!flag->SetFromString(value, SET_FLAGS_VALUE, kCommandLine, &error)) { + flags_internal::ReportUsageError(error, true); + success = false; + } + } + + for (const auto& flag_name : undefined_flag_names) { + if (CanIgnoreUndefinedFlag(flag_name.second)) continue; + + flags_internal::ReportUsageError( + absl::StrCat("Unknown command line flag '", flag_name.second, "'"), + true); + + success = false; + } + +#if ABSL_FLAGS_STRIP_NAMES + if (!success) { + flags_internal::ReportUsageError( + "NOTE: command line flags are disabled in this build", true); + } +#endif + + if (!success) { + flags_internal::HandleUsageFlags(std::cout, + ProgramUsageMessage()); + std::exit(1); + } + + if (usage_flag_act == UsageFlagsAction::kHandleUsage) { + int exit_code = flags_internal::HandleUsageFlags( + std::cout, ProgramUsageMessage()); + + if (exit_code != -1) { + std::exit(exit_code); + } + } + + ResetGeneratorFlags(flagfile_value); + + // Reinstate positional args which were intermixed with flags in the arguments + // list. + for (auto arg : positional_args) { + output_args.push_back(arg); + } + + // All the remaining arguments are positional. + if (!input_args.empty()) { + for (int arg_index = input_args.back().FrontIndex(); arg_index < argc; + ++arg_index) { + output_args.push_back(argv[arg_index]); + } + } + + return output_args; +} + +} // namespace flags_internal + +// -------------------------------------------------------------------- + +std::vector<char*> ParseCommandLine(int argc, char* argv[]) { + return flags_internal::ParseCommandLineImpl( + argc, argv, flags_internal::ArgvListAction::kRemoveParsedArgs, + flags_internal::UsageFlagsAction::kHandleUsage, + flags_internal::OnUndefinedFlag::kAbortIfUndefined); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/parse.h b/absl/flags/parse.h new file mode 100644 index 00000000..469bd506 --- /dev/null +++ b/absl/flags/parse.h @@ -0,0 +1,60 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: parse.h +// ----------------------------------------------------------------------------- +// +// This file defines the main parsing function for Abseil flags: +// `absl::ParseCommandLine()`. + +#ifndef ABSL_FLAGS_PARSE_H_ +#define ABSL_FLAGS_PARSE_H_ + +#include <string> +#include <vector> + +#include "absl/flags/internal/parse.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// ParseCommandLine() +// +// Parses the set of command-line arguments passed in the `argc` (argument +// count) and `argv[]` (argument vector) parameters from `main()`, assigning +// values to any defined Abseil flags. (Any arguments passed after the +// flag-terminating delimiter (`--`) are treated as positional arguments and +// ignored.) +// +// Any command-line flags (and arguments to those flags) are parsed into Abseil +// Flag values, if those flags are defined. Any undefined flags will either +// return an error, or be ignored if that flag is designated using `undefok` to +// indicate "undefined is OK." +// +// Any command-line positional arguments not part of any command-line flag (or +// arguments to a flag) are returned in a vector, with the program invocation +// name at position 0 of that vector. (Note that this includes positional +// arguments after the flag-terminating delimiter `--`.) +// +// After all flags and flag arguments are parsed, this function looks for any +// built-in usage flags (e.g. `--help`), and if any were specified, it reports +// help messages and then exits the program. +std::vector<char*> ParseCommandLine(int argc, char* argv[]); + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_PARSE_H_ diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc new file mode 100644 index 00000000..447a3bc7 --- /dev/null +++ b/absl/flags/parse_test.cc @@ -0,0 +1,858 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/parse.h" + +#include <fstream> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/scoped_set_env.h" +#include "absl/flags/flag.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/substitute.h" +#include "absl/types/span.h" + +#ifdef _WIN32 +#include <windows.h> +#endif + +namespace { + +using absl::base_internal::ScopedSetEnv; + +struct UDT { + UDT() = default; + UDT(const UDT&) = default; + UDT(int v) : value(v) {} // NOLINT + + int value; +}; + +bool AbslParseFlag(absl::string_view in, UDT* udt, std::string* err) { + if (in == "A") { + udt->value = 1; + return true; + } + if (in == "AAA") { + udt->value = 10; + return true; + } + + *err = "Use values A, AAA instead"; + return false; +} +std::string AbslUnparseFlag(const UDT& udt) { + return udt.value == 1 ? "A" : "AAA"; +} + +std::string GetTestTmpDirEnvVar(const char* const env_var_name) { +#ifdef _WIN32 + char buf[MAX_PATH]; + auto get_res = GetEnvironmentVariableA(env_var_name, buf, sizeof(buf)); + if (get_res >= sizeof(buf) || get_res == 0) { + return ""; + } + + return std::string(buf, get_res); +#else + const char* val = ::getenv(env_var_name); + if (val == nullptr) { + return ""; + } + + return val; +#endif +} + +const std::string& GetTestTempDir() { + static std::string* temp_dir_name = []() -> std::string* { + std::string* res = new std::string(GetTestTmpDirEnvVar("TEST_TMPDIR")); + + if (res->empty()) { + *res = GetTestTmpDirEnvVar("TMPDIR"); + } + + if (res->empty()) { +#ifdef _WIN32 + char temp_path_buffer[MAX_PATH]; + + auto len = GetTempPathA(MAX_PATH, temp_path_buffer); + if (len < MAX_PATH && len != 0) { + std::string temp_dir_name = absl::StrCat( + temp_path_buffer, "\\parse_test.", GetCurrentProcessId()); + if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) { + *res = temp_dir_name; + } + } +#else + char temp_dir_template[] = "/tmp/parse_test.XXXXXX"; + if (auto* unique_name = ::mkdtemp(temp_dir_template)) { + *res = unique_name; + } +#endif + + if (res->empty()) { + ABSL_INTERNAL_LOG(FATAL, + "Failed to make temporary directory for data files"); + } + } + +#ifdef _WIN32 + *res += "\\"; +#else + *res += "/"; +#endif + + return res; + }(); + + return *temp_dir_name; +} + +struct FlagfileData { + const absl::string_view file_name; + const absl::Span<const char* const> file_lines; +}; + +// clang-format off +constexpr const char* const ff1_data[] = { + "# comment ", + " # comment ", + "", + " ", + "--int_flag=-1", + " --string_flag=q2w2 ", + " ## ", + " --double_flag=0.1", + "--bool_flag=Y " +}; + +constexpr const char* const ff2_data[] = { + "# Setting legacy flag", + "--legacy_int=1111", + "--legacy_bool", + "--nobool_flag", + "--legacy_str=aqsw", + "--int_flag=100", + " ## =============" +}; +// clang-format on + +// Builds flagfile flag in the flagfile_flag buffer and returns it. This +// function also creates a temporary flagfile based on FlagfileData input. +// We create a flagfile in a temporary directory with the name specified in +// FlagfileData and populate it with lines specifed in FlagfileData. If $0 is +// referenced in any of the lines in FlagfileData they are replaced with +// temporary directory location. This way we can test inclusion of one flagfile +// from another flagfile. +const char* GetFlagfileFlag(const std::vector<FlagfileData>& ffd, + std::string* flagfile_flag) { + *flagfile_flag = "--flagfile="; + absl::string_view separator; + for (const auto& flagfile_data : ffd) { + std::string flagfile_name = + absl::StrCat(GetTestTempDir(), flagfile_data.file_name); + + std::ofstream flagfile_out(flagfile_name); + for (auto line : flagfile_data.file_lines) { + flagfile_out << absl::Substitute(line, GetTestTempDir()) << "\n"; + } + + absl::StrAppend(flagfile_flag, separator, flagfile_name); + separator = ","; + } + + return flagfile_flag->c_str(); +} + +} // namespace + +ABSL_FLAG(int, int_flag, 1, ""); +ABSL_FLAG(double, double_flag, 1.1, ""); +ABSL_FLAG(std::string, string_flag, "a", ""); +ABSL_FLAG(bool, bool_flag, false, ""); +ABSL_FLAG(UDT, udt_flag, -1, ""); +ABSL_RETIRED_FLAG(int, legacy_int, 1, ""); +ABSL_RETIRED_FLAG(bool, legacy_bool, false, ""); +ABSL_RETIRED_FLAG(std::string, legacy_str, "l", ""); + +namespace { + +namespace flags = absl::flags_internal; +using testing::ElementsAreArray; + +class ParseTest : public testing::Test { + private: + flags::FlagSaver flag_saver_; +}; + +// -------------------------------------------------------------------- + +template <int N> +std::vector<char*> InvokeParse(const char* (&in_argv)[N]) { + return absl::ParseCommandLine(N, const_cast<char**>(in_argv)); +} + +// -------------------------------------------------------------------- + +template <int N> +void TestParse(const char* (&in_argv)[N], int int_flag_value, + double double_flag_val, absl::string_view string_flag_val, + bool bool_flag_val, int exp_position_args = 0) { + auto out_args = InvokeParse(in_argv); + + EXPECT_EQ(out_args.size(), 1 + exp_position_args); + EXPECT_STREQ(out_args[0], "testbin"); + + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), int_flag_value); + EXPECT_NEAR(absl::GetFlag(FLAGS_double_flag), double_flag_val, 0.0001); + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), string_flag_val); + EXPECT_EQ(absl::GetFlag(FLAGS_bool_flag), bool_flag_val); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestEmptyArgv) { + const char* in_argv[] = {"testbin"}; + + auto out_args = InvokeParse(in_argv); + + EXPECT_EQ(out_args.size(), 1); + EXPECT_STREQ(out_args[0], "testbin"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidIntArg) { + const char* in_args1[] = { + "testbin", + "--int_flag=10", + }; + TestParse(in_args1, 10, 1.1, "a", false); + + const char* in_args2[] = { + "testbin", + "-int_flag=020", + }; + TestParse(in_args2, 20, 1.1, "a", false); + + const char* in_args3[] = { + "testbin", + "--int_flag", + "-30", + }; + TestParse(in_args3, -30, 1.1, "a", false); + + const char* in_args4[] = { + "testbin", + "-int_flag", + "0x21", + }; + TestParse(in_args4, 33, 1.1, "a", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidDoubleArg) { + const char* in_args1[] = { + "testbin", + "--double_flag=2.3", + }; + TestParse(in_args1, 1, 2.3, "a", false); + + const char* in_args2[] = { + "testbin", + "--double_flag=0x1.2", + }; + TestParse(in_args2, 1, 1.125, "a", false); + + const char* in_args3[] = { + "testbin", + "--double_flag", + "99.7", + }; + TestParse(in_args3, 1, 99.7, "a", false); + + const char* in_args4[] = { + "testbin", + "--double_flag", + "0x20.1", + }; + TestParse(in_args4, 1, 32.0625, "a", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidStringArg) { + const char* in_args1[] = { + "testbin", + "--string_flag=aqswde", + }; + TestParse(in_args1, 1, 1.1, "aqswde", false); + + const char* in_args2[] = { + "testbin", + "-string_flag=a=b=c", + }; + TestParse(in_args2, 1, 1.1, "a=b=c", false); + + const char* in_args3[] = { + "testbin", + "--string_flag", + "zaxscd", + }; + TestParse(in_args3, 1, 1.1, "zaxscd", false); + + const char* in_args4[] = { + "testbin", + "-string_flag", + "--int_flag", + }; + TestParse(in_args4, 1, 1.1, "--int_flag", false); + + const char* in_args5[] = { + "testbin", + "--string_flag", + "--no_a_flag=11", + }; + TestParse(in_args5, 1, 1.1, "--no_a_flag=11", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidBoolArg) { + const char* in_args1[] = { + "testbin", + "--bool_flag", + }; + TestParse(in_args1, 1, 1.1, "a", true); + + const char* in_args2[] = { + "testbin", + "--nobool_flag", + }; + TestParse(in_args2, 1, 1.1, "a", false); + + const char* in_args3[] = { + "testbin", + "--bool_flag=true", + }; + TestParse(in_args3, 1, 1.1, "a", true); + + const char* in_args4[] = { + "testbin", + "-bool_flag=false", + }; + TestParse(in_args4, 1, 1.1, "a", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidUDTArg) { + const char* in_args1[] = { + "testbin", + "--udt_flag=A", + }; + InvokeParse(in_args1); + + EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 1); + + const char* in_args2[] = {"testbin", "--udt_flag", "AAA"}; + InvokeParse(in_args2); + + EXPECT_EQ(absl::GetFlag(FLAGS_udt_flag).value, 10); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidMultipleArg) { + const char* in_args1[] = { + "testbin", "--bool_flag", "--int_flag=2", + "--double_flag=0.1", "--string_flag=asd", + }; + TestParse(in_args1, 2, 0.1, "asd", true); + + const char* in_args2[] = { + "testbin", "--string_flag=", "--nobool_flag", "--int_flag", + "-011", "--double_flag", "-1e-2", + }; + TestParse(in_args2, -11, -0.01, "", false); + + const char* in_args3[] = { + "testbin", "--int_flag", "-0", "--string_flag", "\"\"", + "--bool_flag=true", "--double_flag=1e18", + }; + TestParse(in_args3, 0, 1e18, "\"\"", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestPositionalArgs) { + const char* in_args1[] = { + "testbin", + "p1", + "p2", + }; + TestParse(in_args1, 1, 1.1, "a", false, 2); + + auto out_args1 = InvokeParse(in_args1); + + EXPECT_STREQ(out_args1[1], "p1"); + EXPECT_STREQ(out_args1[2], "p2"); + + const char* in_args2[] = { + "testbin", + "--int_flag=2", + "p1", + }; + TestParse(in_args2, 2, 1.1, "a", false, 1); + + auto out_args2 = InvokeParse(in_args2); + + EXPECT_STREQ(out_args2[1], "p1"); + + const char* in_args3[] = {"testbin", "p1", "--int_flag=3", + "p2", "--bool_flag", "true"}; + TestParse(in_args3, 3, 1.1, "a", true, 3); + + auto out_args3 = InvokeParse(in_args3); + + EXPECT_STREQ(out_args3[1], "p1"); + EXPECT_STREQ(out_args3[2], "p2"); + EXPECT_STREQ(out_args3[3], "true"); + + const char* in_args4[] = { + "testbin", + "--", + "p1", + "p2", + }; + TestParse(in_args4, 3, 1.1, "a", true, 2); + + auto out_args4 = InvokeParse(in_args4); + + EXPECT_STREQ(out_args4[1], "p1"); + EXPECT_STREQ(out_args4[2], "p2"); + + const char* in_args5[] = { + "testbin", "p1", "--int_flag=4", "--", "--bool_flag", "false", "p2", + }; + TestParse(in_args5, 4, 1.1, "a", true, 4); + + auto out_args5 = InvokeParse(in_args5); + + EXPECT_STREQ(out_args5[1], "p1"); + EXPECT_STREQ(out_args5[2], "--bool_flag"); + EXPECT_STREQ(out_args5[3], "false"); + EXPECT_STREQ(out_args5[4], "p2"); +} + +// -------------------------------------------------------------------- + +using ParseDeathTest = ParseTest; + +TEST_F(ParseDeathTest, TestUndefinedArg) { + const char* in_args1[] = { + "testbin", + "--undefined_flag", + }; + EXPECT_DEATH(InvokeParse(in_args1), + "Unknown command line flag 'undefined_flag'"); + + const char* in_args2[] = { + "testbin", + "--noprefixed_flag", + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Unknown command line flag 'noprefixed_flag'"); + + const char* in_args3[] = { + "testbin", + "--Int_flag=1", + }; + EXPECT_DEATH(InvokeParse(in_args3), "Unknown command line flag 'Int_flag'"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestInvalidBoolFlagFormat) { + const char* in_args1[] = { + "testbin", + "--bool_flag=", + }; + EXPECT_DEATH( + InvokeParse(in_args1), + "Missing the value after assignment for the boolean flag 'bool_flag'"); + + const char* in_args2[] = { + "testbin", + "--nobool_flag=true", + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Negative form with assignment is not valid for the boolean " + "flag 'bool_flag'"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestInvalidNonBoolFlagFormat) { + const char* in_args1[] = { + "testbin", + "--nostring_flag", + }; + EXPECT_DEATH(InvokeParse(in_args1), + "Negative form is not valid for the flag 'string_flag'"); + + const char* in_args2[] = { + "testbin", + "--int_flag", + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Missing the value for the flag 'int_flag'"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestInvalidUDTFlagFormat) { + const char* in_args1[] = { + "testbin", + "--udt_flag=1", + }; + EXPECT_DEATH(InvokeParse(in_args1), + "Illegal value '1' specified for flag 'udt_flag'; Use values A, " + "AAA instead"); + + const char* in_args2[] = { + "testbin", + "--udt_flag", + "AA", + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Illegal value 'AA' specified for flag 'udt_flag'; Use values " + "A, AAA instead"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestLegacyFlags) { + const char* in_args1[] = { + "testbin", + "--legacy_int=11", + }; + TestParse(in_args1, 1, 1.1, "a", false); + + const char* in_args2[] = { + "testbin", + "--legacy_bool", + }; + TestParse(in_args2, 1, 1.1, "a", false); + + const char* in_args3[] = { + "testbin", "--legacy_int", "22", "--int_flag=2", + "--legacy_bool", "true", "--legacy_str", "--string_flag=qwe", + }; + TestParse(in_args3, 2, 1.1, "a", false, 1); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestSimpleValidFlagfile) { + std::string flagfile_flag; + + const char* in_args1[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, + &flagfile_flag), + }; + TestParse(in_args1, -1, 0.1, "q2w2 ", true); + + const char* in_args2[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}}, + &flagfile_flag), + }; + TestParse(in_args2, 100, 0.1, "q2w2 ", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestValidMultiFlagfile) { + std::string flagfile_flag; + + const char* in_args1[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff2", absl::MakeConstSpan(ff2_data)}, + {"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, + &flagfile_flag), + }; + TestParse(in_args1, -1, 0.1, "q2w2 ", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestFlagfileMixedWithRegularFlags) { + std::string flagfile_flag; + + const char* in_args1[] = { + "testbin", "--int_flag=3", + GetFlagfileFlag({{"parse_test.ff1", absl::MakeConstSpan(ff1_data)}}, + &flagfile_flag), + "-double_flag=0.2"}; + TestParse(in_args1, -1, 0.2, "q2w2 ", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestFlagfileInFlagfile) { + std::string flagfile_flag; + + constexpr const char* const ff3_data[] = { + "--flagfile=$0/parse_test.ff1", + "--flagfile=$0/parse_test.ff2", + }; + + const char* in_args1[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff3", absl::MakeConstSpan(ff3_data)}}, + &flagfile_flag), + }; + TestParse(in_args1, 100, 0.1, "q2w2 ", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestInvalidFlagfiles) { + std::string flagfile_flag; + + constexpr const char* const ff4_data[] = { + "--unknown_flag=10" + }; + + const char* in_args1[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff4", + absl::MakeConstSpan(ff4_data)}}, &flagfile_flag), + }; + EXPECT_DEATH(InvokeParse(in_args1), + "Unknown command line flag 'unknown_flag'"); + + constexpr const char* const ff5_data[] = { + "--int_flag 10", + }; + + const char* in_args2[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff5", + absl::MakeConstSpan(ff5_data)}}, &flagfile_flag), + }; + EXPECT_DEATH(InvokeParse(in_args2), + "Unknown command line flag 'int_flag 10'"); + + constexpr const char* const ff6_data[] = { + "--int_flag=10", "--", "arg1", "arg2", "arg3", + }; + + const char* in_args3[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff6", absl::MakeConstSpan(ff6_data)}}, + &flagfile_flag), + }; + EXPECT_DEATH(InvokeParse(in_args3), + "Flagfile can't contain position arguments or --"); + + const char* in_args4[] = { + "testbin", + "--flagfile=invalid_flag_file", + }; + EXPECT_DEATH(InvokeParse(in_args4), "Can't open flagfile invalid_flag_file"); + + constexpr const char* const ff7_data[] = { + "--int_flag=10", + "*bin*", + "--str_flag=aqsw", + }; + + const char* in_args5[] = { + "testbin", + GetFlagfileFlag({{"parse_test.ff7", absl::MakeConstSpan(ff7_data)}}, + &flagfile_flag), + }; + EXPECT_DEATH(InvokeParse(in_args5), + "Unexpected line in the flagfile .*: \\*bin\\*"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestReadingRequiredFlagsFromEnv) { + const char* in_args1[] = {"testbin", + "--fromenv=int_flag,bool_flag,string_flag"}; + + ScopedSetEnv set_int_flag("FLAGS_int_flag", "33"); + ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "True"); + ScopedSetEnv set_string_flag("FLAGS_string_flag", "AQ12"); + + TestParse(in_args1, 33, 1.1, "AQ12", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestReadingUnsetRequiredFlagsFromEnv) { + const char* in_args1[] = {"testbin", "--fromenv=int_flag"}; + + EXPECT_DEATH(InvokeParse(in_args1), + "FLAGS_int_flag not found in environment"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestRecursiveFlagsFromEnv) { + const char* in_args1[] = {"testbin", "--fromenv=tryfromenv"}; + + ScopedSetEnv set_tryfromenv("FLAGS_tryfromenv", "int_flag"); + + EXPECT_DEATH(InvokeParse(in_args1), "Infinite recursion on flag tryfromenv"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestReadingOptionalFlagsFromEnv) { + const char* in_args1[] = { + "testbin", "--tryfromenv=int_flag,bool_flag,string_flag,other_flag"}; + + ScopedSetEnv set_int_flag("FLAGS_int_flag", "17"); + ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "Y"); + + TestParse(in_args1, 17, 1.1, "a", true); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) { + const char* in_args1[] = { + "testbin", + "--bool_flag=T", + "--tryfromenv=int_flag,bool_flag", + "--int_flag=-21", + }; + + ScopedSetEnv set_int_flag("FLAGS_int_flag", "-15"); + ScopedSetEnv set_bool_flag("FLAGS_bool_flag", "F"); + + TestParse(in_args1, -21, 1.1, "a", false); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestKeepParsedArgs) { + const char* in_args1[] = { + "testbin", "arg1", "--bool_flag", + "--int_flag=211", "arg2", "--double_flag=1.1", + "--string_flag", "asd", "--", + "arg3", "arg4", + }; + + auto out_args1 = InvokeParse(in_args1); + + EXPECT_THAT( + out_args1, + ElementsAreArray({absl::string_view("testbin"), absl::string_view("arg1"), + absl::string_view("arg2"), absl::string_view("arg3"), + absl::string_view("arg4")})); + + auto out_args2 = flags::ParseCommandLineImpl( + 11, const_cast<char**>(in_args1), flags::ArgvListAction::kKeepParsedArgs, + flags::UsageFlagsAction::kHandleUsage, + flags::OnUndefinedFlag::kAbortIfUndefined); + + EXPECT_THAT( + out_args2, + ElementsAreArray({absl::string_view("testbin"), + absl::string_view("--bool_flag"), + absl::string_view("--int_flag=211"), + absl::string_view("--double_flag=1.1"), + absl::string_view("--string_flag"), + absl::string_view("asd"), absl::string_view("--"), + absl::string_view("arg1"), absl::string_view("arg2"), + absl::string_view("arg3"), absl::string_view("arg4")})); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, TestIgnoreUndefinedFlags) { + const char* in_args1[] = { + "testbin", + "arg1", + "--undef_flag=aa", + "--int_flag=21", + }; + + auto out_args1 = flags::ParseCommandLineImpl( + 4, const_cast<char**>(in_args1), flags::ArgvListAction::kRemoveParsedArgs, + flags::UsageFlagsAction::kHandleUsage, + flags::OnUndefinedFlag::kIgnoreUndefined); + + EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"), + absl::string_view("arg1")})); + + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21); + + const char* in_args2[] = { + "testbin", + "arg1", + "--undef_flag=aa", + "--string_flag=AA", + }; + + auto out_args2 = flags::ParseCommandLineImpl( + 4, const_cast<char**>(in_args2), flags::ArgvListAction::kKeepParsedArgs, + flags::UsageFlagsAction::kHandleUsage, + flags::OnUndefinedFlag::kIgnoreUndefined); + + EXPECT_THAT( + out_args2, + ElementsAreArray( + {absl::string_view("testbin"), absl::string_view("--undef_flag=aa"), + absl::string_view("--string_flag=AA"), absl::string_view("arg1")})); + + EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "AA"); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseDeathTest, TestHelpFlagHandling) { + const char* in_args1[] = { + "testbin", + "--help", + }; + + EXPECT_EXIT(InvokeParse(in_args1), testing::ExitedWithCode(1), ""); + + const char* in_args2[] = { + "testbin", + "--help", + "--int_flag=3", + }; + + auto out_args2 = flags::ParseCommandLineImpl( + 3, const_cast<char**>(in_args2), flags::ArgvListAction::kRemoveParsedArgs, + flags::UsageFlagsAction::kIgnoreUsage, + flags::OnUndefinedFlag::kAbortIfUndefined); + + EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3); +} + +} // namespace diff --git a/absl/flags/usage.cc b/absl/flags/usage.cc new file mode 100644 index 00000000..12c346b7 --- /dev/null +++ b/absl/flags/usage.cc @@ -0,0 +1,58 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "absl/flags/usage.h" + +#include <string> + +#include "absl/flags/internal/usage.h" +#include "absl/synchronization/mutex.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { +namespace { +ABSL_CONST_INIT absl::Mutex usage_message_guard(absl::kConstInit); +ABSL_CONST_INIT std::string* program_usage_message + GUARDED_BY(usage_message_guard) = nullptr; +} // namespace +} // namespace flags_internal + +// -------------------------------------------------------------------- +// Sets the "usage" message to be used by help reporting routines. +void SetProgramUsageMessage(absl::string_view new_usage_message) { + absl::MutexLock l(&flags_internal::usage_message_guard); + + if (flags_internal::program_usage_message != nullptr) { + ABSL_INTERNAL_LOG(FATAL, "SetProgramUsageMessage() called twice."); + std::exit(1); + } + + flags_internal::program_usage_message = new std::string(new_usage_message); +} + +// -------------------------------------------------------------------- +// Returns the usage message set by SetProgramUsageMessage(). +// Note: We able to return string_view here only because calling +// SetProgramUsageMessage twice is prohibited. +absl::string_view ProgramUsageMessage() { + absl::MutexLock l(&flags_internal::usage_message_guard); + + return flags_internal::program_usage_message != nullptr + ? absl::string_view(*flags_internal::program_usage_message) + : "Warning: SetProgramUsageMessage() never called"; +} + +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/usage.h b/absl/flags/usage.h new file mode 100644 index 00000000..c232a7d0 --- /dev/null +++ b/absl/flags/usage.h @@ -0,0 +1,42 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_USAGE_H_ +#define ABSL_FLAGS_USAGE_H_ + +#include "absl/strings/string_view.h" + +// -------------------------------------------------------------------- +// Usage reporting interfaces + +namespace absl { +inline namespace lts_2019_08_08 { + +// Sets the "usage" message to be used by help reporting routines. +// For example: +// absl::SetProgramUsageMessage( +// absl::StrCat("This program does nothing. Sample usage:\n", argv[0], +// " <uselessarg1> <uselessarg2>")); +// Do not include commandline flags in the usage: we do that for you! +// Note: Calling SetProgramUsageMessage twice will trigger a call to std::exit. +void SetProgramUsageMessage(absl::string_view new_usage_message); + +// Returns the usage message set by SetProgramUsageMessage(). +absl::string_view ProgramUsageMessage(); + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_FLAGS_USAGE_H_ diff --git a/absl/flags/usage_config.cc b/absl/flags/usage_config.cc new file mode 100644 index 00000000..a538acd5 --- /dev/null +++ b/absl/flags/usage_config.cc @@ -0,0 +1,154 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/usage_config.h" + +#include <iostream> +#include <memory> + +#include "absl/base/attributes.h" +#include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/program_name.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/strip.h" +#include "absl/synchronization/mutex.h" + +extern "C" { + +// Additional report of fatal usage error message before we std::exit. Error is +// fatal if is_fatal argument to ReportUsageError is true. +ABSL_ATTRIBUTE_WEAK void AbslInternalReportFatalUsageError(absl::string_view) {} + +} // extern "C" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace flags_internal { + +namespace { + +// -------------------------------------------------------------------- +// Returns true if flags defined in the filename should be reported with +// -helpshort flag. + +bool ContainsHelpshortFlags(absl::string_view filename) { + // By default we only want flags in binary's main. We expect the main + // routine to reside in <program>.cc or <program>-main.cc or + // <program>_main.cc, where the <program> is the name of the binary. + auto suffix = flags_internal::Basename(filename); + if (!absl::ConsumePrefix(&suffix, + flags_internal::ShortProgramInvocationName())) + return false; + return absl::StartsWith(suffix, ".") || absl::StartsWith(suffix, "-main.") || + absl::StartsWith(suffix, "_main."); +} + +// -------------------------------------------------------------------- +// Returns true if flags defined in the filename should be reported with +// -helppackage flag. + +bool ContainsHelppackageFlags(absl::string_view filename) { + // TODO(rogeeff): implement properly when registry is available. + return ContainsHelpshortFlags(filename); +} + +// -------------------------------------------------------------------- +// Generates program version information into supplied output. + +std::string VersionString() { + std::string version_str(flags_internal::ShortProgramInvocationName()); + + version_str += "\n"; + +#if !defined(NDEBUG) + version_str += "Debug build (NDEBUG not #defined)\n"; +#endif + + return version_str; +} + +// -------------------------------------------------------------------- +// Normalizes the filename specific to the build system/filesystem used. + +std::string NormalizeFilename(absl::string_view filename) { + // Skip any leading slashes + auto pos = filename.find_first_not_of("\\/"); + if (pos == absl::string_view::npos) return ""; + + filename.remove_prefix(pos); + return std::string(filename); +} + +// -------------------------------------------------------------------- + +ABSL_CONST_INIT absl::Mutex custom_usage_config_guard(absl::kConstInit); +ABSL_CONST_INIT FlagsUsageConfig* custom_usage_config + GUARDED_BY(custom_usage_config_guard) = nullptr; + +} // namespace + +FlagsUsageConfig GetUsageConfig() { + absl::MutexLock l(&custom_usage_config_guard); + + if (custom_usage_config) return *custom_usage_config; + + FlagsUsageConfig default_config; + default_config.contains_helpshort_flags = &ContainsHelpshortFlags; + default_config.contains_help_flags = &ContainsHelppackageFlags; + default_config.contains_helppackage_flags = &ContainsHelppackageFlags; + default_config.version_string = &VersionString; + default_config.normalize_filename = &NormalizeFilename; + + return default_config; +} + +void ReportUsageError(absl::string_view msg, bool is_fatal) { + std::cerr << "ERROR: " << msg << std::endl; + + if (is_fatal) { + AbslInternalReportFatalUsageError(msg); + } +} + +} // namespace flags_internal + +void SetFlagsUsageConfig(FlagsUsageConfig usage_config) { + absl::MutexLock l(&flags_internal::custom_usage_config_guard); + + if (!usage_config.contains_helpshort_flags) + usage_config.contains_helpshort_flags = + flags_internal::ContainsHelpshortFlags; + + if (!usage_config.contains_help_flags) + usage_config.contains_help_flags = flags_internal::ContainsHelppackageFlags; + + if (!usage_config.contains_helppackage_flags) + usage_config.contains_helppackage_flags = + flags_internal::ContainsHelppackageFlags; + + if (!usage_config.version_string) + usage_config.version_string = flags_internal::VersionString; + + if (!usage_config.normalize_filename) + usage_config.normalize_filename = flags_internal::NormalizeFilename; + + if (flags_internal::custom_usage_config) + *flags_internal::custom_usage_config = usage_config; + else + flags_internal::custom_usage_config = new FlagsUsageConfig(usage_config); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/flags/usage_config.h b/absl/flags/usage_config.h new file mode 100644 index 00000000..c6d34e4a --- /dev/null +++ b/absl/flags/usage_config.h @@ -0,0 +1,133 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: usage_config.h +// ----------------------------------------------------------------------------- +// +// This file defines the main usage reporting configuration interfaces and +// documents Abseil's supported built-in usage flags. If these flags are found +// when parsing a command-line, Abseil will exit the program and display +// appropriate help messages. +#ifndef ABSL_FLAGS_USAGE_CONFIG_H_ +#define ABSL_FLAGS_USAGE_CONFIG_H_ + +#include <functional> +#include <string> + +#include "absl/strings/string_view.h" + +// ----------------------------------------------------------------------------- +// Built-in Usage Flags +// ----------------------------------------------------------------------------- +// +// Abseil supports the following built-in usage flags. When passed, these flags +// exit the program and : +// +// * --help +// Shows help on important flags for this binary +// * --helpfull +// Shows help on all flags +// * --helpshort +// Shows help on only the main module for this program +// * --helppackage +// Shows help on all modules in the main package +// * --version +// Shows the version and build info for this binary and exits +// * --only_check_args +// Exits after checking all flags +// * --helpon +// Shows help on the modules named by this flag value +// * --helpmatch +// Shows help on modules whose name contains the specified substring + +namespace absl { +inline namespace lts_2019_08_08 { + +namespace flags_internal { +using FlagKindFilter = std::function<bool (absl::string_view)>; +} // namespace flags_internal + +// FlagsUsageConfig +// +// This structure contains the collection of callbacks for changing the behavior +// of the usage reporting routines in Abseil Flags. +struct FlagsUsageConfig { + // Returns true if flags defined in the given source code file should be + // reported with --helpshort flag. For example, if the file + // "path/to/my/code.cc" defines the flag "--my_flag", and + // contains_helpshort_flags("path/to/my/code.cc") returns true, invoking the + // program with --helpshort will include information about --my_flag in the + // program output. + flags_internal::FlagKindFilter contains_helpshort_flags; + + // Returns true if flags defined in the filename should be reported with + // --help flag. For example, if the file + // "path/to/my/code.cc" defines the flag "--my_flag", and + // contains_help_flags("path/to/my/code.cc") returns true, invoking the + // program with --help will include information about --my_flag in the + // program output. + flags_internal::FlagKindFilter contains_help_flags; + + // Returns true if flags defined in the filename should be reported with + // --helppackage flag. For example, if the file + // "path/to/my/code.cc" defines the flag "--my_flag", and + // contains_helppackage_flags("path/to/my/code.cc") returns true, invoking the + // program with --helppackage will include information about --my_flag in the + // program output. + flags_internal::FlagKindFilter contains_helppackage_flags; + + // Generates std::string containing program version. This is the std::string reported + // when user specifies --version in a command line. + std::function<std::string()> version_string; + + // Normalizes the filename specific to the build system/filesystem used. This + // routine is used when we report the information about the flag definition + // location. For instance, if your build resides at some location you do not + // want to expose in the usage output, you can trim it to show only relevant + // part. + // For example: + // normalize_filename("/my_company/some_long_path/src/project/file.cc") + // might produce + // "project/file.cc". + std::function<std::string (absl::string_view)> normalize_filename; +}; + +// SetFlagsUsageConfig() +// +// Sets the usage reporting configuration callbacks. If any of the callbacks are +// not set in usage_config instance, then the default value of the callback is +// used. +void SetFlagsUsageConfig(FlagsUsageConfig usage_config); + +namespace flags_internal { + +FlagsUsageConfig GetUsageConfig(); + +void ReportUsageError(absl::string_view msg, bool is_fatal); + +} // namespace flags_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +extern "C" { + +// Additional report of fatal usage error message before we std::exit. Error is +// fatal if is_fatal argument to ReportUsageError is true. +void AbslInternalReportFatalUsageError(absl::string_view); + +} // extern "C" + +#endif // ABSL_FLAGS_USAGE_CONFIG_H_ diff --git a/absl/flags/usage_config_test.cc b/absl/flags/usage_config_test.cc new file mode 100644 index 00000000..3bde13af --- /dev/null +++ b/absl/flags/usage_config_test.cc @@ -0,0 +1,198 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/usage_config.h" + +#include "gtest/gtest.h" +#include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/program_name.h" +#include "absl/strings/match.h" + +namespace { + +class FlagsUsageConfigTest : public testing::Test { + protected: + void SetUp() override { + // Install Default config for the use on this unit test. + // Binary may install a custom config before tests are run. + absl::FlagsUsageConfig default_config; + absl::SetFlagsUsageConfig(default_config); + } +}; + +namespace flags = absl::flags_internal; + +bool TstContainsHelpshortFlags(absl::string_view f) { + return absl::StartsWith(flags::Basename(f), "progname."); +} + +bool TstContainsHelppackageFlags(absl::string_view f) { + return absl::EndsWith(flags::Package(f), "aaa/"); +} + +bool TstContainsHelpFlags(absl::string_view f) { + return absl::EndsWith(flags::Package(f), "zzz/"); +} + +std::string TstVersionString() { return "program 1.0.0"; } + +std::string TstNormalizeFilename(absl::string_view filename) { + return std::string(filename.substr(2)); +} + +void TstReportUsageMessage(absl::string_view msg) {} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestGetSetFlagsUsageConfig) { + EXPECT_TRUE(flags::GetUsageConfig().contains_helpshort_flags); + EXPECT_TRUE(flags::GetUsageConfig().contains_help_flags); + EXPECT_TRUE(flags::GetUsageConfig().contains_helppackage_flags); + EXPECT_TRUE(flags::GetUsageConfig().version_string); + EXPECT_TRUE(flags::GetUsageConfig().normalize_filename); + + absl::FlagsUsageConfig empty_config; + empty_config.contains_helpshort_flags = &TstContainsHelpshortFlags; + empty_config.contains_help_flags = &TstContainsHelpFlags; + empty_config.contains_helppackage_flags = &TstContainsHelppackageFlags; + empty_config.version_string = &TstVersionString; + empty_config.normalize_filename = &TstNormalizeFilename; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_TRUE(flags::GetUsageConfig().contains_helpshort_flags); + EXPECT_TRUE(flags::GetUsageConfig().contains_help_flags); + EXPECT_TRUE(flags::GetUsageConfig().contains_helppackage_flags); + EXPECT_TRUE(flags::GetUsageConfig().version_string); + EXPECT_TRUE(flags::GetUsageConfig().normalize_filename); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestContainsHelpshortFlags) { + flags::SetProgramInvocationName("usage_config_test"); + + auto config = flags::GetUsageConfig(); + EXPECT_TRUE(config.contains_helpshort_flags("adir/cd/usage_config_test.cc")); + EXPECT_TRUE( + config.contains_helpshort_flags("aaaa/usage_config_test-main.cc")); + EXPECT_TRUE(config.contains_helpshort_flags("abc/usage_config_test_main.cc")); + EXPECT_FALSE(config.contains_helpshort_flags("usage_config_main.cc")); + + absl::FlagsUsageConfig empty_config; + empty_config.contains_helpshort_flags = &TstContainsHelpshortFlags; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_TRUE( + flags::GetUsageConfig().contains_helpshort_flags("aaa/progname.cpp")); + EXPECT_FALSE( + flags::GetUsageConfig().contains_helpshort_flags("aaa/progmane.cpp")); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestContainsHelpFlags) { + flags::SetProgramInvocationName("usage_config_test"); + + auto config = flags::GetUsageConfig(); + EXPECT_TRUE(config.contains_help_flags("zzz/usage_config_test.cc")); + EXPECT_TRUE( + config.contains_help_flags("bdir/a/zzz/usage_config_test-main.cc")); + EXPECT_TRUE( + config.contains_help_flags("//aqse/zzz/usage_config_test_main.cc")); + EXPECT_FALSE(config.contains_help_flags("zzz/aa/usage_config_main.cc")); + + absl::FlagsUsageConfig empty_config; + empty_config.contains_help_flags = &TstContainsHelpFlags; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_TRUE(flags::GetUsageConfig().contains_help_flags("zzz/main-body.c")); + EXPECT_FALSE( + flags::GetUsageConfig().contains_help_flags("zzz/dir/main-body.c")); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestContainsHelppackageFlags) { + flags::SetProgramInvocationName("usage_config_test"); + + auto config = flags::GetUsageConfig(); + EXPECT_TRUE(config.contains_helppackage_flags("aaa/usage_config_test.cc")); + EXPECT_TRUE( + config.contains_helppackage_flags("bbdir/aaa/usage_config_test-main.cc")); + EXPECT_TRUE(config.contains_helppackage_flags( + "//aqswde/aaa/usage_config_test_main.cc")); + EXPECT_FALSE(config.contains_helppackage_flags("aadir/usage_config_main.cc")); + + absl::FlagsUsageConfig empty_config; + empty_config.contains_helppackage_flags = &TstContainsHelppackageFlags; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_TRUE( + flags::GetUsageConfig().contains_helppackage_flags("aaa/main-body.c")); + EXPECT_FALSE( + flags::GetUsageConfig().contains_helppackage_flags("aadir/main-body.c")); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestVersionString) { + flags::SetProgramInvocationName("usage_config_test"); + +#ifdef NDEBUG + std::string expected_output = "usage_config_test\n"; +#else + std::string expected_output = + "usage_config_test\nDebug build (NDEBUG not #defined)\n"; +#endif + + EXPECT_EQ(flags::GetUsageConfig().version_string(), expected_output); + + absl::FlagsUsageConfig empty_config; + empty_config.version_string = &TstVersionString; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_EQ(flags::GetUsageConfig().version_string(), "program 1.0.0"); +} + +// -------------------------------------------------------------------- + +TEST_F(FlagsUsageConfigTest, TestNormalizeFilename) { + // This tests the default implementation. + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("/a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("///a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("/"), ""); + + // This tests that the custom implementation is called. + absl::FlagsUsageConfig empty_config; + empty_config.normalize_filename = &TstNormalizeFilename; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("a/a.cc"), "a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("aaa/a.cc"), "a/a.cc"); + + // This tests that the default implementation is called. + empty_config.normalize_filename = nullptr; + absl::SetFlagsUsageConfig(empty_config); + + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("/a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("///a/a.cc"), "a/a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("\\a\\a.cc"), "a\\a.cc"); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("//"), ""); + EXPECT_EQ(flags::GetUsageConfig().normalize_filename("\\\\"), ""); +} + +} // namespace diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index 4f7c94ce..8c2daf70 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,9 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -32,6 +33,7 @@ cc_library( ], hdrs = ["hash.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":city", "//absl/base:core_headers", @@ -50,6 +52,7 @@ cc_library( name = "hash_testing", testonly = 1, hdrs = ["hash_testing.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":spy_hash_state", "//absl/meta:type_traits", @@ -63,6 +66,7 @@ cc_test( name = "hash_test", srcs = ["hash_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":hash", ":hash_testing", @@ -80,6 +84,7 @@ cc_library( testonly = 1, hdrs = ["internal/spy_hash_state.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ ":hash", @@ -95,6 +100,7 @@ cc_library( "internal/city.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", "//absl/base:core_headers", @@ -106,6 +112,7 @@ cc_test( name = "city_test", srcs = ["internal/city_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":city", "@com_google_googletest//:gtest_main", diff --git a/absl/hash/CMakeLists.txt b/absl/hash/CMakeLists.txt index 8f97d7cc..febc551f 100644 --- a/absl/hash/CMakeLists.txt +++ b/absl/hash/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -43,6 +43,8 @@ absl_cc_library( hash_testing HDRS "hash_testing.h" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::spy_hash_state absl::meta @@ -56,7 +58,9 @@ absl_cc_test( NAME hash_test SRCS - "hash_test.cc" + "hash_test.cc" + COPTS + ${ABSL_TEST_COPTS} DEPS absl::hash absl::hash_testing diff --git a/absl/hash/hash.h b/absl/hash/hash.h index 2c8982b8..d36f0960 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -36,25 +36,34 @@ // framework by simply combining its state with the state of known, hashable // types. Hashing of that combined state is separately done by `absl::Hash`. // +// One should assume that a hash algorithm is chosen randomly at the start of +// each process. E.g., absl::Hash<int>()(9) in one process and +// absl::Hash<int>()(9) in another process are likely to differ. +// // Example: // -// // Suppose we have a class `Circle` for which we want to add hashing +// // Suppose we have a class `Circle` for which we want to add hashing: // class Circle { -// public: -// ... -// private: -// std::pair<int, int> center_; -// int radius_; -// }; -// -// // To add hashing support to `Circle`, we simply need to add an ordinary -// // function `AbslHashValue()`, and return the combined hash state of the -// // existing hash state and the class state: +// public: +// ... +// private: +// std::pair<int, int> center_; +// int radius_; +// }; // +// // To add hashing support to `Circle`, we simply need to add a free +// // (non-member) function `AbslHashValue()`, and return the combined hash +// // state of the existing hash state and the class state. You can add such a +// // free function using a friend declaration within the body of the class: +// class Circle { +// public: +// ... // template <typename H> // friend H AbslHashValue(H h, const Circle& c) { // return H::combine(std::move(h), c.center_, c.radius_); // } +// ... +// }; // // For more information, see Adding Type Support to `absl::Hash` below. // @@ -64,7 +73,7 @@ #include "absl/hash/internal/hash.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // `absl::Hash` @@ -236,7 +245,7 @@ using Hash = absl::hash_internal::Hash<T>; // } // private: // virtual void HashValue(absl::HashState state) const = 0; -// }; +// }; // // class Impl : Interface { // private: @@ -244,7 +253,7 @@ using Hash = absl::hash_internal::Hash<T>; // absl::HashState::combine(std::move(state), v1_, v2_); // } // int v1_; -// string v2_; +// std::string v2_; // }; class HashState : public hash_internal::HashStateBase<HashState> { public: @@ -309,6 +318,7 @@ class HashState : public hash_internal::HashStateBase<HashState> { void (*combine_contiguous_)(void*, const unsigned char*, size_t); }; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_HASH_HASH_H_ diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 4a1a98d5..2a4e2262 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,6 +15,7 @@ #include "absl/hash/hash.h" #include <array> +#include <bitset> #include <cstring> #include <deque> #include <forward_list> @@ -50,7 +51,7 @@ using absl::hash_internal::SpyHashState; template <typename T> class HashValueIntTest : public testing::Test { }; -TYPED_TEST_CASE_P(HashValueIntTest); +TYPED_TEST_SUITE_P(HashValueIntTest); template <typename T> SpyHashState SpyHash(const T& value) { @@ -84,11 +85,349 @@ using IntTypes = testing::Types<unsigned char, char, int, int32_t, int64_t, uint uint64_t, size_t>; INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueIntTest, IntTypes); +enum LegacyEnum { kValue1, kValue2, kValue3 }; + +enum class EnumClass { kValue4, kValue5, kValue6 }; + +TEST(HashValueTest, EnumAndBool) { + EXPECT_TRUE((is_hashable<LegacyEnum>::value)); + EXPECT_TRUE((is_hashable<EnumClass>::value)); + EXPECT_TRUE((is_hashable<bool>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + LegacyEnum::kValue1, LegacyEnum::kValue2, LegacyEnum::kValue3))); + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + EnumClass::kValue4, EnumClass::kValue5, EnumClass::kValue6))); + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(true, false))); +} + +TEST(HashValueTest, FloatingPoint) { + EXPECT_TRUE((is_hashable<float>::value)); + EXPECT_TRUE((is_hashable<double>::value)); + EXPECT_TRUE((is_hashable<long double>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(42.f, 0.f, -0.f, std::numeric_limits<float>::infinity(), + -std::numeric_limits<float>::infinity()))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(42., 0., -0., std::numeric_limits<double>::infinity(), + -std::numeric_limits<double>::infinity()))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + // Add some values with small exponent to test that NORMAL values also + // append their category. + .5L, 1.L, 2.L, 4.L, 42.L, 0.L, -0.L, + 17 * static_cast<long double>(std::numeric_limits<double>::max()), + std::numeric_limits<long double>::infinity(), + -std::numeric_limits<long double>::infinity()))); +} + +TEST(HashValueTest, Pointer) { + EXPECT_TRUE((is_hashable<int*>::value)); + + int i; + int* ptr = &i; + int* n = nullptr; + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(&i, ptr, nullptr, ptr + 1, n))); +} + +TEST(HashValueTest, PointerAlignment) { + // We want to make sure that pointer alignment will not cause bits to be + // stuck. + + constexpr size_t kTotalSize = 1 << 20; + std::unique_ptr<char[]> data(new char[kTotalSize]); + constexpr size_t kLog2NumValues = 5; + constexpr size_t kNumValues = 1 << kLog2NumValues; + + for (size_t align = 1; align < kTotalSize / kNumValues; + align < 8 ? align += 1 : align < 1024 ? align += 8 : align += 32) { + SCOPED_TRACE(align); + ASSERT_LE(align * kNumValues, kTotalSize); + + size_t bits_or = 0; + size_t bits_and = ~size_t{}; + + for (size_t i = 0; i < kNumValues; ++i) { + size_t hash = absl::Hash<void*>()(data.get() + i * align); + bits_or |= hash; + bits_and &= hash; + } + + // Limit the scope to the bits we would be using for Swisstable. + constexpr size_t kMask = (1 << (kLog2NumValues + 7)) - 1; + size_t stuck_bits = (~bits_or | bits_and) & kMask; + EXPECT_EQ(stuck_bits, 0) << "0x" << std::hex << stuck_bits; + } +} + +TEST(HashValueTest, PairAndTuple) { + EXPECT_TRUE((is_hashable<std::pair<int, int>>::value)); + EXPECT_TRUE((is_hashable<std::pair<const int&, const int&>>::value)); + EXPECT_TRUE((is_hashable<std::tuple<int&, int&>>::value)); + EXPECT_TRUE((is_hashable<std::tuple<int&&, int&&>>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::make_pair(0, 42), std::make_pair(0, 42), std::make_pair(42, 0), + std::make_pair(0, 0), std::make_pair(42, 42), std::make_pair(1, 42)))); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(std::make_tuple(0, 0, 0), std::make_tuple(0, 0, 42), + std::make_tuple(0, 23, 0), std::make_tuple(17, 0, 0), + std::make_tuple(42, 0, 0), std::make_tuple(3, 9, 9), + std::make_tuple(0, 0, -42)))); + + // Test that tuples of lvalue references work (so we need a few lvalues): + int a = 0, b = 1, c = 17, d = 23; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::tie(a, a), std::tie(a, b), std::tie(b, c), std::tie(c, d)))); + + // Test that tuples of rvalue references work: + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::forward_as_tuple(0, 0, 0), std::forward_as_tuple(0, 0, 42), + std::forward_as_tuple(0, 23, 0), std::forward_as_tuple(17, 0, 0), + std::forward_as_tuple(42, 0, 0), std::forward_as_tuple(3, 9, 9), + std::forward_as_tuple(0, 0, -42)))); +} + +TEST(HashValueTest, CombineContiguousWorks) { + std::vector<std::tuple<int>> v1 = {std::make_tuple(1), std::make_tuple(3)}; + std::vector<std::tuple<int>> v2 = {std::make_tuple(1), std::make_tuple(2)}; + + auto vh1 = SpyHash(v1); + auto vh2 = SpyHash(v2); + EXPECT_NE(vh1, vh2); +} + +struct DummyDeleter { + template <typename T> + void operator() (T* ptr) {} +}; + +struct SmartPointerEq { + template <typename T, typename U> + bool operator()(const T& t, const U& u) const { + return GetPtr(t) == GetPtr(u); + } + + template <typename T> + static auto GetPtr(const T& t) -> decltype(&*t) { + return t ? &*t : nullptr; + } + + static std::nullptr_t GetPtr(std::nullptr_t) { return nullptr; } +}; + +TEST(HashValueTest, SmartPointers) { + EXPECT_TRUE((is_hashable<std::unique_ptr<int>>::value)); + EXPECT_TRUE((is_hashable<std::unique_ptr<int, DummyDeleter>>::value)); + EXPECT_TRUE((is_hashable<std::shared_ptr<int>>::value)); + + int i, j; + std::unique_ptr<int, DummyDeleter> unique1(&i); + std::unique_ptr<int, DummyDeleter> unique2(&i); + std::unique_ptr<int, DummyDeleter> unique_other(&j); + std::unique_ptr<int, DummyDeleter> unique_null; + + std::shared_ptr<int> shared1(&i, DummyDeleter()); + std::shared_ptr<int> shared2(&i, DummyDeleter()); + std::shared_ptr<int> shared_other(&j, DummyDeleter()); + std::shared_ptr<int> shared_null; + + // Sanity check of the Eq function. + ASSERT_TRUE(SmartPointerEq{}(unique1, shared1)); + ASSERT_FALSE(SmartPointerEq{}(unique1, shared_other)); + ASSERT_TRUE(SmartPointerEq{}(unique_null, nullptr)); + ASSERT_FALSE(SmartPointerEq{}(shared2, nullptr)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::forward_as_tuple(&i, nullptr, // + unique1, unique2, unique_null, // + absl::make_unique<int>(), // + shared1, shared2, shared_null, // + std::make_shared<int>()), + SmartPointerEq{})); +} + +TEST(HashValueTest, FunctionPointer) { + using Func = int (*)(); + EXPECT_TRUE(is_hashable<Func>::value); + + Func p1 = [] { return 2; }, p2 = [] { return 1; }; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(p1, p2, nullptr))); +} + +struct WrapInTuple { + template <typename T> + std::tuple<int, T, size_t> operator()(const T& t) const { + return std::make_tuple(7, t, 0xdeadbeef); + } +}; + +TEST(HashValueTest, Strings) { + EXPECT_TRUE((is_hashable<std::string>::value)); + + const std::string small = "foo"; + const std::string dup = "foofoo"; + const std::string large = "large"; + const std::string huge = std::string(5000, 'a'); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::string(), absl::string_view(), + std::string(""), absl::string_view(""), + std::string(small), absl::string_view(small), + std::string(dup), absl::string_view(dup), + std::string(large), absl::string_view(large), + std::string(huge), absl::string_view(huge)))); + + // Also check that nested types maintain the same hash. + const WrapInTuple t{}; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + t(std::string()), t(absl::string_view()), + t(std::string("")), t(absl::string_view("")), + t(std::string(small)), t(absl::string_view(small)), + t(std::string(dup)), t(absl::string_view(dup)), + t(std::string(large)), t(absl::string_view(large)), + t(std::string(huge)), t(absl::string_view(huge))))); + + // Make sure that hashing a `const char*` does not use its std::string-value. + EXPECT_NE(SpyHash(static_cast<const char*>("ABC")), + SpyHash(absl::string_view("ABC"))); +} + +TEST(HashValueTest, StdArray) { + EXPECT_TRUE((is_hashable<std::array<int, 3>>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(std::array<int, 3>{}, std::array<int, 3>{{0, 23, 42}}))); +} + +TEST(HashValueTest, StdBitset) { + EXPECT_TRUE((is_hashable<std::bitset<257>>::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + {std::bitset<2>("00"), std::bitset<2>("01"), std::bitset<2>("10"), + std::bitset<2>("11")})); + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + {std::bitset<5>("10101"), std::bitset<5>("10001"), std::bitset<5>()})); + + constexpr int kNumBits = 256; + std::array<std::string, 6> bit_strings; + bit_strings.fill(std::string(kNumBits, '1')); + bit_strings[1][0] = '0'; + bit_strings[2][1] = '0'; + bit_strings[3][kNumBits / 3] = '0'; + bit_strings[4][kNumBits - 2] = '0'; + bit_strings[5][kNumBits - 1] = '0'; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + {std::bitset<kNumBits>(bit_strings[0].c_str()), + std::bitset<kNumBits>(bit_strings[1].c_str()), + std::bitset<kNumBits>(bit_strings[2].c_str()), + std::bitset<kNumBits>(bit_strings[3].c_str()), + std::bitset<kNumBits>(bit_strings[4].c_str()), + std::bitset<kNumBits>(bit_strings[5].c_str())})); +} // namespace + +template <typename T> +class HashValueSequenceTest : public testing::Test { +}; +TYPED_TEST_SUITE_P(HashValueSequenceTest); + +TYPED_TEST_P(HashValueSequenceTest, BasicUsage) { + EXPECT_TRUE((is_hashable<TypeParam>::value)); + + using ValueType = typename TypeParam::value_type; + auto a = static_cast<ValueType>(0); + auto b = static_cast<ValueType>(23); + auto c = static_cast<ValueType>(42); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(TypeParam(), TypeParam{}, TypeParam{a, b, c}, + TypeParam{a, b}, TypeParam{b, c}))); +} + +REGISTER_TYPED_TEST_CASE_P(HashValueSequenceTest, BasicUsage); +using IntSequenceTypes = + testing::Types<std::deque<int>, std::forward_list<int>, std::list<int>, + std::vector<int>, std::vector<bool>, std::set<int>, + std::multiset<int>>; +INSTANTIATE_TYPED_TEST_CASE_P(My, HashValueSequenceTest, IntSequenceTypes); + +// Private type that only supports AbslHashValue to make sure our chosen hash +// implentation is recursive within absl::Hash. +// It uses std::abs() on the value to provide different bitwise representations +// of the same logical value. +struct Private { + int i; + template <typename H> + friend H AbslHashValue(H h, Private p) { + return H::combine(std::move(h), std::abs(p.i)); + } + + friend bool operator==(Private a, Private b) { + return std::abs(a.i) == std::abs(b.i); + } + + friend std::ostream& operator<<(std::ostream& o, Private p) { + return o << p.i; + } +}; + +TEST(HashValueTest, PrivateSanity) { + // Sanity check that Private is working as the tests below expect it to work. + EXPECT_TRUE(is_hashable<Private>::value); + EXPECT_NE(SpyHash(Private{0}), SpyHash(Private{1})); + EXPECT_EQ(SpyHash(Private{1}), SpyHash(Private{1})); +} + +TEST(HashValueTest, Optional) { + EXPECT_TRUE(is_hashable<absl::optional<Private>>::value); + + using O = absl::optional<Private>; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + std::make_tuple(O{}, O{{1}}, O{{-1}}, O{{10}}))); +} + +TEST(HashValueTest, Variant) { + using V = absl::variant<Private, std::string>; + EXPECT_TRUE(is_hashable<V>::value); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + V(Private{1}), V(Private{-1}), V(Private{2}), V("ABC"), V("BCD")))); + +#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ + struct S {}; + EXPECT_FALSE(is_hashable<absl::variant<S>>::value); +#endif +} + +TEST(HashValueTest, Maps) { + EXPECT_TRUE((is_hashable<std::map<int, std::string>>::value)); + + using M = std::map<int, std::string>; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + M{}, M{{0, "foo"}}, M{{1, "foo"}}, M{{0, "bar"}}, M{{1, "bar"}}, + M{{0, "foo"}, {42, "bar"}}, M{{1, "foo"}, {42, "bar"}}, + M{{1, "foo"}, {43, "bar"}}, M{{1, "foo"}, {43, "baz"}}))); + + using MM = std::multimap<int, std::string>; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + MM{}, MM{{0, "foo"}}, MM{{1, "foo"}}, MM{{0, "bar"}}, MM{{1, "bar"}}, + MM{{0, "foo"}, {0, "bar"}}, MM{{0, "bar"}, {0, "foo"}}, + MM{{0, "foo"}, {42, "bar"}}, MM{{1, "foo"}, {42, "bar"}}, + MM{{1, "foo"}, {1, "foo"}, {43, "bar"}}, MM{{1, "foo"}, {43, "baz"}}))); +} + template <typename T, typename = void> -struct IsHashCallble : std::false_type {}; +struct IsHashCallable : std::false_type {}; template <typename T> -struct IsHashCallble<T, absl::void_t<decltype(std::declval<absl::Hash<T>>()( +struct IsHashCallable<T, absl::void_t<decltype(std::declval<absl::Hash<T>>()( std::declval<const T&>()))>> : std::true_type {}; template <typename T, typename = void> @@ -105,10 +444,11 @@ TEST(IsHashableTest, ValidHash) { EXPECT_TRUE(std::is_move_constructible<absl::Hash<int>>::value); EXPECT_TRUE(absl::is_copy_assignable<absl::Hash<int>>::value); EXPECT_TRUE(absl::is_move_assignable<absl::Hash<int>>::value); - EXPECT_TRUE(IsHashCallble<int>::value); + EXPECT_TRUE(IsHashCallable<int>::value); EXPECT_TRUE(IsAggregateInitializable<absl::Hash<int>>::value); } -#if ABSL_HASH_INTERNAL_CAN_POISON_ && !defined(__APPLE__) + +#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ TEST(IsHashableTest, PoisonHash) { struct X {}; EXPECT_FALSE((is_hashable<X>::value)); @@ -117,10 +457,10 @@ TEST(IsHashableTest, PoisonHash) { EXPECT_FALSE(std::is_move_constructible<absl::Hash<X>>::value); EXPECT_FALSE(absl::is_copy_assignable<absl::Hash<X>>::value); EXPECT_FALSE(absl::is_move_assignable<absl::Hash<X>>::value); - EXPECT_FALSE(IsHashCallble<X>::value); + EXPECT_FALSE(IsHashCallable<X>::value); EXPECT_FALSE(IsAggregateInitializable<absl::Hash<X>>::value); } -#endif // ABSL_HASH_INTERNAL_CAN_POISON_ +#endif // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ // Hashable types // @@ -129,7 +469,7 @@ TEST(IsHashableTest, PoisonHash) { struct NoOp { template <typename HashCode> friend HashCode AbslHashValue(HashCode h, NoOp n) { - return std::move(h); + return h; } }; @@ -159,8 +499,16 @@ struct CombineVariadic { Int(4)); } }; +enum class InvokeTag { + kUniquelyRepresented, + kHashValue, +#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ + kLegacyHash, +#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ + kStdHash, + kNone +}; -using InvokeTag = absl::hash_internal::InvokeHashTag; template <InvokeTag T> using InvokeTagConstant = std::integral_constant<InvokeTag, T>; @@ -175,6 +523,7 @@ struct MinTag<a> : InvokeTagConstant<a> {}; template <InvokeTag... Tags> struct CustomHashType { + explicit CustomHashType(size_t val) : value(val) {} size_t value; }; @@ -195,7 +544,7 @@ H AbslHashValue(H state, CustomHashType<Tags...> t) { } // namespace namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { template <InvokeTag... Tags> struct is_uniquely_represented< @@ -203,7 +552,7 @@ struct is_uniquely_represented< typename EnableIfContained<InvokeTag::kUniquelyRepresented, Tags...>::type> : std::true_type {}; } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ @@ -243,17 +592,17 @@ void TestCustomHashType(InvokeTagConstant<InvokeTag::kNone>, T...) { EXPECT_TRUE(is_hashable<const type&>()); const size_t offset = static_cast<int>(std::min({T::value...})); - EXPECT_EQ(SpyHash(type{7}), SpyHash(size_t{7 + offset})); + EXPECT_EQ(SpyHash(type(7)), SpyHash(size_t{7 + offset})); } void TestCustomHashType(InvokeTagConstant<InvokeTag::kNone>) { -#if ABSL_HASH_INTERNAL_CAN_POISON_ +#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ // is_hashable is false if we don't support any of the hooks. using type = CustomHashType<>; EXPECT_FALSE(is_hashable<type>()); EXPECT_FALSE(is_hashable<const type>()); EXPECT_FALSE(is_hashable<const type&>()); -#endif // ABSL_HASH_INTERNAL_CAN_POISON_ +#endif // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ } template <InvokeTag Tag, typename... T> @@ -275,7 +624,7 @@ TEST(HashTest, NoOpsAreEquivalent) { template <typename T> class HashIntTest : public testing::Test { }; -TYPED_TEST_CASE_P(HashIntTest); +TYPED_TEST_SUITE_P(HashIntTest); TYPED_TEST_P(HashIntTest, BasicUsage) { EXPECT_NE(Hash<NoOp>()({}), Hash<TypeParam>()(0)); @@ -354,7 +703,8 @@ TEST(HashTest, HashNonUniquelyRepresentedType) { } TEST(HashTest, StandardHashContainerUsage) { - std::unordered_map<int, std::string, Hash<int>> map = {{0, "foo"}, { 42, "bar" }}; + std::unordered_map<int, std::string, Hash<int>> map = {{0, "foo"}, + {42, "bar"}}; EXPECT_NE(map.find(0), map.end()); EXPECT_EQ(map.find(1), map.end()); @@ -424,4 +774,24 @@ TEST(HashTest, TypeErased) { SpyHash(std::make_pair(size_t{7}, 17))); } +struct ValueWithBoolConversion { + operator bool() const { return false; } + int i; +}; + +} // namespace +namespace std { +template <> +struct hash<ValueWithBoolConversion> { + size_t operator()(ValueWithBoolConversion v) { return v.i; } +}; +} // namespace std + +namespace { + +TEST(HashTest, DoesNotUseImplicitConversionsToBool) { + EXPECT_NE(absl::Hash<ValueWithBoolConversion>()(ValueWithBoolConversion{0}), + absl::Hash<ValueWithBoolConversion>()(ValueWithBoolConversion{1})); +} + } // namespace diff --git a/absl/hash/hash_testing.h b/absl/hash/hash_testing.h index 1c4db26a..6e39028f 100644 --- a/absl/hash/hash_testing.h +++ b/absl/hash/hash_testing.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/types/variant.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Run the absl::Hash algorithm over all the elements passed in and verify that // their hash expansion is congruent with their `==` operator. @@ -191,7 +191,9 @@ VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) { struct Info { const V& value; size_t index; - std::string ToString() const { return absl::visit(PrintVisitor{index}, value); } + std::string ToString() const { + return absl::visit(PrintVisitor{index}, value); + } SpyHashState expand() const { return absl::visit(ExpandVisitor{}, value); } }; @@ -370,7 +372,7 @@ VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values, equals); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HASH_HASH_TESTING_H_ diff --git a/absl/hash/internal/city.cc b/absl/hash/internal/city.cc index 5c076fb5..c7ad1591 100644 --- a/absl/hash/internal/city.cc +++ b/absl/hash/internal/city.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,7 +30,7 @@ #include "absl/base/optimization.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { #ifdef ABSL_IS_BIG_ENDIAN @@ -342,5 +342,5 @@ uint64_t CityHash64WithSeeds(const char *s, size_t len, uint64_t seed0, } } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/hash/internal/city.h b/absl/hash/internal/city.h index 46c18ffa..7586ba08 100644 --- a/absl/hash/internal/city.h +++ b/absl/hash/internal/city.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// http://code.google.com/p/cityhash/ +// https://code.google.com/p/cityhash/ // // This file provides a few functions for hashing strings. All of them are // high-quality functions in the sense that they pass standard tests such @@ -49,9 +49,8 @@ #include <stdlib.h> // for size_t. #include <utility> - namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { typedef std::pair<uint64_t, uint64_t> uint128; @@ -88,7 +87,7 @@ inline uint64_t Hash128to64(const uint128 &x) { } } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HASH_INTERNAL_CITY_H_ diff --git a/absl/hash/internal/city_test.cc b/absl/hash/internal/city_test.cc index f305ed9e..1b9373c2 100644 --- a/absl/hash/internal/city_test.cc +++ b/absl/hash/internal/city_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; @@ -591,5 +591,5 @@ TEST(CityHashTest, Unchanging) { } } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc index 3e553625..087b389b 100644 --- a/absl/hash/internal/hash.cc +++ b/absl/hash/internal/hash.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,11 +15,11 @@ #include "absl/hash/internal/hash.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed; } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index a51ca954..81f1edf8 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -50,7 +50,7 @@ #include "absl/hash/internal/city.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { // HashStateBase @@ -222,7 +222,9 @@ typename std::enable_if<std::is_enum<Enum>::value, H>::type AbslHashValue( } // AbslHashValue() for hashing floating-point values template <typename H, typename Float> -typename std::enable_if<std::is_floating_point<Float>::value, H>::type +typename std::enable_if<std::is_same<Float, float>::value || + std::is_same<Float, double>::value, + H>::type AbslHashValue(H hash_state, Float value) { return hash_internal::hash_bytes(std::move(hash_state), value == 0 ? 0 : value); @@ -232,8 +234,9 @@ AbslHashValue(H hash_state, Float value) { // For example, in x86 sizeof(long double)==16 but it only really uses 80-bits // of it. This means we can't use hash_bytes on a long double and have to // convert it to something else first. -template <typename H> -H AbslHashValue(H hash_state, long double value) { +template <typename H, typename LongDouble> +typename std::enable_if<std::is_same<LongDouble, long double>::value, H>::type +AbslHashValue(H hash_state, LongDouble value) { const int category = std::fpclassify(value); switch (category) { case FP_INFINITE: @@ -265,7 +268,12 @@ H AbslHashValue(H hash_state, long double value) { // AbslHashValue() for hashing pointers template <typename H, typename T> H AbslHashValue(H hash_state, T* ptr) { - return hash_internal::hash_bytes(std::move(hash_state), ptr); + auto v = reinterpret_cast<uintptr_t>(ptr); + // Due to alignment, pointers tend to have low bits as zero, and the next few + // bits follow a pattern since they are also multiples of some base value. + // Mixing the pointer twice helps prevent stuck low bits for certain alignment + // values. + return H::combine(std::move(hash_state), v, v); } // AbslHashValue() for hashing nullptr_t @@ -528,53 +536,22 @@ hash_range_or_bytes(H hash_state, const T* data, size_t size) { return hash_state; } -// InvokeHashTag -// -// InvokeHash(H, const T&) invokes the appropriate hash implementation for a -// hasher of type `H` and a value of type `T`. If `T` is not hashable, there -// will be no matching overload of InvokeHash(). -// Note: Some platforms (eg MSVC) do not support the detect idiom on -// std::hash. In those platforms the last fallback will be std::hash and -// InvokeHash() will always have a valid overload even if std::hash<T> is not -// valid. -// -// We try the following options in order: -// * If is_uniquely_represented, hash bytes directly. -// * ADL AbslHashValue(H, const T&) call. -// * std::hash<T> - -// In MSVC we can't probe std::hash or stdext::hash because it triggers a -// static_assert instead of failing substitution. -#if defined(_MSC_VER) -#define ABSL_HASH_INTERNAL_CAN_POISON_ 0 -#else // _MSC_VER -#define ABSL_HASH_INTERNAL_CAN_POISON_ 1 -#endif // _MSC_VER - #if defined(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE) && \ - ABSL_HASH_INTERNAL_CAN_POISON_ + ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ #define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 1 #else #define ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ 0 #endif -enum class InvokeHashTag { - kUniquelyRepresented, - kHashValue, -#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ - kLegacyHash, -#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ - kStdHash, - kNone -}; - // HashSelect // // Type trait to select the appropriate hash implementation to use. -// HashSelect<T>::value is an instance of InvokeHashTag that indicates the best -// available hashing mechanism. -// See `Note` above about MSVC. -template <typename T> +// HashSelect::type<T> will give the proper hash implementation, to be invoked +// as: +// HashSelect::type<T>::Invoke(state, value) +// Also, HashSelect::type<T>::value is a boolean equal to `true` if there is a +// valid `Invoke` function. Types that are not hashable will have a ::value of +// `false`. struct HashSelect { private: struct State : HashStateBase<State> { @@ -583,95 +560,75 @@ struct HashSelect { using State::HashStateBase::combine_contiguous; }; - // `Probe<V, Tag>::value` evaluates to `V<T>::value` if it is a valid - // expression, and `false` otherwise. - // `Probe<V, Tag>::tag` always evaluates to `Tag`. - template <template <typename> class V, InvokeHashTag Tag> - struct Probe { - private: - template <typename U, typename std::enable_if<V<U>::value, int>::type = 0> - static std::true_type Test(int); - template <typename U> - static std::false_type Test(char); - - public: - static constexpr InvokeHashTag kTag = Tag; - static constexpr bool value = decltype( - Test<absl::remove_const_t<absl::remove_reference_t<T>>>(0))::value; + struct UniquelyRepresentedProbe { + template <typename H, typename T> + static auto Invoke(H state, const T& value) + -> absl::enable_if_t<is_uniquely_represented<T>::value, H> { + return hash_internal::hash_bytes(std::move(state), value); + } }; - template <typename U> - using ProbeUniquelyRepresented = is_uniquely_represented<U>; - - template <typename U> - using ProbeHashValue = - std::is_same<State, decltype(AbslHashValue(std::declval<State>(), - std::declval<const U&>()))>; + struct HashValueProbe { + template <typename H, typename T> + static auto Invoke(H state, const T& value) -> absl::enable_if_t< + std::is_same<H, + decltype(AbslHashValue(std::move(state), value))>::value, + H> { + return AbslHashValue(std::move(state), value); + } + }; + struct LegacyHashProbe { #if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ - template <typename U> - using ProbeLegacyHash = - std::is_convertible<decltype(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash< - U>()(std::declval<const U&>())), - size_t>; + template <typename H, typename T> + static auto Invoke(H state, const T& value) -> absl::enable_if_t< + std::is_convertible< + decltype(ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash<T>()(value)), + size_t>::value, + H> { + return hash_internal::hash_bytes( + std::move(state), + ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash<T>{}(value)); + } #endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ + }; - template <typename U> - using ProbeStdHash = -#if ABSL_HASH_INTERNAL_CAN_POISON_ - std::is_convertible<decltype(std::hash<U>()(std::declval<const U&>())), - size_t>; -#else // ABSL_HASH_INTERNAL_CAN_POISON_ - std::true_type; -#endif // ABSL_HASH_INTERNAL_CAN_POISON_ + struct StdHashProbe { + template <typename H, typename T> + static auto Invoke(H state, const T& value) + -> absl::enable_if_t<type_traits_internal::IsHashable<T>::value, H> { + return hash_internal::hash_bytes(std::move(state), std::hash<T>{}(value)); + } + }; - template <typename U> - using ProbeNone = std::true_type; + template <typename Hash, typename T> + struct Probe : Hash { + private: + template <typename H, typename = decltype(H::Invoke( + std::declval<State>(), std::declval<const T&>()))> + static std::true_type Test(int); + template <typename U> + static std::false_type Test(char); + + public: + static constexpr bool value = decltype(Test<Hash>(0))::value; + }; public: // Probe each implementation in order. - // disjunction provides short circuting wrt instantiation. - static constexpr InvokeHashTag value = absl::disjunction< - Probe<ProbeUniquelyRepresented, InvokeHashTag::kUniquelyRepresented>, - Probe<ProbeHashValue, InvokeHashTag::kHashValue>, -#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ - Probe<ProbeLegacyHash, InvokeHashTag::kLegacyHash>, -#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ - Probe<ProbeStdHash, InvokeHashTag::kStdHash>, - Probe<ProbeNone, InvokeHashTag::kNone>>::kTag; + // disjunction provides short circuiting wrt instantiation. + template <typename T> + using Apply = absl::disjunction< // + Probe<UniquelyRepresentedProbe, T>, // + Probe<HashValueProbe, T>, // + Probe<LegacyHashProbe, T>, // + Probe<StdHashProbe, T>, // + std::false_type>; }; template <typename T> -struct is_hashable : std::integral_constant<bool, HashSelect<T>::value != - InvokeHashTag::kNone> {}; - -template <typename H, typename T> -absl::enable_if_t<HashSelect<T>::value == InvokeHashTag::kUniquelyRepresented, - H> -InvokeHash(H state, const T& value) { - return hash_internal::hash_bytes(std::move(state), value); -} - -template <typename H, typename T> -absl::enable_if_t<HashSelect<T>::value == InvokeHashTag::kHashValue, H> -InvokeHash(H state, const T& value) { - return AbslHashValue(std::move(state), value); -} - -#if ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ -template <typename H, typename T> -absl::enable_if_t<HashSelect<T>::value == InvokeHashTag::kLegacyHash, H> -InvokeHash(H state, const T& value) { - return hash_internal::hash_bytes( - std::move(state), ABSL_INTERNAL_LEGACY_HASH_NAMESPACE::hash<T>{}(value)); -} -#endif // ABSL_HASH_INTERNAL_SUPPORT_LEGACY_HASH_ - -template <typename H, typename T> -absl::enable_if_t<HashSelect<T>::value == InvokeHashTag::kStdHash, H> -InvokeHash(H state, const T& value) { - return hash_internal::hash_bytes(std::move(state), std::hash<T>{}(value)); -} +struct is_hashable + : std::integral_constant<bool, HashSelect::template Apply<T>::value> {}; // CityHashState class CityHashState : public HashStateBase<CityHashState> { @@ -684,7 +641,8 @@ class CityHashState : public HashStateBase<CityHashState> { #endif // ABSL_HAVE_INTRINSIC_INT128 static constexpr uint64_t kMul = - sizeof(size_t) == 4 ? uint64_t{0xcc9e2d51} : uint64_t{0x9ddfea08eb382d69}; + sizeof(size_t) == 4 ? uint64_t{0xcc9e2d51} + : uint64_t{0x9ddfea08eb382d69}; template <typename T> using IntegralFastPath = @@ -881,7 +839,8 @@ struct Hash template <typename H> template <typename T, typename... Ts> H HashStateBase<H>::combine(H state, const T& value, const Ts&... values) { - return H::combine(hash_internal::InvokeHash(std::move(state), value), + return H::combine(hash_internal::HashSelect::template Apply<T>::Invoke( + std::move(state), value), values...); } @@ -892,7 +851,7 @@ H HashStateBase<H>::combine_contiguous(H state, const T* data, size_t size) { return hash_internal::hash_range_or_bytes(std::move(state), data, size); } } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HASH_INTERNAL_HASH_H_ diff --git a/absl/hash/internal/print_hash_of.cc b/absl/hash/internal/print_hash_of.cc index b6df31cc..c392125a 100644 --- a/absl/hash/internal/print_hash_of.cc +++ b/absl/hash/internal/print_hash_of.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/hash/internal/spy_hash_state.h b/absl/hash/internal/spy_hash_state.h index 1886d2ef..57cd70b2 100644 --- a/absl/hash/internal/spy_hash_state.h +++ b/absl/hash/internal/spy_hash_state.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ #include "absl/strings/str_join.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace hash_internal { // SpyHashState is an implementation of the HashState API that simply @@ -40,8 +40,7 @@ namespace hash_internal { template <typename T> class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> { public: - SpyHashStateImpl() - : error_(std::make_shared<absl::optional<std::string>>()) { + SpyHashStateImpl() : error_(std::make_shared<absl::optional<std::string>>()) { static_assert(std::is_void<T>::value, ""); } @@ -171,7 +170,6 @@ class SpyHashStateImpl : public HashStateBase<SpyHashStateImpl<T>> { // AbslHashValue directly (because the hash state type does not match). static bool direct_absl_hash_value_error_; - std::vector<std::string> hash_representation_; // This is a shared_ptr because we want all instances of the particular // SpyHashState run to share the field. This way we can set the error for @@ -201,7 +199,7 @@ bool RunOnStartup<f>::run = (f(), true); template < typename T, typename U, // Only trigger for when (T != U), - absl::enable_if_t<!std::is_same<T, U>::value, int> = 0, + typename = absl::enable_if_t<!std::is_same<T, U>::value>, // This statement works in two ways: // - First, it instantiates RunOnStartup and forces the initialization of // `run`, which set the global variable. @@ -214,7 +212,7 @@ void AbslHashValue(SpyHashStateImpl<T>, const U&); using SpyHashState = SpyHashStateImpl<void>; } // namespace hash_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HASH_INTERNAL_SPY_HASH_STATE_H_ diff --git a/absl/memory/BUILD.bazel b/absl/memory/BUILD.bazel index 89a312ea..f815ef94 100644 --- a/absl/memory/BUILD.bazel +++ b/absl/memory/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,9 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", "ABSL_EXCEPTIONS_FLAG", "ABSL_EXCEPTIONS_FLAG_LINKOPTS", @@ -30,6 +31,7 @@ cc_library( name = "memory", hdrs = ["memory.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:core_headers", "//absl/meta:type_traits", @@ -40,6 +42,7 @@ cc_test( name = "memory_test", srcs = ["memory_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":memory", "//absl/base", @@ -54,7 +57,7 @@ cc_test( "memory_exception_safety_test.cc", ], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":memory", "//absl/base:exception_safety_testing", diff --git a/absl/memory/CMakeLists.txt b/absl/memory/CMakeLists.txt index 4b494dc0..0a812203 100644 --- a/absl/memory/CMakeLists.txt +++ b/absl/memory/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/memory/memory.h b/absl/memory/memory.h index 34cfb285..7f525e4c 100644 --- a/absl/memory/memory.h +++ b/absl/memory/memory.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -34,7 +34,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // Function Template: WrapUnique() @@ -48,19 +48,14 @@ inline namespace lts_2018_12_18 { // X* NewX(int, int); // auto x = WrapUnique(NewX(1, 2)); // 'x' is std::unique_ptr<X>. // -// The purpose of WrapUnique is to automatically deduce the pointer type. If you -// wish to make the type explicit, for readability reasons or because you prefer -// to use a base-class pointer rather than a derived one, just use +// Do not call WrapUnique with an explicit type, as in +// `WrapUnique<X>(NewX(1, 2))`. The purpose of WrapUnique is to automatically +// deduce the pointer type. If you wish to make the type explicit, just use // `std::unique_ptr` directly. // -// Example: -// X* Factory(int, int); -// auto x = std::unique_ptr<X>(Factory(1, 2)); +// auto x = std::unique_ptr<X>(NewX(1, 2)); // - or - -// std::unique_ptr<X> x(Factory(1, 2)); -// -// This has the added advantage of working whether Factory returns a raw -// pointer or a `std::unique_ptr`. +// std::unique_ptr<X> x(NewX(1, 2)); // // While `absl::WrapUnique` is useful for capturing the output of a raw // pointer factory, prefer 'absl::make_unique<T>(args...)' over @@ -121,7 +116,7 @@ using std::make_unique; // // For more background on why `std::unique_ptr<T>(new T(a,b))` is problematic, // see Herb Sutter's explanation on -// (Exception-Safe Function Calls)[http://herbsutter.com/gotw/_102/]. +// (Exception-Safe Function Calls)[https://herbsutter.com/gotw/_102/]. // (In general, reviewers should treat `new T(a,b)` with scrutiny.) // // Example usage: @@ -647,7 +642,7 @@ struct allocator_is_nothrow : memory_internal::ExtractOrT<memory_internal::GetIsNothrow, Alloc, std::false_type> {}; -#if ABSL_ALLOCATOR_NOTHROW +#if defined(ABSL_ALLOCATOR_NOTHROW) && ABSL_ALLOCATOR_NOTHROW template <typename T> struct allocator_is_nothrow<std::allocator<T>> : std::true_type {}; struct default_allocator_is_nothrow : std::true_type {}; @@ -693,7 +688,7 @@ void CopyRange(Allocator& alloc, Iterator destination, InputIterator first, } } } // namespace memory_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_MEMORY_MEMORY_H_ diff --git a/absl/memory/memory_exception_safety_test.cc b/absl/memory/memory_exception_safety_test.cc index 9661502d..f5b39b34 100644 --- a/absl/memory/memory_exception_safety_test.cc +++ b/absl/memory/memory_exception_safety_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include "absl/base/internal/exception_safety_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { constexpr int kLength = 50; @@ -50,5 +50,5 @@ TEST(MakeUnique, CheckForLeaks) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/memory/memory_test.cc b/absl/memory/memory_test.cc index 21fe32f9..c47820e5 100644 --- a/absl/memory/memory_test.cc +++ b/absl/memory/memory_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -620,7 +620,7 @@ TEST(AllocatorTraits, FunctionsFull) { } TEST(AllocatorNoThrowTest, DefaultAllocator) { -#if ABSL_ALLOCATOR_NOTHROW +#if defined(ABSL_ALLOCATOR_NOTHROW) && ABSL_ALLOCATOR_NOTHROW EXPECT_TRUE(absl::default_allocator_is_nothrow::value); #else EXPECT_FALSE(absl::default_allocator_is_nothrow::value); @@ -628,7 +628,7 @@ TEST(AllocatorNoThrowTest, DefaultAllocator) { } TEST(AllocatorNoThrowTest, StdAllocator) { -#if ABSL_ALLOCATOR_NOTHROW +#if defined(ABSL_ALLOCATOR_NOTHROW) && ABSL_ALLOCATOR_NOTHROW EXPECT_TRUE(absl::allocator_is_nothrow<std::allocator<int>>::value); #else EXPECT_FALSE(absl::allocator_is_nothrow<std::allocator<int>>::value); diff --git a/absl/meta/BUILD.bazel b/absl/meta/BUILD.bazel index dbc9717d..e004b509 100644 --- a/absl/meta/BUILD.bazel +++ b/absl/meta/BUILD.bazel @@ -1,6 +1,7 @@ load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -12,6 +13,7 @@ cc_library( name = "type_traits", hdrs = ["type_traits.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", ], @@ -21,9 +23,9 @@ cc_test( name = "type_traits_test", srcs = ["type_traits_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":type_traits", - "//absl/base:core_headers", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/meta/CMakeLists.txt b/absl/meta/CMakeLists.txt index adb0ceb7..672ead2f 100644 --- a/absl/meta/CMakeLists.txt +++ b/absl/meta/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,39 +14,37 @@ # limitations under the License. # -list(APPEND META_PUBLIC_HEADERS - "type_traits.h" +absl_cc_library( + NAME + type_traits + HDRS + "type_traits.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + PUBLIC ) - -# -## TESTS -# - -# test type_traits_test -list(APPEND TYPE_TRAITS_TEST_SRC - "type_traits_test.cc" - ${META_PUBLIC_HEADERS} +absl_cc_test( + NAME + type_traits_test + SRCS + "type_traits_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::type_traits + gmock_main ) -absl_header_library( - TARGET - absl_meta - PUBLIC_LIBRARIES - absl::base - EXPORT_NAME +# component target +absl_cc_library( + NAME meta - ) - -absl_test( - TARGET - type_traits_test - SOURCES - ${TYPE_TRAITS_TEST_SRC} - PUBLIC_LIBRARIES - absl::base - absl::meta + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::type_traits + PUBLIC ) - - - diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index 231e08db..ae7130dd 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ // support type inference, classification, and transformation, as well as // make it easier to write templates based on generic type behavior. // -// See http://en.cppreference.com/w/cpp/header/type_traits +// See https://en.cppreference.com/w/cpp/header/type_traits // // WARNING: use of many of the constructs in this header will count as "complex // template metaprogramming", so before proceeding, please carefully consider @@ -42,10 +42,41 @@ #include "absl/base/config.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { + +// Defined and documented later on in this file. +template <typename T> +struct is_trivially_move_assignable; namespace type_traits_internal { +// Silence MSVC warnings about the destructor being defined as deleted. +#if defined(_MSC_VER) && !defined(__GNUC__) +#pragma warning(push) +#pragma warning(disable : 4624) +#endif // defined(_MSC_VER) && !defined(__GNUC__) + +template <class T> +union SingleMemberUnion { + T t; +}; + +// Restore the state of the destructor warning that was silenced above. +#if defined(_MSC_VER) && !defined(__GNUC__) +#pragma warning(pop) +#endif // defined(_MSC_VER) && !defined(__GNUC__) + +template <class T> +struct IsTriviallyMoveAssignableReference : std::false_type {}; + +template <class T> +struct IsTriviallyMoveAssignableReference<T&> + : absl::is_trivially_move_assignable<T>::type {}; + +template <class T> +struct IsTriviallyMoveAssignableReference<T&&> + : absl::is_trivially_move_assignable<T>::type {}; + template <typename... Ts> struct VoidTImpl { using type = void; @@ -194,6 +225,23 @@ struct disjunction<> : std::false_type {}; template <typename T> struct negation : std::integral_constant<bool, !T::value> {}; +// is_function() +// +// Determines whether the passed type `T` is a function type. +// +// This metafunction is designed to be a drop-in replacement for the C++11 +// `std::is_function()` metafunction for platforms that have incomplete C++11 +// support (such as libstdc++ 4.x). +// +// This metafunction works because appending `const` to a type does nothing to +// function types and reference types (and forms a const-qualified type +// otherwise). +template <typename T> +struct is_function + : std::integral_constant< + bool, !(std::is_reference<T>::value || + std::is_const<typename std::add_const<T>::type>::value)> {}; + // is_trivially_destructible() // // Determines whether the passed type `T` is trivially destructable. @@ -247,7 +295,7 @@ struct is_trivially_destructible // For the purposes of this check, the call to std::declval is considered // trivial." // -// Notes from http://en.cppreference.com/w/cpp/types/is_constructible: +// Notes from https://en.cppreference.com/w/cpp/types/is_constructible: // In many implementations, is_nothrow_constructible also checks if the // destructor throws because it is effectively noexcept(T(arg)). Same // applies to is_trivially_constructible, which, in these implementations, also @@ -276,6 +324,40 @@ struct is_trivially_default_constructible #endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE }; +// is_trivially_move_constructible() +// +// Determines whether the passed type `T` is trivially move constructible. +// +// This metafunction is designed to be a drop-in replacement for the C++11 +// `std::is_trivially_move_constructible()` metafunction for platforms that have +// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do +// fully support C++11, we check whether this yields the same result as the std +// implementation. +// +// NOTE: `T obj(declval<T>());` needs to be well-formed and not call any +// nontrivial operation. Nontrivially destructible types will cause the +// expression to be nontrivial. +template <typename T> +struct is_trivially_move_constructible + : std::conditional< + std::is_object<T>::value && !std::is_array<T>::value, + std::is_move_constructible< + type_traits_internal::SingleMemberUnion<T>>, + std::is_reference<T>>::type::type { +#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE + private: + static constexpr bool compliant = + std::is_trivially_move_constructible<T>::value == + is_trivially_move_constructible::value; + static_assert(compliant || std::is_trivially_move_constructible<T>::value, + "Not compliant with std::is_trivially_move_constructible; " + "Standard: false, Implementation: true"); + static_assert(compliant || !std::is_trivially_move_constructible<T>::value, + "Not compliant with std::is_trivially_move_constructible; " + "Standard: true, Implementation: false"); +#endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +}; + // is_trivially_copy_constructible() // // Determines whether the passed type `T` is trivially copy constructible. @@ -291,9 +373,11 @@ struct is_trivially_default_constructible // expression to be nontrivial. template <typename T> struct is_trivially_copy_constructible - : std::integral_constant<bool, __has_trivial_copy(T) && - std::is_copy_constructible<T>::value && - is_trivially_destructible<T>::value> { + : std::conditional< + std::is_object<T>::value && !std::is_array<T>::value, + std::is_copy_constructible< + type_traits_internal::SingleMemberUnion<T>>, + std::is_lvalue_reference<T>>::type::type { #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE private: static constexpr bool compliant = @@ -308,6 +392,42 @@ struct is_trivially_copy_constructible #endif // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE }; +// is_trivially_move_assignable() +// +// Determines whether the passed type `T` is trivially move assignable. +// +// This metafunction is designed to be a drop-in replacement for the C++11 +// `std::is_trivially_move_assignable()` metafunction for platforms that have +// incomplete C++11 support (such as libstdc++ 4.x). On any platforms that do +// fully support C++11, we check whether this yields the same result as the std +// implementation. +// +// NOTE: `is_assignable<T, U>::value` is `true` if the expression +// `declval<T>() = declval<U>()` is well-formed when treated as an unevaluated +// operand. `is_trivially_assignable<T, U>` requires the assignment to call no +// operation that is not trivial. `is_trivially_copy_assignable<T>` is simply +// `is_trivially_assignable<T&, T>`. +template <typename T> +struct is_trivially_move_assignable + : std::conditional< + std::is_object<T>::value && !std::is_array<T>::value, + std::is_move_assignable<type_traits_internal::SingleMemberUnion<T>>, + type_traits_internal::IsTriviallyMoveAssignableReference<T>>::type:: + type { +#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE + private: + static constexpr bool compliant = + std::is_trivially_move_assignable<T>::value == + is_trivially_move_assignable::value; + static_assert(compliant || std::is_trivially_move_assignable<T>::value, + "Not compliant with std::is_trivially_move_assignable; " + "Standard: false, Implementation: true"); + static_assert(compliant || !std::is_trivially_move_assignable<T>::value, + "Not compliant with std::is_trivially_move_assignable; " + "Standard: true, Implementation: false"); +#endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE +}; + // is_trivially_copy_assignable() // // Determines whether the passed type `T` is trivially copy assignable. @@ -342,6 +462,49 @@ struct is_trivially_copy_assignable #endif // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE }; +namespace type_traits_internal { +// is_trivially_copyable() +// +// Determines whether the passed type `T` is trivially copyable. +// +// This metafunction is designed to be a drop-in replacement for the C++11 +// `std::is_trivially_copyable()` metafunction for platforms that have +// incomplete C++11 support (such as libstdc++ 4.x). We use the C++17 definition +// of TriviallyCopyable. +// +// NOTE: `is_trivially_copyable<T>::value` is `true` if all of T's copy/move +// constructors/assignment operators are trivial or deleted, T has at least +// one non-deleted copy/move constructor/assignment operator, and T is trivially +// destructible. Arrays of trivially copyable types are trivially copyable. +// +// We expose this metafunction only for internal use within absl. +template <typename T> +class is_trivially_copyable_impl { + using ExtentsRemoved = typename std::remove_all_extents<T>::type; + static constexpr bool kIsCopyOrMoveConstructible = + std::is_copy_constructible<ExtentsRemoved>::value || + std::is_move_constructible<ExtentsRemoved>::value; + static constexpr bool kIsCopyOrMoveAssignable = + absl::is_copy_assignable<ExtentsRemoved>::value || + absl::is_move_assignable<ExtentsRemoved>::value; + + public: + static constexpr bool kValue = + (__has_trivial_copy(ExtentsRemoved) || !kIsCopyOrMoveConstructible) && + (__has_trivial_assign(ExtentsRemoved) || !kIsCopyOrMoveAssignable) && + (kIsCopyOrMoveConstructible || kIsCopyOrMoveAssignable) && + is_trivially_destructible<ExtentsRemoved>::value && + // We need to check for this explicitly because otherwise we'll say + // references are trivial copyable when compiled by MSVC. + !std::is_reference<ExtentsRemoved>::value; +}; + +template <typename T> +struct is_trivially_copyable + : std::integral_constant< + bool, type_traits_internal::is_trivially_copyable_impl<T>::kValue> {}; +} // namespace type_traits_internal + // ----------------------------------------------------------------------------- // C++14 "_t" trait aliases // ----------------------------------------------------------------------------- @@ -414,25 +577,140 @@ template <typename T> using result_of_t = typename std::result_of<T>::type; namespace type_traits_internal { +// In MSVC we can't probe std::hash or stdext::hash because it triggers a +// static_assert instead of failing substitution. Libc++ prior to 4.0 +// also used a static_assert. +// +#if defined(_MSC_VER) || (defined(_LIBCPP_VERSION) && \ + _LIBCPP_VERSION < 4000 && _LIBCPP_STD_VER > 11) +#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 0 +#else +#define ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ 1 +#endif + +#if !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ template <typename Key, typename = size_t> +struct IsHashable : std::true_type {}; +#else // ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ +template <typename Key, typename = void> struct IsHashable : std::false_type {}; template <typename Key> -struct IsHashable<Key, - decltype(std::declval<std::hash<Key>>()(std::declval<Key>()))> - : std::true_type {}; +struct IsHashable< + Key, + absl::enable_if_t<std::is_convertible< + decltype(std::declval<std::hash<Key>&>()(std::declval<Key const&>())), + std::size_t>::value>> : std::true_type {}; +#endif // !ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ + +struct AssertHashEnabledHelper { + private: + static void Sink(...) {} + struct NAT {}; + + template <class Key> + static auto GetReturnType(int) + -> decltype(std::declval<std::hash<Key>>()(std::declval<Key const&>())); + template <class Key> + static NAT GetReturnType(...); + + template <class Key> + static std::nullptr_t DoIt() { + static_assert(IsHashable<Key>::value, + "std::hash<Key> does not provide a call operator"); + static_assert( + std::is_default_constructible<std::hash<Key>>::value, + "std::hash<Key> must be default constructible when it is enabled"); + static_assert( + std::is_copy_constructible<std::hash<Key>>::value, + "std::hash<Key> must be copy constructible when it is enabled"); + static_assert(absl::is_copy_assignable<std::hash<Key>>::value, + "std::hash<Key> must be copy assignable when it is enabled"); + // is_destructible is unchecked as it's implied by each of the + // is_constructible checks. + using ReturnType = decltype(GetReturnType<Key>(0)); + static_assert(std::is_same<ReturnType, NAT>::value || + std::is_same<ReturnType, size_t>::value, + "std::hash<Key> must return size_t"); + return nullptr; + } + + template <class... Ts> + friend void AssertHashEnabled(); +}; -template <typename Key> -struct IsHashEnabled - : absl::conjunction<std::is_default_constructible<std::hash<Key>>, - std::is_copy_constructible<std::hash<Key>>, - std::is_destructible<std::hash<Key>>, - absl::is_copy_assignable<std::hash<Key>>, - IsHashable<Key>> {}; +template <class... Ts> +inline void AssertHashEnabled() { + using Helper = AssertHashEnabledHelper; + Helper::Sink(Helper::DoIt<Ts>()...); +} } // namespace type_traits_internal -} // inline namespace lts_2018_12_18 +// An internal namespace that is required to implement the C++17 swap traits. +// It is not further nested in type_traits_internal to avoid long symbol names. +namespace swap_internal { + +// Necessary for the traits. +using std::swap; + +// This declaration prevents global `swap` and `absl::swap` overloads from being +// considered unless ADL picks them up. +void swap(); + +template <class T> +using IsSwappableImpl = decltype(swap(std::declval<T&>(), std::declval<T&>())); + +// NOTE: This dance with the default template parameter is for MSVC. +template <class T, + class IsNoexcept = std::integral_constant< + bool, noexcept(swap(std::declval<T&>(), std::declval<T&>()))>> +using IsNothrowSwappableImpl = typename std::enable_if<IsNoexcept::value>::type; + +// IsSwappable +// +// Determines whether the standard swap idiom is a valid expression for +// arguments of type `T`. +template <class T> +struct IsSwappable + : absl::type_traits_internal::is_detected<IsSwappableImpl, T> {}; + +// IsNothrowSwappable +// +// Determines whether the standard swap idiom is a valid expression for +// arguments of type `T` and is noexcept. +template <class T> +struct IsNothrowSwappable + : absl::type_traits_internal::is_detected<IsNothrowSwappableImpl, T> {}; + +// Swap() +// +// Performs the swap idiom from a namespace where valid candidates may only be +// found in `std` or via ADL. +template <class T, absl::enable_if_t<IsSwappable<T>::value, int> = 0> +void Swap(T& lhs, T& rhs) noexcept(IsNothrowSwappable<T>::value) { + swap(lhs, rhs); +} + +// StdSwapIsUnconstrained +// +// Some standard library implementations are broken in that they do not +// constrain `std::swap`. This will effectively tell us if we are dealing with +// one of those implementations. +using StdSwapIsUnconstrained = IsSwappable<void()>; + +} // namespace swap_internal + +namespace type_traits_internal { + +// Make the swap-related traits/function accessible from this namespace. +using swap_internal::IsNothrowSwappable; +using swap_internal::IsSwappable; +using swap_internal::Swap; +using swap_internal::StdSwapIsUnconstrained; + +} // namespace type_traits_internal +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_META_TYPE_TRAITS_H_ diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc index f51f5ded..a7a9c5c9 100644 --- a/absl/meta/type_traits_test.cc +++ b/absl/meta/type_traits_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -214,6 +214,29 @@ class DeletedDefaultCtor { int n_; }; +class TrivialMoveCtor { + public: + explicit TrivialMoveCtor(int n) : n_(n) {} + TrivialMoveCtor(TrivialMoveCtor&&) = default; + TrivialMoveCtor& operator=(const TrivialMoveCtor& t) { + n_ = t.n_; + return *this; + } + + private: + int n_; +}; + +class NontrivialMoveCtor { + public: + explicit NontrivialMoveCtor(int n) : n_(n) {} + NontrivialMoveCtor(NontrivialMoveCtor&& t) noexcept : n_(t.n_) {} + NontrivialMoveCtor& operator=(const NontrivialMoveCtor&) = default; + + private: + int n_; +}; + class TrivialCopyCtor { public: explicit TrivialCopyCtor(int n) : n_(n) {} @@ -247,6 +270,29 @@ class DeletedCopyCtor { int n_; }; +class TrivialMoveAssign { + public: + explicit TrivialMoveAssign(int n) : n_(n) {} + TrivialMoveAssign(const TrivialMoveAssign& t) : n_(t.n_) {} + TrivialMoveAssign& operator=(TrivialMoveAssign&&) = default; + ~TrivialMoveAssign() {} // can have nontrivial destructor + private: + int n_; +}; + +class NontrivialMoveAssign { + public: + explicit NontrivialMoveAssign(int n) : n_(n) {} + NontrivialMoveAssign(const NontrivialMoveAssign&) = default; + NontrivialMoveAssign& operator=(NontrivialMoveAssign&& t) noexcept { + n_ = t.n_; + return *this; + } + + private: + int n_; +}; + class TrivialCopyAssign { public: explicit TrivialCopyAssign(int n) : n_(n) {} @@ -280,10 +326,20 @@ class DeletedCopyAssign { int n_; }; -struct NonCopyable { - NonCopyable() = default; - NonCopyable(const NonCopyable&) = delete; - NonCopyable& operator=(const NonCopyable&) = delete; +struct MovableNonCopyable { + MovableNonCopyable() = default; + MovableNonCopyable(const MovableNonCopyable&) = delete; + MovableNonCopyable(MovableNonCopyable&&) = default; + MovableNonCopyable& operator=(const MovableNonCopyable&) = delete; + MovableNonCopyable& operator=(MovableNonCopyable&&) = default; +}; + +struct NonCopyableOrMovable { + NonCopyableOrMovable() = default; + NonCopyableOrMovable(const NonCopyableOrMovable&) = delete; + NonCopyableOrMovable(NonCopyableOrMovable&&) = delete; + NonCopyableOrMovable& operator=(const NonCopyableOrMovable&) = delete; + NonCopyableOrMovable& operator=(NonCopyableOrMovable&&) = delete; }; class Base { @@ -329,6 +385,22 @@ class Base { #define ABSL_GCC_BUG_TRIVIALLY_CONSTRUCTIBLE_ON_ARRAY_OF_NONTRIVIAL 1 #endif +TEST(TypeTraitsTest, TestIsFunction) { + struct Callable { + void operator()() {} + }; + EXPECT_TRUE(absl::is_function<void()>::value); + EXPECT_TRUE(absl::is_function<void()&>::value); + EXPECT_TRUE(absl::is_function<void() const>::value); + EXPECT_TRUE(absl::is_function<void() noexcept>::value); + EXPECT_TRUE(absl::is_function<void(...) noexcept>::value); + + EXPECT_FALSE(absl::is_function<void(*)()>::value); + EXPECT_FALSE(absl::is_function<void(&)()>::value); + EXPECT_FALSE(absl::is_function<int>::value); + EXPECT_FALSE(absl::is_function<Callable>::value); +} + TEST(TypeTraitsTest, TestTrivialDestructor) { // Verify that arithmetic types and pointers have trivial destructors. EXPECT_TRUE(absl::is_trivially_destructible<bool>::value); @@ -474,6 +546,79 @@ TEST(TypeTraitsTest, TestTrivialDefaultCtor) { #endif } +TEST(TypeTraitsTest, TestTrivialMoveCtor) { + // Verify that arithmetic types and pointers have trivial move + // constructors. + EXPECT_TRUE(absl::is_trivially_move_constructible<bool>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<char>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<unsigned char>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<signed char>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<wchar_t>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<int>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<unsigned int>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<int16_t>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<uint16_t>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<int64_t>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<uint64_t>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<float>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<double>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<long double>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<std::string*>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial*>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<const std::string*>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<const Trivial*>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<std::string**>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial**>::value); + + // Reference types + EXPECT_TRUE(absl::is_trivially_move_constructible<int&>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<int&&>::value); + + // types with compiler generated move ctors + EXPECT_TRUE(absl::is_trivially_move_constructible<Trivial>::value); + EXPECT_TRUE(absl::is_trivially_move_constructible<TrivialMoveCtor>::value); + + // Verify that types without them (i.e. nontrivial or deleted) are not. + EXPECT_FALSE( + absl::is_trivially_move_constructible<NontrivialCopyCtor>::value); + EXPECT_FALSE(absl::is_trivially_move_constructible<DeletedCopyCtor>::value); + EXPECT_FALSE( + absl::is_trivially_move_constructible<NonCopyableOrMovable>::value); + +#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE + // type with nontrivial destructor are nontrivial move construbtible + EXPECT_FALSE( + absl::is_trivially_move_constructible<NontrivialDestructor>::value); +#endif + + // types with vtables + EXPECT_FALSE(absl::is_trivially_move_constructible<Base>::value); + + // Verify that simple_pair of such types is trivially move constructible + EXPECT_TRUE( + (absl::is_trivially_move_constructible<simple_pair<int, char*>>::value)); + EXPECT_TRUE(( + absl::is_trivially_move_constructible<simple_pair<int, Trivial>>::value)); + EXPECT_TRUE((absl::is_trivially_move_constructible< + simple_pair<int, TrivialMoveCtor>>::value)); + + // Verify that types without trivial move constructors are + // correctly marked as such. + EXPECT_FALSE(absl::is_trivially_move_constructible<std::string>::value); + EXPECT_FALSE(absl::is_trivially_move_constructible<std::vector<int>>::value); + + // Verify that simple_pairs of types without trivial move constructors + // are not marked as trivial. + EXPECT_FALSE((absl::is_trivially_move_constructible< + simple_pair<int, std::string>>::value)); + EXPECT_FALSE((absl::is_trivially_move_constructible< + simple_pair<std::string, int>>::value)); + + // Verify that arrays are not + using int10 = int[10]; + EXPECT_FALSE(absl::is_trivially_move_constructible<int10>::value); +} + TEST(TypeTraitsTest, TestTrivialCopyCtor) { // Verify that arithmetic types and pointers have trivial copy // constructors. @@ -498,6 +643,10 @@ TEST(TypeTraitsTest, TestTrivialCopyCtor) { EXPECT_TRUE(absl::is_trivially_copy_constructible<std::string**>::value); EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial**>::value); + // Reference types + EXPECT_TRUE(absl::is_trivially_copy_constructible<int&>::value); + EXPECT_FALSE(absl::is_trivially_copy_constructible<int&&>::value); + // types with compiler generated copy ctors EXPECT_TRUE(absl::is_trivially_copy_constructible<Trivial>::value); EXPECT_TRUE(absl::is_trivially_copy_constructible<TrivialCopyCtor>::value); @@ -507,7 +656,9 @@ TEST(TypeTraitsTest, TestTrivialCopyCtor) { absl::is_trivially_copy_constructible<NontrivialCopyCtor>::value); EXPECT_FALSE(absl::is_trivially_copy_constructible<DeletedCopyCtor>::value); EXPECT_FALSE( - absl::is_trivially_copy_constructible<NonCopyable>::value); + absl::is_trivially_copy_constructible<MovableNonCopyable>::value); + EXPECT_FALSE( + absl::is_trivially_copy_constructible<NonCopyableOrMovable>::value); #ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE // type with nontrivial destructor are nontrivial copy construbtible @@ -543,6 +694,74 @@ TEST(TypeTraitsTest, TestTrivialCopyCtor) { EXPECT_FALSE(absl::is_trivially_copy_constructible<int10>::value); } +TEST(TypeTraitsTest, TestTrivialMoveAssign) { + // Verify that arithmetic types and pointers have trivial move + // assignment operators. + EXPECT_TRUE(absl::is_trivially_move_assignable<bool>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<char>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<unsigned char>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<signed char>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<wchar_t>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<int>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<unsigned int>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<int16_t>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<uint16_t>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<int64_t>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<uint64_t>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<float>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<double>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<long double>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<std::string*>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial*>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<const std::string*>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<const Trivial*>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<std::string**>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial**>::value); + + // const qualified types are not assignable + EXPECT_FALSE(absl::is_trivially_move_assignable<const int>::value); + + // types with compiler generated move assignment + EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<TrivialMoveAssign>::value); + + // Verify that types without them (i.e. nontrivial or deleted) are not. + EXPECT_FALSE(absl::is_trivially_move_assignable<NontrivialCopyAssign>::value); + EXPECT_FALSE(absl::is_trivially_move_assignable<DeletedCopyAssign>::value); + EXPECT_FALSE(absl::is_trivially_move_assignable<NonCopyableOrMovable>::value); + + // types with vtables + EXPECT_FALSE(absl::is_trivially_move_assignable<Base>::value); + + // Verify that simple_pair is trivially assignable + EXPECT_TRUE( + (absl::is_trivially_move_assignable<simple_pair<int, char*>>::value)); + EXPECT_TRUE( + (absl::is_trivially_move_assignable<simple_pair<int, Trivial>>::value)); + EXPECT_TRUE((absl::is_trivially_move_assignable< + simple_pair<int, TrivialMoveAssign>>::value)); + + // Verify that types not trivially move assignable are + // correctly marked as such. + EXPECT_FALSE(absl::is_trivially_move_assignable<std::string>::value); + EXPECT_FALSE(absl::is_trivially_move_assignable<std::vector<int>>::value); + + // Verify that simple_pairs of types not trivially move assignable + // are not marked as trivial. + EXPECT_FALSE((absl::is_trivially_move_assignable< + simple_pair<int, std::string>>::value)); + EXPECT_FALSE((absl::is_trivially_move_assignable< + simple_pair<std::string, int>>::value)); + + // Verify that arrays are not trivially move assignable + using int10 = int[10]; + EXPECT_FALSE(absl::is_trivially_move_assignable<int10>::value); + + // Verify that references are handled correctly + EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial&&>::value); + EXPECT_TRUE(absl::is_trivially_move_assignable<Trivial&>::value); +} + TEST(TypeTraitsTest, TestTrivialCopyAssign) { // Verify that arithmetic types and pointers have trivial copy // assignment operators. @@ -577,7 +796,8 @@ TEST(TypeTraitsTest, TestTrivialCopyAssign) { // Verify that types without them (i.e. nontrivial or deleted) are not. EXPECT_FALSE(absl::is_trivially_copy_assignable<NontrivialCopyAssign>::value); EXPECT_FALSE(absl::is_trivially_copy_assignable<DeletedCopyAssign>::value); - EXPECT_FALSE(absl::is_trivially_copy_assignable<NonCopyable>::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable<MovableNonCopyable>::value); + EXPECT_FALSE(absl::is_trivially_copy_assignable<NonCopyableOrMovable>::value); // types with vtables EXPECT_FALSE(absl::is_trivially_copy_assignable<Base>::value); @@ -611,6 +831,116 @@ TEST(TypeTraitsTest, TestTrivialCopyAssign) { EXPECT_TRUE(absl::is_trivially_copy_assignable<Trivial&>::value); } +TEST(TypeTraitsTest, TestTriviallyCopyable) { + // Verify that arithmetic types and pointers are trivially copyable. + EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<bool>::value); + EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<char>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<unsigned char>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<signed char>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<wchar_t>::value); + EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<int>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<unsigned int>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<int16_t>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<uint16_t>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<int64_t>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<uint64_t>::value); + EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<float>::value); + EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<double>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<long double>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<std::string*>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<Trivial*>::value); + EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable< + const std::string*>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<const Trivial*>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<std::string**>::value); + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<Trivial**>::value); + + // const qualified types are not assignable but are constructible + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<const int>::value); + + // Trivial copy constructor/assignment and destructor. + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<Trivial>::value); + // Trivial copy assignment, but non-trivial copy constructor/destructor. + EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable< + TrivialCopyAssign>::value); + // Trivial copy constructor, but non-trivial assignment. + EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable< + TrivialCopyCtor>::value); + + // Types with a non-trivial copy constructor/assignment + EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable< + NontrivialCopyCtor>::value); + EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable< + NontrivialCopyAssign>::value); + + // Types without copy constructor/assignment, but with move + // MSVC disagrees with other compilers about this: + // EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable< + // MovableNonCopyable>::value); + + // Types without copy/move constructor/assignment + EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable< + NonCopyableOrMovable>::value); + + // No copy assign, but has trivial copy constructor. + EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable< + DeletedCopyAssign>::value); + + // types with vtables + EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable<Base>::value); + + // Verify that simple_pair is trivially copyable if members are + EXPECT_TRUE((absl::type_traits_internal::is_trivially_copyable< + simple_pair<int, char*>>::value)); + EXPECT_TRUE((absl::type_traits_internal::is_trivially_copyable< + simple_pair<int, Trivial>>::value)); + + // Verify that types not trivially copyable are + // correctly marked as such. + EXPECT_FALSE( + absl::type_traits_internal::is_trivially_copyable<std::string>::value); + EXPECT_FALSE(absl::type_traits_internal::is_trivially_copyable< + std::vector<int>>::value); + + // Verify that simple_pairs of types not trivially copyable + // are not marked as trivial. + EXPECT_FALSE((absl::type_traits_internal::is_trivially_copyable< + simple_pair<int, std::string>>::value)); + EXPECT_FALSE((absl::type_traits_internal::is_trivially_copyable< + simple_pair<std::string, int>>::value)); + EXPECT_FALSE((absl::type_traits_internal::is_trivially_copyable< + simple_pair<int, TrivialCopyAssign>>::value)); + + // Verify that arrays of trivially copyable types are trivially copyable + using int10 = int[10]; + EXPECT_TRUE(absl::type_traits_internal::is_trivially_copyable<int10>::value); + using int10x10 = int[10][10]; + EXPECT_TRUE( + absl::type_traits_internal::is_trivially_copyable<int10x10>::value); + + // Verify that references are handled correctly + EXPECT_FALSE( + absl::type_traits_internal::is_trivially_copyable<Trivial&&>::value); + EXPECT_FALSE( + absl::type_traits_internal::is_trivially_copyable<Trivial&>::value); +} + #define ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(trait_name, ...) \ EXPECT_TRUE((std::is_same<typename std::trait_name<__VA_ARGS__>::type, \ absl::trait_name##_t<__VA_ARGS__>>::value)) @@ -953,4 +1283,85 @@ TEST(TypeTraitsTest, IsMoveAssignable) { #endif // _LIBCPP_VERSION } +namespace adl_namespace { + +struct DeletedSwap { +}; + +void swap(DeletedSwap&, DeletedSwap&) = delete; + +struct SpecialNoexceptSwap { + SpecialNoexceptSwap(SpecialNoexceptSwap&&) {} + SpecialNoexceptSwap& operator=(SpecialNoexceptSwap&&) { return *this; } + ~SpecialNoexceptSwap() = default; +}; + +void swap(SpecialNoexceptSwap&, SpecialNoexceptSwap&) noexcept {} + +} // namespace adl_namespace + +TEST(TypeTraitsTest, IsSwappable) { + using absl::type_traits_internal::IsSwappable; + using absl::type_traits_internal::StdSwapIsUnconstrained; + + EXPECT_TRUE(IsSwappable<int>::value); + + struct S {}; + EXPECT_TRUE(IsSwappable<S>::value); + + struct NoConstruct { + NoConstruct(NoConstruct&&) = delete; + NoConstruct& operator=(NoConstruct&&) { return *this; } + ~NoConstruct() = default; + }; + + EXPECT_EQ(IsSwappable<NoConstruct>::value, StdSwapIsUnconstrained::value); + struct NoAssign { + NoAssign(NoAssign&&) {} + NoAssign& operator=(NoAssign&&) = delete; + ~NoAssign() = default; + }; + + EXPECT_EQ(IsSwappable<NoAssign>::value, StdSwapIsUnconstrained::value); + + EXPECT_FALSE(IsSwappable<adl_namespace::DeletedSwap>::value); + + EXPECT_TRUE(IsSwappable<adl_namespace::SpecialNoexceptSwap>::value); +} + +TEST(TypeTraitsTest, IsNothrowSwappable) { + using absl::type_traits_internal::IsNothrowSwappable; + using absl::type_traits_internal::StdSwapIsUnconstrained; + + EXPECT_TRUE(IsNothrowSwappable<int>::value); + + struct NonNoexceptMoves { + NonNoexceptMoves(NonNoexceptMoves&&) {} + NonNoexceptMoves& operator=(NonNoexceptMoves&&) { return *this; } + ~NonNoexceptMoves() = default; + }; + + EXPECT_FALSE(IsNothrowSwappable<NonNoexceptMoves>::value); + + struct NoConstruct { + NoConstruct(NoConstruct&&) = delete; + NoConstruct& operator=(NoConstruct&&) { return *this; } + ~NoConstruct() = default; + }; + + EXPECT_FALSE(IsNothrowSwappable<NoConstruct>::value); + + struct NoAssign { + NoAssign(NoAssign&&) {} + NoAssign& operator=(NoAssign&&) = delete; + ~NoAssign() = default; + }; + + EXPECT_FALSE(IsNothrowSwappable<NoAssign>::value); + + EXPECT_FALSE(IsNothrowSwappable<adl_namespace::DeletedSwap>::value); + + EXPECT_TRUE(IsNothrowSwappable<adl_namespace::SpecialNoexceptSwap>::value); +} + } // namespace diff --git a/absl/numeric/BUILD.bazel b/absl/numeric/BUILD.bazel index 324ce669..d9b561df 100644 --- a/absl/numeric/BUILD.bazel +++ b/absl/numeric/BUILD.bazel @@ -4,7 +4,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -13,8 +13,9 @@ # limitations under the License. load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -31,6 +32,7 @@ cc_library( ], hdrs = ["int128.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:config", "//absl/base:core_headers", @@ -45,6 +47,7 @@ cc_test( "int128_test.cc", ], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":int128", "//absl/base", @@ -59,6 +62,7 @@ cc_test( name = "int128_benchmark", srcs = ["int128_benchmark.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], deps = [ ":int128", diff --git a/absl/numeric/CMakeLists.txt b/absl/numeric/CMakeLists.txt index 3360b2ee..242889f0 100644 --- a/absl/numeric/CMakeLists.txt +++ b/absl/numeric/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,49 +14,47 @@ # limitations under the License. # -list(APPEND NUMERIC_PUBLIC_HEADERS - "int128.h" -) - - -# library 128 -list(APPEND INT128_SRC - "int128.cc" - ${NUMERIC_PUBLIC_HEADERS} -) -absl_library( - TARGET - absl_int128 - SOURCES - ${INT128_SRC} - PUBLIC_LIBRARIES - ${INT128_PUBLIC_LIBRARIES} - EXPORT_NAME +absl_cc_library( + NAME int128 + HDRS + "int128.h" + SRCS + "int128.cc" + "int128_have_intrinsic.inc" + "int128_no_intrinsic.inc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::config + absl::core_headers + PUBLIC ) - -absl_header_library( - TARGET - absl_numeric - PUBLIC_LIBRARIES +absl_cc_test( + NAME + int128_test + SRCS + "int128_stream_test.cc" + "int128_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS absl::int128 - EXPORT_NAME - numeric + absl::base + absl::core_headers + absl::hash_testing + absl::type_traits + gmock_main ) -# test int128_test -set(INT128_TEST_SRC "int128_test.cc") -set(INT128_TEST_PUBLIC_LIBRARIES absl::numeric absl::base) - -absl_test( - TARGET - int128_test - SOURCES - ${INT128_TEST_SRC} - PUBLIC_LIBRARIES - ${INT128_TEST_PUBLIC_LIBRARIES} +# component target +absl_cc_library( + NAME + numeric + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::int128 + PUBLIC ) - - - diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index 98af84b7..a22f1e3e 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include <type_traits> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { const uint128 kuint128max = MakeUint128(std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max()); @@ -66,7 +66,7 @@ static inline int Fls128(uint128 n) { // Long division/modulo for uint128 implemented using the shift-subtract // division algorithm adapted from: -// http://stackoverflow.com/questions/5386377/division-without-using +// https://stackoverflow.com/questions/5386377/division-without-using void DivModImpl(uint128 dividend, uint128 divisor, uint128* quotient_ret, uint128* remainder_ret) { assert(divisor != 0); @@ -124,6 +124,28 @@ uint128 MakeUint128FromFloat(T v) { return MakeUint128(0, static_cast<uint64_t>(v)); } + +#if defined(__clang__) && !defined(__SSE3__) +// Workaround for clang bug: https://bugs.llvm.org/show_bug.cgi?id=38289 +// Casting from long double to uint64_t is miscompiled and drops bits. +// It is more work, so only use when we need the workaround. +uint128 MakeUint128FromFloat(long double v) { + // Go 50 bits at a time, that fits in a double + static_assert(std::numeric_limits<double>::digits >= 50, ""); + static_assert(std::numeric_limits<long double>::digits <= 150, ""); + // Undefined behavior if v is not finite or cannot fit into uint128. + assert(std::isfinite(v) && v > -1 && v < std::ldexp(1.0L, 128)); + + v = std::ldexp(v, -100); + uint64_t w0 = static_cast<uint64_t>(static_cast<double>(std::trunc(v))); + v = std::ldexp(v - static_cast<double>(w0), 50); + uint64_t w1 = static_cast<uint64_t>(static_cast<double>(std::trunc(v))); + v = std::ldexp(v - static_cast<double>(w1), 50); + uint64_t w2 = static_cast<uint64_t>(static_cast<double>(std::trunc(v))); + return (static_cast<uint128>(w0) << 100) | (static_cast<uint128>(w1) << 50) | + static_cast<uint128>(w2); +} +#endif // __clang__ && !__SSE3__ } // namespace uint128::uint128(float v) : uint128(MakeUint128FromFloat(v)) {} @@ -223,7 +245,7 @@ std::ostream& operator<<(std::ostream& os, uint128 v) { return os << rep; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl namespace std { diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index cb4776f1..a9693a27 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,8 +19,8 @@ // // This header file defines 128-bit integer types. // -// Currently, this file defines `uint128`, an unsigned 128-bit integer; a signed -// 128-bit integer is forthcoming. +// Currently, this file defines `uint128`, an unsigned 128-bit integer; +// a signed 128-bit integer is forthcoming. #ifndef ABSL_NUMERIC_INT128_H_ #define ABSL_NUMERIC_INT128_H_ @@ -37,14 +37,22 @@ #include "absl/base/macros.h" #include "absl/base/port.h" -#if defined(_MSC_VER) && defined(_WIN64) +#if defined(_MSC_VER) +// In very old versions of MSVC and when the /Zc:wchar_t flag is off, wchar_t is +// a typedef for unsigned short. Otherwise wchar_t is mapped to the __wchar_t +// builtin type. We need to make sure not to define operator wchar_t() +// alongside operator unsigned short() in these instances. +#define ABSL_INTERNAL_WCHAR_T __wchar_t +#if defined(_M_X64) #include <intrin.h> #pragma intrinsic(_umul128) -#endif // defined(_MSC_VER) && defined(_WIN64) +#endif // defined(_M_X64) +#else // defined(_MSC_VER) +#define ABSL_INTERNAL_WCHAR_T wchar_t +#endif // defined(_MSC_VER) namespace absl { -inline namespace lts_2018_12_18 { - +inline namespace lts_2019_08_08 { // uint128 // @@ -132,7 +140,7 @@ class constexpr explicit operator unsigned char() const; constexpr explicit operator char16_t() const; constexpr explicit operator char32_t() const; - constexpr explicit operator wchar_t() const; + constexpr explicit operator ABSL_INTERNAL_WCHAR_T() const; constexpr explicit operator short() const; // NOLINT(runtime/int) // NOLINTNEXTLINE(runtime/int) constexpr explicit operator unsigned short() const; @@ -237,7 +245,7 @@ constexpr uint128 Uint128Max() { (std::numeric_limits<uint64_t>::max)()); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // Specialized numeric_limits for uint128. @@ -273,9 +281,9 @@ class numeric_limits<absl::uint128> { #endif // ABSL_HAVE_INTRINSIC_INT128 static constexpr bool tinyness_before = false; - static constexpr absl::uint128 min() { return 0; } + static constexpr absl::uint128 (min)() { return 0; } static constexpr absl::uint128 lowest() { return 0; } - static constexpr absl::uint128 max() { return absl::Uint128Max(); } + static constexpr absl::uint128 (max)() { return absl::Uint128Max(); } static constexpr absl::uint128 epsilon() { return 0; } static constexpr absl::uint128 round_error() { return 0; } static constexpr absl::uint128 infinity() { return 0; } @@ -291,7 +299,7 @@ class numeric_limits<absl::uint128> { // Implementation details follow // -------------------------------------------------------------------------- namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { constexpr uint128 MakeUint128(uint64_t high, uint64_t low) { return uint128(high, low); @@ -471,8 +479,8 @@ constexpr uint128::operator char32_t() const { return static_cast<char32_t>(lo_); } -constexpr uint128::operator wchar_t() const { - return static_cast<wchar_t>(lo_); +constexpr uint128::operator ABSL_INTERNAL_WCHAR_T() const { + return static_cast<ABSL_INTERNAL_WCHAR_T>(lo_); } // NOLINTNEXTLINE(runtime/int) @@ -669,7 +677,7 @@ inline uint128 operator*(uint128 lhs, uint128 rhs) { // can be used for uint128 storage. return static_cast<unsigned __int128>(lhs) * static_cast<unsigned __int128>(rhs); -#elif defined(_MSC_VER) && defined(_WIN64) +#elif defined(_MSC_VER) && defined(_M_X64) uint64_t carry; uint64_t low = _umul128(Uint128Low64(lhs), Uint128Low64(rhs), &carry); return MakeUint128(Uint128Low64(lhs) * Uint128High64(rhs) + @@ -720,7 +728,9 @@ inline uint128& uint128::operator--() { #include "absl/numeric/int128_no_intrinsic.inc" #endif // ABSL_HAVE_INTRINSIC_INT128 -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl +#undef ABSL_INTERNAL_WCHAR_T + #endif // ABSL_NUMERIC_INT128_H_ diff --git a/absl/numeric/int128_benchmark.cc b/absl/numeric/int128_benchmark.cc index 1cb7d0ed..a5502d92 100644 --- a/absl/numeric/int128_benchmark.cc +++ b/absl/numeric/int128_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/numeric/int128_have_intrinsic.inc b/absl/numeric/int128_have_intrinsic.inc index ee2a0930..c7ea6834 100644 --- a/absl/numeric/int128_have_intrinsic.inc +++ b/absl/numeric/int128_have_intrinsic.inc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,4 +15,4 @@ // This file contains :int128 implementation details that depend on internal // representation when ABSL_HAVE_INTRINSIC_INT128 is defined. This file is -// included by int128.h. +// included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined. diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc index 0d0b3cfd..046cb9b3 100644 --- a/absl/numeric/int128_no_intrinsic.inc +++ b/absl/numeric/int128_no_intrinsic.inc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,4 +15,4 @@ // This file contains :int128 implementation details that depend on internal // representation when ABSL_HAVE_INTRINSIC_INT128 is *not* defined. This file -// is included by int128.h. +// is included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined. diff --git a/absl/numeric/int128_stream_test.cc b/absl/numeric/int128_stream_test.cc index 09efaad4..3cfa9dc1 100644 --- a/absl/numeric/int128_stream_test.cc +++ b/absl/numeric/int128_stream_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc index dfe3475a..5e1b5ec3 100644 --- a/absl/numeric/int128_test.cc +++ b/absl/numeric/int128_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -51,7 +51,7 @@ template <typename T> class Uint128FloatTraitsTest : public ::testing::Test {}; typedef ::testing::Types<float, double, long double> FloatingPointTypes; -TYPED_TEST_CASE(Uint128IntegerTraitsTest, IntegerTypes); +TYPED_TEST_SUITE(Uint128IntegerTraitsTest, IntegerTypes); TYPED_TEST(Uint128IntegerTraitsTest, ConstructAssignTest) { static_assert(std::is_constructible<absl::uint128, TypeParam>::value, @@ -62,7 +62,7 @@ TYPED_TEST(Uint128IntegerTraitsTest, ConstructAssignTest) { "TypeParam must not be assignable from absl::uint128"); } -TYPED_TEST_CASE(Uint128FloatTraitsTest, FloatingPointTypes); +TYPED_TEST_SUITE(Uint128FloatTraitsTest, FloatingPointTypes); TYPED_TEST(Uint128FloatTraitsTest, ConstructAssignTest) { static_assert(std::is_constructible<absl::uint128, TypeParam>::value, @@ -271,6 +271,20 @@ TEST(Uint128, ConversionTests) { EXPECT_EQ(static_cast<absl::uint128>(round_to_zero), 0); EXPECT_EQ(static_cast<absl::uint128>(round_to_five), 5); EXPECT_EQ(static_cast<absl::uint128>(round_to_nine), 9); + + absl::uint128 highest_precision_in_long_double = + ~absl::uint128{} >> (128 - std::numeric_limits<long double>::digits); + EXPECT_EQ(highest_precision_in_long_double, + static_cast<absl::uint128>( + static_cast<long double>(highest_precision_in_long_double))); + // Apply a mask just to make sure all the bits are the right place. + const absl::uint128 arbitrary_mask = + absl::MakeUint128(0xa29f622677ded751, 0xf8ca66add076f468); + EXPECT_EQ(highest_precision_in_long_double & arbitrary_mask, + static_cast<absl::uint128>(static_cast<long double>( + highest_precision_in_long_double & arbitrary_mask))); + + EXPECT_EQ(static_cast<absl::uint128>(-0.1L), 0); } TEST(Uint128, OperatorAssignReturnRef) { @@ -440,4 +454,29 @@ TEST(Uint128, NumericLimitsTest) { EXPECT_EQ(absl::Uint128Max(), std::numeric_limits<absl::uint128>::max()); } +TEST(Uint128, Hash) { + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ + // Some simple values + absl::uint128{0}, + absl::uint128{1}, + ~absl::uint128{}, + // 64 bit limits + absl::uint128{std::numeric_limits<int64_t>::max()}, + absl::uint128{std::numeric_limits<uint64_t>::max()} + 0, + absl::uint128{std::numeric_limits<uint64_t>::max()} + 1, + absl::uint128{std::numeric_limits<uint64_t>::max()} + 2, + // Keeping high same + absl::uint128{1} << 62, + absl::uint128{1} << 63, + // Keeping low same + absl::uint128{1} << 64, + absl::uint128{1} << 65, + // 128 bit limits + std::numeric_limits<absl::uint128>::max(), + std::numeric_limits<absl::uint128>::max() - 1, + std::numeric_limits<absl::uint128>::min() + 1, + std::numeric_limits<absl::uint128>::min(), + })); +} + } // namespace diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel new file mode 100644 index 00000000..f7587bf9 --- /dev/null +++ b/absl/random/BUILD.bazel @@ -0,0 +1,390 @@ +# ABSL random-number generation libraries. + +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", + "ABSL_EXCEPTIONS_FLAG", + "ABSL_EXCEPTIONS_FLAG_LINKOPTS", + "ABSL_TEST_COPTS", +) + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +cc_library( + name = "random", + hdrs = ["random.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":seed_sequences", + "//absl/random/internal:nonsecure_base", + "//absl/random/internal:pcg_engine", + "//absl/random/internal:pool_urbg", + "//absl/random/internal:randen_engine", + ], +) + +cc_library( + name = "distributions", + srcs = [ + "discrete_distribution.cc", + "gaussian_distribution.cc", + ], + hdrs = [ + "bernoulli_distribution.h", + "beta_distribution.h", + "discrete_distribution.h", + "distribution_format_traits.h", + "distributions.h", + "exponential_distribution.h", + "gaussian_distribution.h", + "log_uniform_int_distribution.h", + "poisson_distribution.h", + "uniform_int_distribution.h", + "uniform_real_distribution.h", + "zipf_distribution.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:base_internal", + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/random/internal:distribution_impl", + "//absl/random/internal:distributions", + "//absl/random/internal:fast_uniform_bits", + "//absl/random/internal:fastmath", + "//absl/random/internal:iostream_state_saver", + "//absl/random/internal:traits", + "//absl/random/internal:uniform_helper", + "//absl/strings", + "//absl/types:span", + ], +) + +cc_library( + name = "seed_gen_exception", + srcs = ["seed_gen_exception.cc"], + hdrs = ["seed_gen_exception.h"], + copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, + deps = ["//absl/base:config"], +) + +cc_library( + name = "seed_sequences", + srcs = ["seed_sequences.cc"], + hdrs = [ + "seed_sequences.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":seed_gen_exception", + "//absl/container:inlined_vector", + "//absl/random/internal:nonsecure_base", + "//absl/random/internal:pool_urbg", + "//absl/random/internal:salted_seed_seq", + "//absl/random/internal:seed_material", + "//absl/types:span", + ], +) + +cc_test( + name = "bernoulli_distribution_test", + size = "small", + timeout = "eternal", # Android can take a very long time + srcs = ["bernoulli_distribution_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "//absl/random/internal:sequence_urbg", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "beta_distribution_test", + size = "small", + timeout = "eternal", # Android can take a very long time + srcs = ["beta_distribution_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "//absl/base", + "//absl/random/internal:distribution_test_util", + "//absl/random/internal:sequence_urbg", + "//absl/strings", + "//absl/strings:str_format", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "distributions_test", + size = "small", + srcs = [ + "distributions_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "//absl/random/internal:distribution_test_util", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "generators_test", + size = "small", + srcs = ["generators_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "log_uniform_int_distribution_test", + size = "medium", + srcs = [ + "log_uniform_int_distribution_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "//absl/base", + "//absl/base:core_headers", + "//absl/random/internal:distribution_test_util", + "//absl/random/internal:sequence_urbg", + "//absl/strings", + "//absl/strings:str_format", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "discrete_distribution_test", + size = "medium", + srcs = [ + "discrete_distribution_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "//absl/base", + "//absl/random/internal:distribution_test_util", + "//absl/random/internal:sequence_urbg", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "poisson_distribution_test", + size = "small", + timeout = "eternal", # Android can take a very long time + srcs = [ + "poisson_distribution_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + # Too Slow. + "no_test_android_arm", + "no_test_loonix", + ], + deps = [ + ":distributions", + ":random", + "//absl/base", + "//absl/base:core_headers", + "//absl/container:flat_hash_map", + "//absl/random/internal:distribution_test_util", + "//absl/random/internal:sequence_urbg", + "//absl/strings", + "//absl/strings:str_format", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "exponential_distribution_test", + size = "small", + srcs = ["exponential_distribution_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "//absl/base", + "//absl/base:core_headers", + "//absl/random/internal:distribution_test_util", + "//absl/random/internal:sequence_urbg", + "//absl/strings", + "//absl/strings:str_format", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "gaussian_distribution_test", + size = "small", + timeout = "eternal", # Android can take a very long time + srcs = [ + "gaussian_distribution_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "//absl/base", + "//absl/base:core_headers", + "//absl/random/internal:distribution_test_util", + "//absl/random/internal:sequence_urbg", + "//absl/strings", + "//absl/strings:str_format", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "uniform_int_distribution_test", + size = "medium", + timeout = "long", + srcs = [ + "uniform_int_distribution_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "//absl/base", + "//absl/random/internal:distribution_test_util", + "//absl/random/internal:sequence_urbg", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "uniform_real_distribution_test", + size = "medium", + srcs = [ + "uniform_real_distribution_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_android_arm", + "no_test_android_arm64", + "no_test_android_x86", + ], + deps = [ + ":distributions", + ":random", + "//absl/base", + "//absl/random/internal:distribution_test_util", + "//absl/random/internal:sequence_urbg", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "zipf_distribution_test", + size = "medium", + srcs = [ + "zipf_distribution_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + ":random", + "//absl/base", + "//absl/random/internal:distribution_test_util", + "//absl/random/internal:sequence_urbg", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "examples_test", + size = "small", + srcs = ["examples_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":random", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "seed_sequences_test", + size = "small", + srcs = ["seed_sequences_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":random", + ":seed_sequences", + "//absl/random/internal:nonsecure_base", + "@com_google_googletest//:gtest_main", + ], +) + +BENCHMARK_TAGS = [ + "benchmark", + "no_test_android_arm", + "no_test_android_arm64", + "no_test_android_x86", + "no_test_darwin_x86_64", + "no_test_ios_x86_64", + "no_test_loonix", + "no_test_msvc_x64", + "no_test_wasm", +] + +# Benchmarks for various methods / test utilities +cc_binary( + name = "benchmarks", + testonly = 1, + srcs = [ + "benchmarks.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = BENCHMARK_TAGS, + deps = [ + ":distributions", + ":random", + ":seed_sequences", + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/random/internal:fast_uniform_bits", + "//absl/random/internal:randen_engine", + "@com_github_google_benchmark//:benchmark_main", + ], +) diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt new file mode 100644 index 00000000..2d5c0658 --- /dev/null +++ b/absl/random/CMakeLists.txt @@ -0,0 +1,1034 @@ +# +# Copyright 2019 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +absl_cc_library( + NAME + random_random + HDRS + "random.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_distributions + absl::random_internal_nonsecure_base + absl::random_internal_pcg_engine + absl::random_internal_pool_urbg + absl::random_internal_randen_engine + absl::random_seed_sequences +) + +absl_cc_library( + NAME + random_distributions + SRCS + "discrete_distribution.cc" + "gaussian_distribution.cc" + HDRS + "bernoulli_distribution.h" + "beta_distribution.h" + "discrete_distribution.h" + "distribution_format_traits.h" + "distributions.h" + "exponential_distribution.h" + "gaussian_distribution.h" + "log_uniform_int_distribution.h" + "poisson_distribution.h" + "uniform_int_distribution.h" + "uniform_real_distribution.h" + "zipf_distribution.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base_internal + absl::core_headers + absl::random_internal_distribution_impl + absl::random_internal_distributions + absl::random_internal_fast_uniform_bits + absl::random_internal_fastmath + absl::random_internal_iostream_state_saver + absl::random_internal_traits + absl::random_internal_uniform_helper + absl::strings + absl::span + absl::type_traits +) + +absl_cc_library( + NAME + random_seed_gen_exception + SRCS + "seed_gen_exception.cc" + HDRS + "seed_gen_exception.h" + COPTS + ${ABSL_DEFAULT_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config +) + +absl_cc_library( + NAME + random_seed_sequences + SRCS + "seed_sequences.cc" + HDRS + "seed_sequences.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::inlined_vector + absl::random_internal_nonsecure_base + absl::random_internal_pool_urbg + absl::random_internal_salted_seed_seq + absl::random_internal_seed_material + absl::random_seed_gen_exception + absl::span +) + +absl_cc_test( + NAME + random_bernoulli_distribution_test + SRCS + "bernoulli_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_distributions + absl::random_random + absl::random_internal_sequence_urbg + gmock + gtest_main +) + +absl_cc_test( + NAME + random_beta_distribution_test + SRCS + "beta_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_distributions + absl::random_random + absl::base + absl::random_internal_distribution_test_util + absl::random_internal_sequence_urbg + absl::strings + absl::str_format + gmock + gtest_main +) + +absl_cc_test( + NAME + random_distributions_test + SRCS + "distributions_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_distributions + absl::random_random + absl::random_internal_distribution_test_util + gmock + gtest_main +) + +absl_cc_test( + NAME + random_generators_test + SRCS + "generators_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + absl::random_distributions + absl::random_random + gmock + gtest_main +) + +absl_cc_test( + NAME + random_log_uniform_int_distribution_test + SRCS + "log_uniform_int_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + absl::base + absl::core_headers + absl::random_distributions + absl::random_internal_distribution_test_util + absl::random_internal_sequence_urbg + absl::random_random + absl::strings + absl::str_format + gmock + gtest_main +) + +absl_cc_test( + NAME + random_discrete_distribution_test + SRCS + "discrete_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::random_distributions + absl::random_internal_distribution_test_util + absl::random_internal_sequence_urbg + absl::random_random + absl::strings + gmock + gtest_main +) + +absl_cc_test( + NAME + random_poisson_distribution_test + SRCS + "poisson_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_distributions + absl::random_random + absl::base + absl::core_headers + absl::flat_hash_map + absl::random_internal_distribution_test_util + absl::random_internal_sequence_urbg + absl::strings + absl::str_format + gmock + gtest_main +) + +absl_cc_test( + NAME + random_exponential_distribution_test + SRCS + "exponential_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::core_headers + absl::random_distributions + absl::random_internal_distribution_test_util + absl::random_internal_sequence_urbg + absl::random_random + absl::strings + absl::str_format + gmock + gtest_main +) + +absl_cc_test( + NAME + random_gaussian_distribution_test + SRCS + "gaussian_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::core_headers + absl::random_distributions + absl::random_internal_distribution_test_util + absl::random_internal_sequence_urbg + absl::random_random + absl::strings + absl::str_format + gmock + gtest_main +) + +absl_cc_test( + NAME + random_uniform_int_distribution_test + SRCS + "uniform_int_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::random_distributions + absl::random_internal_distribution_test_util + absl::random_internal_sequence_urbg + absl::random_random + absl::strings + gmock + gtest_main +) + +absl_cc_test( + NAME + random_uniform_real_distribution_test + SRCS + "uniform_real_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::random_distributions + absl::random_internal_distribution_test_util + absl::random_internal_sequence_urbg + absl::random_random + absl::strings + gmock + gtest_main +) + +absl_cc_test( + NAME + random_zipf_distribution_test + SRCS + "zipf_distribution_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::random_distributions + absl::random_internal_distribution_test_util + absl::random_internal_sequence_urbg + absl::random_random + absl::strings + gmock + gtest_main +) + +absl_cc_test( + NAME + random_examples_test + SRCS + "examples_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_random + gtest_main +) + +absl_cc_test( + NAME + random_seed_sequences_test + SRCS + "seed_sequences_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_seed_sequences + absl::random_internal_nonsecure_base + absl::random_random + gmock + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_traits + HDRS + "internal/traits.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_distribution_caller + HDRS + "internal/distribution_caller.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_distributions + HDRS + "internal/distributions.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_distribution_caller + absl::random_internal_fast_uniform_bits + absl::random_internal_fastmath + absl::random_internal_traits + absl::random_internal_uniform_helper + absl::span + absl::strings + absl::type_traits +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_fast_uniform_bits + HDRS + "internal/fast_uniform_bits.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_seed_material + SRCS + "internal/seed_material.cc" + HDRS + "internal/seed_material.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::core_headers + absl::optional + absl::random_internal_fast_uniform_bits + absl::span + absl::strings +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_pool_urbg + SRCS + "internal/pool_urbg.cc" + HDRS + "internal/pool_urbg.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::config + absl::core_headers + absl::endian + absl::random_internal_randen + absl::random_internal_seed_material + absl::random_internal_traits + absl::random_seed_gen_exception + absl::span +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_explicit_seed_seq + HDRS + "internal/random_internal_explicit_seed_seq.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + TESTONLY +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_sequence_urbg + HDRS + "internal/sequence_urbg.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + TESTONLY +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_salted_seed_seq + HDRS + "internal/salted_seed_seq.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::inlined_vector + absl::optional + absl::span + absl::random_internal_seed_material + absl::type_traits +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_iostream_state_saver + HDRS + "internal/iostream_state_saver.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::int128 + absl::type_traits +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_distribution_impl + HDRS + "internal/distribution_impl.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::bits + absl::config + absl::int128 + absl::random_internal_fastmath + absl::random_internal_traits +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_fastmath + HDRS + "internal/fastmath.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::bits +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_nonsecure_base + HDRS + "internal/nonsecure_base.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::core_headers + absl::optional + absl::random_internal_pool_urbg + absl::random_internal_salted_seed_seq + absl::random_internal_seed_material + absl::span + absl::strings + absl::type_traits +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_pcg_engine + HDRS + "internal/pcg_engine.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config + absl::int128 + absl::random_internal_fastmath + absl::random_internal_iostream_state_saver + absl::type_traits +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_randen_engine + HDRS + "internal/randen_engine.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_iostream_state_saver + absl::random_internal_randen + absl::type_traits +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_platform + HDRS + "internal/randen_traits.h" + "internal/randen-keys.inc" + "internal/platform.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_randen + SRCS + "internal/randen.cc" + HDRS + "internal/randen.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::random_internal_platform + absl::random_internal_randen_hwaes + absl::random_internal_randen_slow +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_randen_slow + SRCS + "internal/randen_slow.cc" + HDRS + "internal/randen_slow.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_platform +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_randen_hwaes + SRCS + "internal/randen_detect.cc" + HDRS + "internal/randen_detect.h" + "internal/randen_hwaes.h" + COPTS + ${ABSL_DEFAULT_COPTS} + ${ABSL_RANDOM_RANDEN_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_platform + absl::random_internal_randen_hwaes_impl +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_randen_hwaes_impl + SRCS + "internal/randen_hwaes.cc" + "internal/randen_hwaes.h" + COPTS + ${ABSL_DEFAULT_COPTS} + ${ABSL_RANDOM_RANDEN_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_platform +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_distribution_test_util + SRCS + "internal/chi_square.cc" + "internal/distribution_test_util.cc" + HDRS + "internal/chi_square.h" + "internal/distribution_test_util.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::core_headers + absl::strings + absl::str_format + absl::span +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_traits_test + SRCS + "internal/traits_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_traits + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_distribution_impl_test + SRCS + "internal/distribution_impl_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::bits + absl::flags + absl::int128 + absl::random_internal_distribution_impl + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_distribution_test_util_test + SRCS + "internal/distribution_test_util_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_distribution_test_util + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_fastmath_test + SRCS + "internal/fastmath_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_fastmath + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_explicit_seed_seq_test + SRCS + "internal/explicit_seed_seq_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_explicit_seed_seq + absl::random_seed_sequences + gmock + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_salted_seed_seq_test + SRCS + "internal/salted_seed_seq_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_salted_seed_seq + gmock + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_chi_square_test + SRCS + "internal/chi_square_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::core_headers + absl::random_internal_distribution_test_util + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_fast_uniform_bits_test + SRCS + "internal/fast_uniform_bits_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_fast_uniform_bits + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_nonsecure_base_test + SRCS + "internal/nonsecure_base_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_nonsecure_base + absl::random_random + absl::random_distributions + absl::random_seed_sequences + absl::strings + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_seed_material_test + SRCS + "internal/seed_material_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_seed_material + gmock + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_pool_urbg_test + SRCS + "internal/pool_urbg_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_pool_urbg + absl::span + absl::type_traits + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_pcg_engine_test + SRCS + "internal/pcg_engine_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_explicit_seed_seq + absl::random_internal_pcg_engine + absl::time + gmock + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_randen_engine_test + SRCS + "internal/randen_engine_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::random_internal_explicit_seed_seq + absl::random_internal_randen_engine + absl::strings + absl::time + gmock + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_randen_test + SRCS + "internal/randen_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_randen + absl::type_traits + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_randen_slow_test + SRCS + "internal/randen_slow_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_randen_slow + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_randen_hwaes_test + SRCS + "internal/randen_hwaes_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_platform + absl::random_internal_randen_hwaes + absl::random_internal_randen_hwaes_impl + absl::base + absl::str_format + gmock + gtest +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_uniform_helper + HDRS + "internal/uniform_helper.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::core_headers + absl::random_internal_distribution_impl + absl::random_internal_fast_uniform_bits + absl::random_internal_iostream_state_saver + absl::random_internal_traits + absl::type_traits +) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_iostream_state_saver_test + SRCS + "internal/iostream_state_saver_test.cc" + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_iostream_state_saver + gtest_main +) diff --git a/absl/random/benchmarks.cc b/absl/random/benchmarks.cc new file mode 100644 index 00000000..87bbb981 --- /dev/null +++ b/absl/random/benchmarks.cc @@ -0,0 +1,383 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Benchmarks for absl random distributions as well as a selection of the +// C++ standard library random distributions. + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <initializer_list> +#include <iterator> +#include <limits> +#include <random> +#include <type_traits> +#include <vector> + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" +#include "absl/random/bernoulli_distribution.h" +#include "absl/random/beta_distribution.h" +#include "absl/random/exponential_distribution.h" +#include "absl/random/gaussian_distribution.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/randen_engine.h" +#include "absl/random/log_uniform_int_distribution.h" +#include "absl/random/poisson_distribution.h" +#include "absl/random/random.h" +#include "absl/random/uniform_int_distribution.h" +#include "absl/random/uniform_real_distribution.h" +#include "absl/random/zipf_distribution.h" +#include "benchmark/benchmark.h" + +namespace { + +// Seed data to avoid reading random_device() for benchmarks. +uint32_t kSeedData[] = { + 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, + 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, + 0xFF34052E, 0xC5855664, 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A, + 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, + 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, 0x699A18FF, 0x5664526C, 0xC2B19EE1, + 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65, + 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, + 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, + 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0x13198A2E, 0x03707344, +}; + +// PrecompiledSeedSeq provides kSeedData to a conforming +// random engine to speed initialization in the benchmarks. +class PrecompiledSeedSeq { + public: + using result_type = uint32_t; + + PrecompiledSeedSeq() {} + + template <typename Iterator> + PrecompiledSeedSeq(Iterator begin, Iterator end) {} + + template <typename T> + PrecompiledSeedSeq(std::initializer_list<T> il) {} + + template <typename OutIterator> + void generate(OutIterator begin, OutIterator end) { + static size_t idx = 0; + for (; begin != end; begin++) { + *begin = kSeedData[idx++]; + if (idx >= ABSL_ARRAYSIZE(kSeedData)) { + idx = 0; + } + } + } + + size_t size() const { return ABSL_ARRAYSIZE(kSeedData); } + + template <typename OutIterator> + void param(OutIterator out) const { + std::copy(std::begin(kSeedData), std::end(kSeedData), out); + } +}; + +// use_default_initialization<T> indicates whether the random engine +// T must be default initialized, or whether we may initialize it using +// a seed sequence. This is used because some engines do not accept seed +// sequence-based initialization. +template <typename E> +using use_default_initialization = std::false_type; + +// make_engine<T, SSeq> returns a random_engine which is initialized, +// either via the default constructor, when use_default_initialization<T> +// is true, or via the indicated seed sequence, SSeq. +template <typename Engine, typename SSeq = PrecompiledSeedSeq> +typename absl::enable_if_t<!use_default_initialization<Engine>::value, Engine> +make_engine() { + // Initialize the random engine using the seed sequence SSeq, which + // is constructed from the precompiled seed data. + SSeq seq(std::begin(kSeedData), std::end(kSeedData)); + return Engine(seq); +} + +template <typename Engine, typename SSeq = PrecompiledSeedSeq> +typename absl::enable_if_t<use_default_initialization<Engine>::value, Engine> +make_engine() { + // Initialize the random engine using the default constructor. + return Engine(); +} + +template <typename Engine, typename SSeq> +void BM_Construct(benchmark::State& state) { + for (auto _ : state) { + auto rng = make_engine<Engine, SSeq>(); + benchmark::DoNotOptimize(rng()); + } +} + +template <typename Engine> +void BM_Direct(benchmark::State& state) { + using value_type = typename Engine::result_type; + // Direct use of the URBG. + auto rng = make_engine<Engine>(); + for (auto _ : state) { + benchmark::DoNotOptimize(rng()); + } + state.SetBytesProcessed(sizeof(value_type) * state.iterations()); +} + +template <typename Engine> +void BM_Generate(benchmark::State& state) { + // std::generate makes a copy of the RNG; thus this tests the + // copy-constructor efficiency. + using value_type = typename Engine::result_type; + std::vector<value_type> v(64); + auto rng = make_engine<Engine>(); + while (state.KeepRunningBatch(64)) { + std::generate(std::begin(v), std::end(v), rng); + } +} + +template <typename Engine, size_t elems> +void BM_Shuffle(benchmark::State& state) { + // Direct use of the Engine. + std::vector<uint32_t> v(elems); + while (state.KeepRunningBatch(elems)) { + auto rng = make_engine<Engine>(); + std::shuffle(std::begin(v), std::end(v), rng); + } +} + +template <typename Engine, size_t elems> +void BM_ShuffleReuse(benchmark::State& state) { + // Direct use of the Engine. + std::vector<uint32_t> v(elems); + auto rng = make_engine<Engine>(); + while (state.KeepRunningBatch(elems)) { + std::shuffle(std::begin(v), std::end(v), rng); + } +} + +template <typename Engine, typename Dist, typename... Args> +void BM_Dist(benchmark::State& state, Args&&... args) { + using value_type = typename Dist::result_type; + auto rng = make_engine<Engine>(); + Dist dis{std::forward<Args>(args)...}; + // Compare the following loop performance: + for (auto _ : state) { + benchmark::DoNotOptimize(dis(rng)); + } + state.SetBytesProcessed(sizeof(value_type) * state.iterations()); +} + +template <typename Engine, typename Dist> +void BM_Large(benchmark::State& state) { + using value_type = typename Dist::result_type; + volatile value_type kMin = 0; + volatile value_type kMax = std::numeric_limits<value_type>::max() / 2 + 1; + BM_Dist<Engine, Dist>(state, kMin, kMax); +} + +template <typename Engine, typename Dist> +void BM_Small(benchmark::State& state) { + using value_type = typename Dist::result_type; + volatile value_type kMin = 0; + volatile value_type kMax = std::numeric_limits<value_type>::max() / 64 + 1; + BM_Dist<Engine, Dist>(state, kMin, kMax); +} + +template <typename Engine, typename Dist, int A> +void BM_Bernoulli(benchmark::State& state) { + volatile double a = static_cast<double>(A) / 1000000; + BM_Dist<Engine, Dist>(state, a); +} + +template <typename Engine, typename Dist, int A, int B> +void BM_Beta(benchmark::State& state) { + using value_type = typename Dist::result_type; + volatile value_type a = static_cast<value_type>(A) / 100; + volatile value_type b = static_cast<value_type>(B) / 100; + BM_Dist<Engine, Dist>(state, a, b); +} + +template <typename Engine, typename Dist, int A> +void BM_Gamma(benchmark::State& state) { + using value_type = typename Dist::result_type; + volatile value_type a = static_cast<value_type>(A) / 100; + BM_Dist<Engine, Dist>(state, a); +} + +template <typename Engine, typename Dist, int A = 100> +void BM_Poisson(benchmark::State& state) { + volatile double a = static_cast<double>(A) / 100; + BM_Dist<Engine, Dist>(state, a); +} + +template <typename Engine, typename Dist, int Q = 2, int V = 1> +void BM_Zipf(benchmark::State& state) { + using value_type = typename Dist::result_type; + volatile double q = Q; + volatile double v = V; + BM_Dist<Engine, Dist>(state, std::numeric_limits<value_type>::max(), q, v); +} + +template <typename Engine, typename Dist> +void BM_Thread(benchmark::State& state) { + using value_type = typename Dist::result_type; + auto rng = make_engine<Engine>(); + Dist dis{}; + for (auto _ : state) { + benchmark::DoNotOptimize(dis(rng)); + } + state.SetBytesProcessed(sizeof(value_type) * state.iterations()); +} + +// NOTES: +// +// std::geometric_distribution is similar to the zipf distributions. +// The algorithm for the geometric_distribution is, basically, +// floor(log(1-X) / log(1-p)) + +// Normal benchmark suite +#define BM_BASIC(Engine) \ + BENCHMARK_TEMPLATE(BM_Construct, Engine, PrecompiledSeedSeq); \ + BENCHMARK_TEMPLATE(BM_Construct, Engine, std::seed_seq); \ + BENCHMARK_TEMPLATE(BM_Direct, Engine); \ + BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 10); \ + BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 100); \ + BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 1000); \ + BENCHMARK_TEMPLATE(BM_ShuffleReuse, Engine, 100); \ + BENCHMARK_TEMPLATE(BM_ShuffleReuse, Engine, 1000); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, \ + absl::random_internal::FastUniformBits<uint32_t>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, \ + absl::random_internal::FastUniformBits<uint64_t>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, std::uniform_int_distribution<int32_t>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, std::uniform_int_distribution<int64_t>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, \ + absl::uniform_int_distribution<int32_t>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, \ + absl::uniform_int_distribution<int64_t>); \ + BENCHMARK_TEMPLATE(BM_Large, Engine, \ + std::uniform_int_distribution<int32_t>); \ + BENCHMARK_TEMPLATE(BM_Large, Engine, \ + std::uniform_int_distribution<int64_t>); \ + BENCHMARK_TEMPLATE(BM_Large, Engine, \ + absl::uniform_int_distribution<int32_t>); \ + BENCHMARK_TEMPLATE(BM_Large, Engine, \ + absl::uniform_int_distribution<int64_t>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, std::uniform_real_distribution<float>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, std::uniform_real_distribution<double>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, absl::uniform_real_distribution<float>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, absl::uniform_real_distribution<double>) + +#define BM_COPY(Engine) BENCHMARK_TEMPLATE(BM_Generate, Engine) + +#define BM_THREAD(Engine) \ + BENCHMARK_TEMPLATE(BM_Thread, Engine, \ + absl::uniform_int_distribution<int64_t>) \ + ->ThreadPerCpu(); \ + BENCHMARK_TEMPLATE(BM_Thread, Engine, \ + absl::uniform_real_distribution<double>) \ + ->ThreadPerCpu(); \ + BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 100)->ThreadPerCpu(); \ + BENCHMARK_TEMPLATE(BM_Shuffle, Engine, 1000)->ThreadPerCpu(); \ + BENCHMARK_TEMPLATE(BM_ShuffleReuse, Engine, 100)->ThreadPerCpu(); \ + BENCHMARK_TEMPLATE(BM_ShuffleReuse, Engine, 1000)->ThreadPerCpu(); + +#define BM_EXTENDED(Engine) \ + /* -------------- Extended Uniform -----------------------*/ \ + BENCHMARK_TEMPLATE(BM_Small, Engine, \ + std::uniform_int_distribution<int32_t>); \ + BENCHMARK_TEMPLATE(BM_Small, Engine, \ + std::uniform_int_distribution<int64_t>); \ + BENCHMARK_TEMPLATE(BM_Small, Engine, \ + absl::uniform_int_distribution<int32_t>); \ + BENCHMARK_TEMPLATE(BM_Small, Engine, \ + absl::uniform_int_distribution<int64_t>); \ + BENCHMARK_TEMPLATE(BM_Small, Engine, std::uniform_real_distribution<float>); \ + BENCHMARK_TEMPLATE(BM_Small, Engine, \ + std::uniform_real_distribution<double>); \ + BENCHMARK_TEMPLATE(BM_Small, Engine, \ + absl::uniform_real_distribution<float>); \ + BENCHMARK_TEMPLATE(BM_Small, Engine, \ + absl::uniform_real_distribution<double>); \ + /* -------------- Other -----------------------*/ \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, std::normal_distribution<double>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, absl::gaussian_distribution<double>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, std::exponential_distribution<double>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, absl::exponential_distribution<double>); \ + BENCHMARK_TEMPLATE(BM_Poisson, Engine, std::poisson_distribution<int64_t>, \ + 100); \ + BENCHMARK_TEMPLATE(BM_Poisson, Engine, absl::poisson_distribution<int64_t>, \ + 100); \ + BENCHMARK_TEMPLATE(BM_Poisson, Engine, std::poisson_distribution<int64_t>, \ + 10 * 100); \ + BENCHMARK_TEMPLATE(BM_Poisson, Engine, absl::poisson_distribution<int64_t>, \ + 10 * 100); \ + BENCHMARK_TEMPLATE(BM_Poisson, Engine, std::poisson_distribution<int64_t>, \ + 13 * 100); \ + BENCHMARK_TEMPLATE(BM_Poisson, Engine, absl::poisson_distribution<int64_t>, \ + 13 * 100); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, \ + absl::log_uniform_int_distribution<int32_t>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, \ + absl::log_uniform_int_distribution<int64_t>); \ + BENCHMARK_TEMPLATE(BM_Dist, Engine, std::geometric_distribution<int64_t>); \ + BENCHMARK_TEMPLATE(BM_Zipf, Engine, absl::zipf_distribution<uint64_t>); \ + BENCHMARK_TEMPLATE(BM_Zipf, Engine, absl::zipf_distribution<uint64_t>, 2, \ + 3); \ + BENCHMARK_TEMPLATE(BM_Bernoulli, Engine, std::bernoulli_distribution, \ + 257305); \ + BENCHMARK_TEMPLATE(BM_Bernoulli, Engine, absl::bernoulli_distribution, \ + 257305); \ + BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<double>, 65, \ + 41); \ + BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<double>, 99, \ + 330); \ + BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<double>, 150, \ + 150); \ + BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<double>, 410, \ + 580); \ + BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<float>, 65, 41); \ + BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<float>, 99, \ + 330); \ + BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<float>, 150, \ + 150); \ + BENCHMARK_TEMPLATE(BM_Beta, Engine, absl::beta_distribution<float>, 410, \ + 580); \ + BENCHMARK_TEMPLATE(BM_Gamma, Engine, std::gamma_distribution<float>, 199); \ + BENCHMARK_TEMPLATE(BM_Gamma, Engine, std::gamma_distribution<double>, 199); + +// ABSL Recommended interfaces. +BM_BASIC(absl::InsecureBitGen); // === pcg64_2018_engine +BM_BASIC(absl::BitGen); // === randen_engine<uint64_t>. +BM_THREAD(absl::BitGen); +BM_EXTENDED(absl::BitGen); + +// Instantiate benchmarks for multiple engines. +using randen_engine_64 = absl::random_internal::randen_engine<uint64_t>; +using randen_engine_32 = absl::random_internal::randen_engine<uint32_t>; + +// Comparison interfaces. +BM_BASIC(std::mt19937_64); +BM_COPY(std::mt19937_64); +BM_EXTENDED(std::mt19937_64); +BM_BASIC(randen_engine_64); +BM_COPY(randen_engine_64); +BM_EXTENDED(randen_engine_64); + +BM_BASIC(std::mt19937); +BM_COPY(std::mt19937); +BM_BASIC(randen_engine_32); +BM_COPY(randen_engine_32); + +} // namespace diff --git a/absl/random/bernoulli_distribution.h b/absl/random/bernoulli_distribution.h new file mode 100644 index 00000000..0afc2c14 --- /dev/null +++ b/absl/random/bernoulli_distribution.h @@ -0,0 +1,200 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_ +#define ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_ + +#include <cstdint> +#include <istream> +#include <limits> + +#include "absl/base/optimization.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/iostream_state_saver.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// absl::bernoulli_distribution is a drop in replacement for +// std::bernoulli_distribution. It guarantees that (given a perfect +// UniformRandomBitGenerator) the acceptance probability is *exactly* equal to +// the given double. +// +// The implementation assumes that double is IEEE754 +class bernoulli_distribution { + public: + using result_type = bool; + + class param_type { + public: + using distribution_type = bernoulli_distribution; + + explicit param_type(double p = 0.5) : prob_(p) { + assert(p >= 0.0 && p <= 1.0); + } + + double p() const { return prob_; } + + friend bool operator==(const param_type& p1, const param_type& p2) { + return p1.p() == p2.p(); + } + friend bool operator!=(const param_type& p1, const param_type& p2) { + return p1.p() != p2.p(); + } + + private: + double prob_; + }; + + bernoulli_distribution() : bernoulli_distribution(0.5) {} + + explicit bernoulli_distribution(double p) : param_(p) {} + + explicit bernoulli_distribution(param_type p) : param_(p) {} + + // no-op + void reset() {} + + template <typename URBG> + bool operator()(URBG& g) { // NOLINT(runtime/references) + return Generate(param_.p(), g); + } + + template <typename URBG> + bool operator()(URBG& g, // NOLINT(runtime/references) + const param_type& param) { + return Generate(param.p(), g); + } + + param_type param() const { return param_; } + void param(const param_type& param) { param_ = param; } + + double p() const { return param_.p(); } + + result_type(min)() const { return false; } + result_type(max)() const { return true; } + + friend bool operator==(const bernoulli_distribution& d1, + const bernoulli_distribution& d2) { + return d1.param_ == d2.param_; + } + + friend bool operator!=(const bernoulli_distribution& d1, + const bernoulli_distribution& d2) { + return d1.param_ != d2.param_; + } + + private: + static constexpr uint64_t kP32 = static_cast<uint64_t>(1) << 32; + + template <typename URBG> + static bool Generate(double p, URBG& g); // NOLINT(runtime/references) + + param_type param_; +}; + +template <typename CharT, typename Traits> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const bernoulli_distribution& x) { + auto saver = random_internal::make_ostream_state_saver(os); + os.precision(random_internal::stream_precision_helper<double>::kPrecision); + os << x.p(); + return os; +} + +template <typename CharT, typename Traits> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + bernoulli_distribution& x) { // NOLINT(runtime/references) + auto saver = random_internal::make_istream_state_saver(is); + auto p = random_internal::read_floating_point<double>(is); + if (!is.fail()) { + x.param(bernoulli_distribution::param_type(p)); + } + return is; +} + +template <typename URBG> +bool bernoulli_distribution::Generate(double p, + URBG& g) { // NOLINT(runtime/references) + random_internal::FastUniformBits<uint32_t> fast_u32; + + while (true) { + // There are two aspects of the definition of `c` below that are worth + // commenting on. First, because `p` is in the range [0, 1], `c` is in the + // range [0, 2^32] which does not fit in a uint32_t and therefore requires + // 64 bits. + // + // Second, `c` is constructed by first casting explicitly to a signed + // integer and then converting implicitly to an unsigned integer of the same + // size. This is done because the hardware conversion instructions produce + // signed integers from double; if taken as a uint64_t the conversion would + // be wrong for doubles greater than 2^63 (not relevant in this use-case). + // If converted directly to an unsigned integer, the compiler would end up + // emitting code to handle such large values that are not relevant due to + // the known bounds on `c`. To avoid these extra instructions this + // implementation converts first to the signed type and then use the + // implicit conversion to unsigned (which is a no-op). + const uint64_t c = static_cast<int64_t>(p * kP32); + const uint32_t v = fast_u32(g); + // FAST PATH: this path fails with probability 1/2^32. Note that simply + // returning v <= c would approximate P very well (up to an absolute error + // of 1/2^32); the slow path (taken in that range of possible error, in the + // case of equality) eliminates the remaining error. + if (ABSL_PREDICT_TRUE(v != c)) return v < c; + + // It is guaranteed that `q` is strictly less than 1, because if `q` were + // greater than or equal to 1, the same would be true for `p`. Certainly `p` + // cannot be greater than 1, and if `p == 1`, then the fast path would + // necessary have been taken already. + const double q = static_cast<double>(c) / kP32; + + // The probability of acceptance on the fast path is `q` and so the + // probability of acceptance here should be `p - q`. + // + // Note that `q` is obtained from `p` via some shifts and conversions, the + // upshot of which is that `q` is simply `p` with some of the + // least-significant bits of its mantissa set to zero. This means that the + // difference `p - q` will not have any rounding errors. To see why, pretend + // that double has 10 bits of resolution and q is obtained from `p` in such + // a way that the 4 least-significant bits of its mantissa are set to zero. + // For example: + // p = 1.1100111011 * 2^-1 + // q = 1.1100110000 * 2^-1 + // p - q = 1.011 * 2^-8 + // The difference `p - q` has exactly the nonzero mantissa bits that were + // "lost" in `q` producing a number which is certainly representable in a + // double. + const double left = p - q; + + // By construction, the probability of being on this slow path is 1/2^32, so + // P(accept in slow path) = P(accept| in slow path) * P(slow path), + // which means the probability of acceptance here is `1 / (left * kP32)`: + const double here = left * kP32; + + // The simplest way to compute the result of this trial is to repeat the + // whole algorithm with the new probability. This terminates because even + // given arbitrarily unfriendly "random" bits, each iteration either + // multiplies a tiny probability by 2^32 (if c == 0) or strips off some + // number of nonzero mantissa bits. That process is bounded. + if (here == 0) return false; + p = here; + } +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_ diff --git a/absl/random/bernoulli_distribution_test.cc b/absl/random/bernoulli_distribution_test.cc new file mode 100644 index 00000000..f2c3b99c --- /dev/null +++ b/absl/random/bernoulli_distribution_test.cc @@ -0,0 +1,213 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/bernoulli_distribution.h" + +#include <cmath> +#include <cstddef> +#include <random> +#include <sstream> +#include <utility> + +#include "gtest/gtest.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" + +namespace { + +class BernoulliTest : public testing::TestWithParam<std::pair<double, size_t>> { +}; + +TEST_P(BernoulliTest, Serialize) { + const double d = GetParam().first; + absl::bernoulli_distribution before(d); + + { + absl::bernoulli_distribution via_param{ + absl::bernoulli_distribution::param_type(d)}; + EXPECT_EQ(via_param, before); + } + + std::stringstream ss; + ss << before; + absl::bernoulli_distribution after(0.6789); + + EXPECT_NE(before.p(), after.p()); + EXPECT_NE(before.param(), after.param()); + EXPECT_NE(before, after); + + ss >> after; + + EXPECT_EQ(before.p(), after.p()); + EXPECT_EQ(before.param(), after.param()); + EXPECT_EQ(before, after); +} + +TEST_P(BernoulliTest, Accuracy) { + // Sadly, the claim to fame for this implementation is precise accuracy, which + // is very, very hard to measure, the improvements come as trials approach the + // limit of double accuracy; thus the outcome differs from the + // std::bernoulli_distribution with a probability of approximately 1 in 2^-53. + const std::pair<double, size_t> para = GetParam(); + size_t trials = para.second; + double p = para.first; + + absl::InsecureBitGen rng; + + size_t yes = 0; + absl::bernoulli_distribution dist(p); + for (size_t i = 0; i < trials; ++i) { + if (dist(rng)) yes++; + } + + // Compute the distribution parameters for a binomial test, using a normal + // approximation for the confidence interval, as there are a sufficiently + // large number of trials that the central limit theorem applies. + const double stddev_p = std::sqrt((p * (1.0 - p)) / trials); + const double expected = trials * p; + const double stddev = trials * stddev_p; + + // 5 sigma, approved by Richard Feynman + EXPECT_NEAR(yes, expected, 5 * stddev) + << "@" << p << ", " + << std::abs(static_cast<double>(yes) - expected) / stddev << " stddev"; +} + +// There must be many more trials to make the mean approximately normal for `p` +// closes to 0 or 1. +INSTANTIATE_TEST_SUITE_P( + All, BernoulliTest, + ::testing::Values( + // Typical values. + std::make_pair(0, 30000), std::make_pair(1e-3, 30000000), + std::make_pair(0.1, 3000000), std::make_pair(0.5, 3000000), + std::make_pair(0.9, 30000000), std::make_pair(0.999, 30000000), + std::make_pair(1, 30000), + // Boundary cases. + std::make_pair(std::nextafter(1.0, 0.0), 1), // ~1 - epsilon + std::make_pair(std::numeric_limits<double>::epsilon(), 1), + std::make_pair(std::nextafter(std::numeric_limits<double>::min(), + 1.0), // min + epsilon + 1), + std::make_pair(std::numeric_limits<double>::min(), // smallest normal + 1), + std::make_pair( + std::numeric_limits<double>::denorm_min(), // smallest denorm + 1), + std::make_pair(std::numeric_limits<double>::min() / 2, 1), // denorm + std::make_pair(std::nextafter(std::numeric_limits<double>::min(), + 0.0), // denorm_max + 1))); + +// NOTE: absl::bernoulli_distribution is not guaranteed to be stable. +TEST(BernoulliTest, StabilityTest) { + // absl::bernoulli_distribution stability relies on FastUniformBits and + // integer arithmetic. + absl::random_internal::sequence_urbg urbg({ + 0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull, + 0x4864f22c059bf29eull, 0x247856d8b862665cull, 0xe46e86e9a1337e10ull, + 0xd8c8541f3519b133ull, 0xe75b5162c567b9e4ull, 0xf732e5ded7009c5bull, + 0xb170b98353121eacull, 0x1ec2e8986d2362caull, 0x814c8e35fe9a961aull, + 0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull, 0x1224e62c978bbc7full, + 0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull, 0x1bbc23cfa8fac721ull, + 0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull, 0x836d794457c08849ull, + 0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull, 0xb12d74fdd718c8c5ull, + 0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull, 0x5738341045ba0d85ull, + 0xe3fd722dc65ad09eull, 0x5a14fd21ea2a5705ull, 0x14e6ea4d6edb0c73ull, + 0x275b0dc7e0a18acfull, 0x36cebe0d2653682eull, 0x0361e9b23861596bull, + }); + + // Generate a std::string of '0' and '1' for the distribution output. + auto generate = [&urbg](absl::bernoulli_distribution& dist) { + std::string output; + output.reserve(36); + urbg.reset(); + for (int i = 0; i < 35; i++) { + output.append(dist(urbg) ? "1" : "0"); + } + return output; + }; + + const double kP = 0.0331289862362; + { + absl::bernoulli_distribution dist(kP); + auto v = generate(dist); + EXPECT_EQ(35, urbg.invocations()); + EXPECT_EQ(v, "00000000000010000000000010000000000") << dist; + } + { + absl::bernoulli_distribution dist(kP * 10.0); + auto v = generate(dist); + EXPECT_EQ(35, urbg.invocations()); + EXPECT_EQ(v, "00000100010010010010000011000011010") << dist; + } + { + absl::bernoulli_distribution dist(kP * 20.0); + auto v = generate(dist); + EXPECT_EQ(35, urbg.invocations()); + EXPECT_EQ(v, "00011110010110110011011111110111011") << dist; + } + { + absl::bernoulli_distribution dist(1.0 - kP); + auto v = generate(dist); + EXPECT_EQ(35, urbg.invocations()); + EXPECT_EQ(v, "11111111111111111111011111111111111") << dist; + } +} + +TEST(BernoulliTest, StabilityTest2) { + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + // Generate a std::string of '0' and '1' for the distribution output. + auto generate = [&urbg](absl::bernoulli_distribution& dist) { + std::string output; + output.reserve(13); + urbg.reset(); + for (int i = 0; i < 12; i++) { + output.append(dist(urbg) ? "1" : "0"); + } + return output; + }; + + constexpr double b0 = 1.0 / 13.0 / 0.2; + constexpr double b1 = 2.0 / 13.0 / 0.2; + constexpr double b3 = (5.0 / 13.0 / 0.2) - ((1 - b0) + (1 - b1) + (1 - b1)); + { + absl::bernoulli_distribution dist(b0); + auto v = generate(dist); + EXPECT_EQ(12, urbg.invocations()); + EXPECT_EQ(v, "000011100101") << dist; + } + { + absl::bernoulli_distribution dist(b1); + auto v = generate(dist); + EXPECT_EQ(12, urbg.invocations()); + EXPECT_EQ(v, "001111101101") << dist; + } + { + absl::bernoulli_distribution dist(b3); + auto v = generate(dist); + EXPECT_EQ(12, urbg.invocations()); + EXPECT_EQ(v, "001111101111") << dist; + } +} + +} // namespace diff --git a/absl/random/beta_distribution.h b/absl/random/beta_distribution.h new file mode 100644 index 00000000..ff1eba80 --- /dev/null +++ b/absl/random/beta_distribution.h @@ -0,0 +1,416 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_BETA_DISTRIBUTION_H_ +#define ABSL_RANDOM_BETA_DISTRIBUTION_H_ + +#include <cassert> +#include <cmath> +#include <istream> +#include <limits> +#include <ostream> +#include <type_traits> + +#include "absl/random/internal/distribution_impl.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/fastmath.h" +#include "absl/random/internal/iostream_state_saver.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// absl::beta_distribution: +// Generate a floating-point variate conforming to a Beta distribution: +// pdf(x) \propto x^(alpha-1) * (1-x)^(beta-1), +// where the params alpha and beta are both strictly positive real values. +// +// The support is the open interval (0, 1), but the return value might be equal +// to 0 or 1, due to numerical errors when alpha and beta are very different. +// +// Usage note: One usage is that alpha and beta are counts of number of +// successes and failures. When the total number of trials are large, consider +// approximating a beta distribution with a Gaussian distribution with the same +// mean and variance. One could use the skewness, which depends only on the +// smaller of alpha and beta when the number of trials are sufficiently large, +// to quantify how far a beta distribution is from the normal distribution. +template <typename RealType = double> +class beta_distribution { + public: + using result_type = RealType; + + class param_type { + public: + using distribution_type = beta_distribution; + + explicit param_type(result_type alpha, result_type beta) + : alpha_(alpha), beta_(beta) { + assert(alpha >= 0); + assert(beta >= 0); + assert(alpha <= (std::numeric_limits<result_type>::max)()); + assert(beta <= (std::numeric_limits<result_type>::max)()); + if (alpha == 0 || beta == 0) { + method_ = DEGENERATE_SMALL; + x_ = (alpha >= beta) ? 1 : 0; + return; + } + // a_ = min(beta, alpha), b_ = max(beta, alpha). + if (beta < alpha) { + inverted_ = true; + a_ = beta; + b_ = alpha; + } else { + inverted_ = false; + a_ = alpha; + b_ = beta; + } + if (a_ <= 1 && b_ >= ThresholdForLargeA()) { + method_ = DEGENERATE_SMALL; + x_ = inverted_ ? result_type(1) : result_type(0); + return; + } + // For threshold values, see also: + // Evaluation of Beta Generation Algorithms, Ying-Chao Hung, et. al. + // February, 2009. + if ((b_ < 1.0 && a_ + b_ <= 1.2) || a_ <= ThresholdForSmallA()) { + // Choose Joehnk over Cheng when it's faster or when Cheng encounters + // numerical issues. + method_ = JOEHNK; + a_ = result_type(1) / alpha_; + b_ = result_type(1) / beta_; + if (std::isinf(a_) || std::isinf(b_)) { + method_ = DEGENERATE_SMALL; + x_ = inverted_ ? result_type(1) : result_type(0); + } + return; + } + if (a_ >= ThresholdForLargeA()) { + method_ = DEGENERATE_LARGE; + // Note: on PPC for long double, evaluating + // `std::numeric_limits::max() / ThresholdForLargeA` results in NaN. + result_type r = a_ / b_; + x_ = (inverted_ ? result_type(1) : r) / (1 + r); + return; + } + x_ = a_ + b_; + log_x_ = std::log(x_); + if (a_ <= 1) { + method_ = CHENG_BA; + y_ = result_type(1) / a_; + gamma_ = a_ + a_; + return; + } + method_ = CHENG_BB; + result_type r = (a_ - 1) / (b_ - 1); + y_ = std::sqrt((1 + r) / (b_ * r * 2 - r + 1)); + gamma_ = a_ + result_type(1) / y_; + } + + result_type alpha() const { return alpha_; } + result_type beta() const { return beta_; } + + friend bool operator==(const param_type& a, const param_type& b) { + return a.alpha_ == b.alpha_ && a.beta_ == b.beta_; + } + + friend bool operator!=(const param_type& a, const param_type& b) { + return !(a == b); + } + + private: + friend class beta_distribution; + +#ifdef COMPILER_MSVC + // MSVC does not have constexpr implementations for std::log and std::exp + // so they are computed at runtime. +#define ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR +#else +#define ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR constexpr +#endif + + // The threshold for whether std::exp(1/a) is finite. + // Note that this value is quite large, and a smaller a_ is NOT abnormal. + static ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR result_type + ThresholdForSmallA() { + return result_type(1) / + std::log((std::numeric_limits<result_type>::max)()); + } + + // The threshold for whether a * std::log(a) is finite. + static ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR result_type + ThresholdForLargeA() { + return std::exp( + std::log((std::numeric_limits<result_type>::max)()) - + std::log(std::log((std::numeric_limits<result_type>::max)())) - + ThresholdPadding()); + } + +#undef ABSL_RANDOM_INTERNAL_LOG_EXP_CONSTEXPR + + // Pad the threshold for large A for long double on PPC. This is done via a + // template specialization below. + static constexpr result_type ThresholdPadding() { return 0; } + + enum Method { + JOEHNK, // Uses algorithm Joehnk + CHENG_BA, // Uses algorithm BA in Cheng + CHENG_BB, // Uses algorithm BB in Cheng + + // Note: See also: + // Hung et al. Evaluation of beta generation algorithms. Communications + // in Statistics-Simulation and Computation 38.4 (2009): 750-770. + // especially: + // Zechner, Heinz, and Ernst Stadlober. Generating beta variates via + // patchwork rejection. Computing 50.1 (1993): 1-18. + + DEGENERATE_SMALL, // a_ is abnormally small. + DEGENERATE_LARGE, // a_ is abnormally large. + }; + + result_type alpha_; + result_type beta_; + + result_type a_; // the smaller of {alpha, beta}, or 1.0/alpha_ in JOEHNK + result_type b_; // the larger of {alpha, beta}, or 1.0/beta_ in JOEHNK + result_type x_; // alpha + beta, or the result in degenerate cases + result_type log_x_; // log(x_) + result_type y_; // "beta" in Cheng + result_type gamma_; // "gamma" in Cheng + + Method method_; + + // Placing this last for optimal alignment. + // Whether alpha_ != a_, i.e. true iff alpha_ > beta_. + bool inverted_; + + static_assert(std::is_floating_point<RealType>::value, + "Class-template absl::beta_distribution<> must be " + "parameterized using a floating-point type."); + }; + + beta_distribution() : beta_distribution(1) {} + + explicit beta_distribution(result_type alpha, result_type beta = 1) + : param_(alpha, beta) {} + + explicit beta_distribution(const param_type& p) : param_(p) {} + + void reset() {} + + // Generating functions + template <typename URBG> + result_type operator()(URBG& g) { // NOLINT(runtime/references) + return (*this)(g, param_); + } + + template <typename URBG> + result_type operator()(URBG& g, // NOLINT(runtime/references) + const param_type& p); + + param_type param() const { return param_; } + void param(const param_type& p) { param_ = p; } + + result_type(min)() const { return 0; } + result_type(max)() const { return 1; } + + result_type alpha() const { return param_.alpha(); } + result_type beta() const { return param_.beta(); } + + friend bool operator==(const beta_distribution& a, + const beta_distribution& b) { + return a.param_ == b.param_; + } + friend bool operator!=(const beta_distribution& a, + const beta_distribution& b) { + return a.param_ != b.param_; + } + + private: + template <typename URBG> + result_type AlgorithmJoehnk(URBG& g, // NOLINT(runtime/references) + const param_type& p); + + template <typename URBG> + result_type AlgorithmCheng(URBG& g, // NOLINT(runtime/references) + const param_type& p); + + template <typename URBG> + result_type DegenerateCase(URBG& g, // NOLINT(runtime/references) + const param_type& p) { + if (p.method_ == param_type::DEGENERATE_SMALL && p.alpha_ == p.beta_) { + // Returns 0 or 1 with equal probability. + random_internal::FastUniformBits<uint8_t> fast_u8; + return static_cast<result_type>((fast_u8(g) & 0x10) != + 0); // pick any single bit. + } + return p.x_; + } + + param_type param_; + random_internal::FastUniformBits<uint64_t> fast_u64_; +}; + +#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ + defined(__ppc__) || defined(__PPC__) +// PPC needs a more stringent boundary for long double. +template <> +constexpr long double +beta_distribution<long double>::param_type::ThresholdPadding() { + return 10; +} +#endif + +template <typename RealType> +template <typename URBG> +typename beta_distribution<RealType>::result_type +beta_distribution<RealType>::AlgorithmJoehnk( + URBG& g, // NOLINT(runtime/references) + const param_type& p) { + // Based on Joehnk, M. D. Erzeugung von betaverteilten und gammaverteilten + // Zufallszahlen. Metrika 8.1 (1964): 5-15. + // This method is described in Knuth, Vol 2 (Third Edition), pp 134. + using RandU64ToReal = typename random_internal::RandU64ToReal<result_type>; + using random_internal::PositiveValueT; + result_type u, v, x, y, z; + for (;;) { + u = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g)); + v = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g)); + + // Direct method. std::pow is slow for float, so rely on the optimizer to + // remove the std::pow() path for that case. + if (!std::is_same<float, result_type>::value) { + x = std::pow(u, p.a_); + y = std::pow(v, p.b_); + z = x + y; + if (z > 1) { + // Reject if and only if `x + y > 1.0` + continue; + } + if (z > 0) { + // When both alpha and beta are small, x and y are both close to 0, so + // divide by (x+y) directly may result in nan. + return x / z; + } + } + + // Log transform. + // x = log( pow(u, p.a_) ), y = log( pow(v, p.b_) ) + // since u, v <= 1.0, x, y < 0. + x = std::log(u) * p.a_; + y = std::log(v) * p.b_; + if (!std::isfinite(x) || !std::isfinite(y)) { + continue; + } + // z = log( pow(u, a) + pow(v, b) ) + z = x > y ? (x + std::log(1 + std::exp(y - x))) + : (y + std::log(1 + std::exp(x - y))); + // Reject iff log(x+y) > 0. + if (z > 0) { + continue; + } + return std::exp(x - z); + } +} + +template <typename RealType> +template <typename URBG> +typename beta_distribution<RealType>::result_type +beta_distribution<RealType>::AlgorithmCheng( + URBG& g, // NOLINT(runtime/references) + const param_type& p) { + // Based on Cheng, Russell CH. Generating beta variates with nonintegral + // shape parameters. Communications of the ACM 21.4 (1978): 317-322. + // (https://dl.acm.org/citation.cfm?id=359482). + using RandU64ToReal = typename random_internal::RandU64ToReal<result_type>; + using random_internal::PositiveValueT; + + static constexpr result_type kLogFour = + result_type(1.3862943611198906188344642429163531361); // log(4) + static constexpr result_type kS = + result_type(2.6094379124341003746007593332261876); // 1+log(5) + + const bool use_algorithm_ba = (p.method_ == param_type::CHENG_BA); + result_type u1, u2, v, w, z, r, s, t, bw_inv, lhs; + for (;;) { + u1 = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g)); + u2 = RandU64ToReal::template Value<PositiveValueT, false>(fast_u64_(g)); + v = p.y_ * std::log(u1 / (1 - u1)); + w = p.a_ * std::exp(v); + bw_inv = result_type(1) / (p.b_ + w); + r = p.gamma_ * v - kLogFour; + s = p.a_ + r - w; + z = u1 * u1 * u2; + if (!use_algorithm_ba && s + kS >= 5 * z) { + break; + } + t = std::log(z); + if (!use_algorithm_ba && s >= t) { + break; + } + lhs = p.x_ * (p.log_x_ + std::log(bw_inv)) + r; + if (lhs >= t) { + break; + } + } + return p.inverted_ ? (1 - w * bw_inv) : w * bw_inv; +} + +template <typename RealType> +template <typename URBG> +typename beta_distribution<RealType>::result_type +beta_distribution<RealType>::operator()(URBG& g, // NOLINT(runtime/references) + const param_type& p) { + switch (p.method_) { + case param_type::JOEHNK: + return AlgorithmJoehnk(g, p); + case param_type::CHENG_BA: + ABSL_FALLTHROUGH_INTENDED; + case param_type::CHENG_BB: + return AlgorithmCheng(g, p); + default: + return DegenerateCase(g, p); + } +} + +template <typename CharT, typename Traits, typename RealType> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const beta_distribution<RealType>& x) { + auto saver = random_internal::make_ostream_state_saver(os); + os.precision(random_internal::stream_precision_helper<RealType>::kPrecision); + os << x.alpha() << os.fill() << x.beta(); + return os; +} + +template <typename CharT, typename Traits, typename RealType> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + beta_distribution<RealType>& x) { // NOLINT(runtime/references) + using result_type = typename beta_distribution<RealType>::result_type; + using param_type = typename beta_distribution<RealType>::param_type; + result_type alpha, beta; + + auto saver = random_internal::make_istream_state_saver(is); + alpha = random_internal::read_floating_point<result_type>(is); + if (is.fail()) return is; + beta = random_internal::read_floating_point<result_type>(is); + if (!is.fail()) { + x.param(param_type(alpha, beta)); + } + return is; +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_BETA_DISTRIBUTION_H_ diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc new file mode 100644 index 00000000..966ad08b --- /dev/null +++ b/absl/random/beta_distribution_test.cc @@ -0,0 +1,614 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/beta_distribution.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <random> +#include <sstream> +#include <string> +#include <unordered_map> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/chi_square.h" +#include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/strip.h" + +namespace { + +template <typename IntType> +class BetaDistributionInterfaceTest : public ::testing::Test {}; + +using RealTypes = ::testing::Types<float, double, long double>; +TYPED_TEST_CASE(BetaDistributionInterfaceTest, RealTypes); + +TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { + // The threshold for whether std::exp(1/a) is finite. + const TypeParam kSmallA = + 1.0f / std::log((std::numeric_limits<TypeParam>::max)()); + // The threshold for whether a * std::log(a) is finite. + const TypeParam kLargeA = + std::exp(std::log((std::numeric_limits<TypeParam>::max)()) - + std::log(std::log((std::numeric_limits<TypeParam>::max)()))); + const TypeParam kLargeAPPC = std::exp( + std::log((std::numeric_limits<TypeParam>::max)()) - + std::log(std::log((std::numeric_limits<TypeParam>::max)())) - 10.0f); + using param_type = typename absl::beta_distribution<TypeParam>::param_type; + + constexpr int kCount = 1000; + absl::InsecureBitGen gen; + const TypeParam kValues[] = { + TypeParam(1e-20), TypeParam(1e-12), TypeParam(1e-8), TypeParam(1e-4), + TypeParam(1e-3), TypeParam(0.1), TypeParam(0.25), + std::nextafter(TypeParam(0.5), TypeParam(0)), // 0.5 - epsilon + std::nextafter(TypeParam(0.5), TypeParam(1)), // 0.5 + epsilon + TypeParam(0.5), TypeParam(1.0), // + std::nextafter(TypeParam(1), TypeParam(0)), // 1 - epsilon + std::nextafter(TypeParam(1), TypeParam(2)), // 1 + epsilon + TypeParam(12.5), TypeParam(1e2), TypeParam(1e8), TypeParam(1e12), + TypeParam(1e20), // + kSmallA, // + std::nextafter(kSmallA, TypeParam(0)), // + std::nextafter(kSmallA, TypeParam(1)), // + kLargeA, // + std::nextafter(kLargeA, TypeParam(0)), // + std::nextafter(kLargeA, std::numeric_limits<TypeParam>::max()), + kLargeAPPC, // + std::nextafter(kLargeAPPC, TypeParam(0)), + std::nextafter(kLargeAPPC, std::numeric_limits<TypeParam>::max()), + // Boundary cases. + std::numeric_limits<TypeParam>::max(), + std::numeric_limits<TypeParam>::epsilon(), + std::nextafter(std::numeric_limits<TypeParam>::min(), + TypeParam(1)), // min + epsilon + std::numeric_limits<TypeParam>::min(), // smallest normal + std::numeric_limits<TypeParam>::denorm_min(), // smallest denorm + std::numeric_limits<TypeParam>::min() / 2, // denorm + std::nextafter(std::numeric_limits<TypeParam>::min(), + TypeParam(0)), // denorm_max + }; + for (TypeParam alpha : kValues) { + for (TypeParam beta : kValues) { + ABSL_INTERNAL_LOG( + INFO, absl::StrFormat("Smoke test for Beta(%f, %f)", alpha, beta)); + + param_type param(alpha, beta); + absl::beta_distribution<TypeParam> before(alpha, beta); + EXPECT_EQ(before.alpha(), param.alpha()); + EXPECT_EQ(before.beta(), param.beta()); + + { + absl::beta_distribution<TypeParam> via_param(param); + EXPECT_EQ(via_param, before); + EXPECT_EQ(via_param.param(), before.param()); + } + + // Smoke test. + for (int i = 0; i < kCount; ++i) { + auto sample = before(gen); + EXPECT_TRUE(std::isfinite(sample)); + EXPECT_GE(sample, before.min()); + EXPECT_LE(sample, before.max()); + } + + // Validate stream serialization. + std::stringstream ss; + ss << before; + absl::beta_distribution<TypeParam> after(3.8f, 1.43f); + EXPECT_NE(before.alpha(), after.alpha()); + EXPECT_NE(before.beta(), after.beta()); + EXPECT_NE(before.param(), after.param()); + EXPECT_NE(before, after); + + ss >> after; + +#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ + defined(__ppc__) || defined(__PPC__) + if (std::is_same<TypeParam, long double>::value) { + // Roundtripping floating point values requires sufficient precision + // to reconstruct the exact value. It turns out that long double + // has some errors doing this on ppc. + if (alpha <= std::numeric_limits<double>::max() && + alpha >= std::numeric_limits<double>::lowest()) { + EXPECT_EQ(static_cast<double>(before.alpha()), + static_cast<double>(after.alpha())) + << ss.str(); + } + if (beta <= std::numeric_limits<double>::max() && + beta >= std::numeric_limits<double>::lowest()) { + EXPECT_EQ(static_cast<double>(before.beta()), + static_cast<double>(after.beta())) + << ss.str(); + } + continue; + } +#endif + + EXPECT_EQ(before.alpha(), after.alpha()); + EXPECT_EQ(before.beta(), after.beta()); + EXPECT_EQ(before, after) // + << ss.str() << " " // + << (ss.good() ? "good " : "") // + << (ss.bad() ? "bad " : "") // + << (ss.eof() ? "eof " : "") // + << (ss.fail() ? "fail " : ""); + } + } +} + +TYPED_TEST(BetaDistributionInterfaceTest, DegenerateCases) { + // Extreme cases when the params are abnormal. + absl::InsecureBitGen gen; + constexpr int kCount = 1000; + const TypeParam kSmallValues[] = { + std::numeric_limits<TypeParam>::min(), + std::numeric_limits<TypeParam>::denorm_min(), + std::nextafter(std::numeric_limits<TypeParam>::min(), + TypeParam(0)), // denorm_max + std::numeric_limits<TypeParam>::epsilon(), + }; + const TypeParam kLargeValues[] = { + std::numeric_limits<TypeParam>::max() * static_cast<TypeParam>(0.9999), + std::numeric_limits<TypeParam>::max() - 1, + std::numeric_limits<TypeParam>::max(), + }; + { + // Small alpha and beta. + // Useful WolframAlpha plots: + // * plot InverseBetaRegularized[x, 0.0001, 0.0001] from 0.495 to 0.505 + // * Beta[1.0, 0.0000001, 0.0000001] + // * Beta[0.9999, 0.0000001, 0.0000001] + for (TypeParam alpha : kSmallValues) { + for (TypeParam beta : kSmallValues) { + int zeros = 0; + int ones = 0; + absl::beta_distribution<TypeParam> d(alpha, beta); + for (int i = 0; i < kCount; ++i) { + TypeParam x = d(gen); + if (x == 0.0) { + zeros++; + } else if (x == 1.0) { + ones++; + } + } + EXPECT_EQ(ones + zeros, kCount); + if (alpha == beta) { + EXPECT_NE(ones, 0); + EXPECT_NE(zeros, 0); + } + } + } + } + { + // Small alpha, large beta. + // Useful WolframAlpha plots: + // * plot InverseBetaRegularized[x, 0.0001, 10000] from 0.995 to 1 + // * Beta[0, 0.0000001, 1000000] + // * Beta[0.001, 0.0000001, 1000000] + // * Beta[1, 0.0000001, 1000000] + for (TypeParam alpha : kSmallValues) { + for (TypeParam beta : kLargeValues) { + absl::beta_distribution<TypeParam> d(alpha, beta); + for (int i = 0; i < kCount; ++i) { + EXPECT_EQ(d(gen), 0.0); + } + } + } + } + { + // Large alpha, small beta. + // Useful WolframAlpha plots: + // * plot InverseBetaRegularized[x, 10000, 0.0001] from 0 to 0.001 + // * Beta[0.99, 1000000, 0.0000001] + // * Beta[1, 1000000, 0.0000001] + for (TypeParam alpha : kLargeValues) { + for (TypeParam beta : kSmallValues) { + absl::beta_distribution<TypeParam> d(alpha, beta); + for (int i = 0; i < kCount; ++i) { + EXPECT_EQ(d(gen), 1.0); + } + } + } + } + { + // Large alpha and beta. + absl::beta_distribution<TypeParam> d(std::numeric_limits<TypeParam>::max(), + std::numeric_limits<TypeParam>::max()); + for (int i = 0; i < kCount; ++i) { + EXPECT_EQ(d(gen), 0.5); + } + } + { + // Large alpha and beta but unequal. + absl::beta_distribution<TypeParam> d( + std::numeric_limits<TypeParam>::max(), + std::numeric_limits<TypeParam>::max() * 0.9999); + for (int i = 0; i < kCount; ++i) { + TypeParam x = d(gen); + EXPECT_NE(x, 0.5f); + EXPECT_FLOAT_EQ(x, 0.500025f); + } + } +} + +class BetaDistributionModel { + public: + explicit BetaDistributionModel(::testing::tuple<double, double> p) + : alpha_(::testing::get<0>(p)), beta_(::testing::get<1>(p)) {} + + double Mean() const { return alpha_ / (alpha_ + beta_); } + + double Variance() const { + return alpha_ * beta_ / (alpha_ + beta_ + 1) / (alpha_ + beta_) / + (alpha_ + beta_); + } + + double Kurtosis() const { + return 3 + 6 * + ((alpha_ - beta_) * (alpha_ - beta_) * (alpha_ + beta_ + 1) - + alpha_ * beta_ * (2 + alpha_ + beta_)) / + alpha_ / beta_ / (alpha_ + beta_ + 2) / (alpha_ + beta_ + 3); + } + + protected: + const double alpha_; + const double beta_; +}; + +class BetaDistributionTest + : public ::testing::TestWithParam<::testing::tuple<double, double>>, + public BetaDistributionModel { + public: + BetaDistributionTest() : BetaDistributionModel(GetParam()) {} + + protected: + template <class D> + bool SingleZTestOnMeanAndVariance(double p, size_t samples); + + template <class D> + bool SingleChiSquaredTest(double p, size_t samples, size_t buckets); + + absl::InsecureBitGen rng_; +}; + +template <class D> +bool BetaDistributionTest::SingleZTestOnMeanAndVariance(double p, + size_t samples) { + D dis(alpha_, beta_); + + std::vector<double> data; + data.reserve(samples); + for (size_t i = 0; i < samples; i++) { + const double variate = dis(rng_); + EXPECT_FALSE(std::isnan(variate)); + // Note that equality is allowed on both sides. + EXPECT_GE(variate, 0.0); + EXPECT_LE(variate, 1.0); + data.push_back(variate); + } + + // We validate that the sample mean and sample variance are indeed from a + // Beta distribution with the given shape parameters. + const auto m = absl::random_internal::ComputeDistributionMoments(data); + + // The variance of the sample mean is variance / n. + const double mean_stddev = std::sqrt(Variance() / static_cast<double>(m.n)); + + // The variance of the sample variance is (approximately): + // (kurtosis - 1) * variance^2 / n + const double variance_stddev = std::sqrt( + (Kurtosis() - 1) * Variance() * Variance() / static_cast<double>(m.n)); + // z score for the sample variance. + const double z_variance = (m.variance - Variance()) / variance_stddev; + + const double max_err = absl::random_internal::MaxErrorTolerance(p); + const double z_mean = absl::random_internal::ZScore(Mean(), m); + const bool pass = + absl::random_internal::Near("z", z_mean, 0.0, max_err) && + absl::random_internal::Near("z_variance", z_variance, 0.0, max_err); + if (!pass) { + ABSL_INTERNAL_LOG( + INFO, + absl::StrFormat( + "Beta(%f, %f), " + "mean: sample %f, expect %f, which is %f stddevs away, " + "variance: sample %f, expect %f, which is %f stddevs away.", + alpha_, beta_, m.mean, Mean(), + std::abs(m.mean - Mean()) / mean_stddev, m.variance, Variance(), + std::abs(m.variance - Variance()) / variance_stddev)); + } + return pass; +} + +template <class D> +bool BetaDistributionTest::SingleChiSquaredTest(double p, size_t samples, + size_t buckets) { + constexpr double kErr = 1e-7; + std::vector<double> cutoffs, expected; + const double bucket_width = 1.0 / static_cast<double>(buckets); + int i = 1; + int unmerged_buckets = 0; + for (; i < buckets; ++i) { + const double p = bucket_width * static_cast<double>(i); + const double boundary = + absl::random_internal::BetaIncompleteInv(alpha_, beta_, p); + // The intention is to add `boundary` to the list of `cutoffs`. It becomes + // problematic, however, when the boundary values are not monotone, due to + // numerical issues when computing the inverse regularized incomplete + // Beta function. In these cases, we merge that bucket with its previous + // neighbor and merge their expected counts. + if ((cutoffs.empty() && boundary < kErr) || + (!cutoffs.empty() && boundary <= cutoffs.back())) { + unmerged_buckets++; + continue; + } + if (boundary >= 1.0 - 1e-10) { + break; + } + cutoffs.push_back(boundary); + expected.push_back(static_cast<double>(1 + unmerged_buckets) * + bucket_width * static_cast<double>(samples)); + unmerged_buckets = 0; + } + cutoffs.push_back(std::numeric_limits<double>::infinity()); + // Merge all remaining buckets. + expected.push_back(static_cast<double>(buckets - i + 1) * bucket_width * + static_cast<double>(samples)); + // Make sure that we don't merge all the buckets, making this test + // meaningless. + EXPECT_GE(cutoffs.size(), 3) << alpha_ << ", " << beta_; + + D dis(alpha_, beta_); + + std::vector<int32_t> counts(cutoffs.size(), 0); + for (int i = 0; i < samples; i++) { + const double x = dis(rng_); + auto it = std::upper_bound(cutoffs.begin(), cutoffs.end(), x); + counts[std::distance(cutoffs.begin(), it)]++; + } + + // Null-hypothesis is that the distribution is beta distributed with the + // provided alpha, beta params (not estimated from the data). + const int dof = cutoffs.size() - 1; + + const double chi_square = absl::random_internal::ChiSquare( + counts.begin(), counts.end(), expected.begin(), expected.end()); + const bool pass = + (absl::random_internal::ChiSquarePValue(chi_square, dof) >= p); + if (!pass) { + for (int i = 0; i < cutoffs.size(); i++) { + ABSL_INTERNAL_LOG( + INFO, absl::StrFormat("cutoff[%d] = %f, actual count %d, expected %d", + i, cutoffs[i], counts[i], + static_cast<int>(expected[i]))); + } + + ABSL_INTERNAL_LOG( + INFO, absl::StrFormat( + "Beta(%f, %f) %s %f, p = %f", alpha_, beta_, + absl::random_internal::kChiSquared, chi_square, + absl::random_internal::ChiSquarePValue(chi_square, dof))); + } + return pass; +} + +TEST_P(BetaDistributionTest, TestSampleStatistics) { + static constexpr int kRuns = 20; + static constexpr double kPFail = 0.02; + const double p = + absl::random_internal::RequiredSuccessProbability(kPFail, kRuns); + static constexpr int kSampleCount = 10000; + static constexpr int kBucketCount = 100; + int failed = 0; + for (int i = 0; i < kRuns; ++i) { + if (!SingleZTestOnMeanAndVariance<absl::beta_distribution<double>>( + p, kSampleCount)) { + failed++; + } + if (!SingleChiSquaredTest<absl::beta_distribution<double>>( + 0.005, kSampleCount, kBucketCount)) { + failed++; + } + } + // Set so that the test is not flaky at --runs_per_test=10000 + EXPECT_LE(failed, 5); +} + +std::string ParamName( + const ::testing::TestParamInfo<::testing::tuple<double, double>>& info) { + std::string name = absl::StrCat("alpha_", ::testing::get<0>(info.param), + "__beta_", ::testing::get<1>(info.param)); + return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); +} + +INSTANTIATE_TEST_CASE_P( + TestSampleStatisticsCombinations, BetaDistributionTest, + ::testing::Combine(::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4), + ::testing::Values(0.1, 0.2, 0.9, 1.1, 2.5, 10.0, 123.4)), + ParamName); + +INSTANTIATE_TEST_CASE_P( + TestSampleStatistics_SelectedPairs, BetaDistributionTest, + ::testing::Values(std::make_pair(0.5, 1000), std::make_pair(1000, 0.5), + std::make_pair(900, 1000), std::make_pair(10000, 20000), + std::make_pair(4e5, 2e7), std::make_pair(1e7, 1e5)), + ParamName); + +// NOTE: absl::beta_distribution is not guaranteed to be stable. +TEST(BetaDistributionTest, StabilityTest) { + // absl::beta_distribution stability relies on the stability of + // absl::random_interna::RandU64ToDouble, std::exp, std::log, std::pow, + // and std::sqrt. + // + // This test also depends on the stability of std::frexp. + using testing::ElementsAre; + absl::random_internal::sequence_urbg urbg({ + 0xffff00000000e6c8ull, 0xffff0000000006c8ull, 0x800003766295CFA9ull, + 0x11C819684E734A41ull, 0x832603766295CFA9ull, 0x7fbe76c8b4395800ull, + 0xB3472DCA7B14A94Aull, 0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, + 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, 0x00035C904C70A239ull, + 0x00009E0BCBAADE14ull, 0x0000000000622CA7ull, 0x4864f22c059bf29eull, + 0x247856d8b862665cull, 0xe46e86e9a1337e10ull, 0xd8c8541f3519b133ull, + 0xffe75b52c567b9e4ull, 0xfffff732e5709c5bull, 0xff1f7f0b983532acull, + 0x1ec2e8986d2362caull, 0xC332DDEFBE6C5AA5ull, 0x6558218568AB9702ull, + 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, 0xECDD4775619F1510ull, + 0x814c8e35fe9a961aull, 0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull, + 0x1224e62c978bbc7full, 0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull, + 0x1bbc23cfa8fac721ull, 0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull, + 0x836d794457c08849ull, 0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull, + 0xb12d74fdd718c8c5ull, 0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull, + 0x5738341045ba0d85ull, 0xf3fd722dc65ad09eull, 0xfa14fd21ea2a5705ull, + 0xffe6ea4d6edb0c73ull, 0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull, + 0xEAAD8E716B93D5A0ull, 0xD08ED1D0AFC725E0ull, 0x8E3C5B2F8E7594B7ull, + 0x8FF6E2FBF2122B64ull, 0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull, + 0xD1CFF191B3A8C1ADull, 0x2F2F2218BE0E1777ull, 0xEA752DFE8B021FA1ull, + }); + + // Convert the real-valued result into a unit64 where we compare + // 5 (float) or 10 (double) decimal digits plus the base-2 exponent. + auto float_to_u64 = [](float d) { + int exp = 0; + auto f = std::frexp(d, &exp); + return (static_cast<uint64_t>(1e5 * f) * 10000) + std::abs(exp); + }; + auto double_to_u64 = [](double d) { + int exp = 0; + auto f = std::frexp(d, &exp); + return (static_cast<uint64_t>(1e10 * f) * 10000) + std::abs(exp); + }; + + std::vector<uint64_t> output(20); + { + // Algorithm Joehnk (float) + absl::beta_distribution<float> dist(0.1f, 0.2f); + std::generate(std::begin(output), std::end(output), + [&] { return float_to_u64(dist(urbg)); }); + EXPECT_EQ(44, urbg.invocations()); + EXPECT_THAT(output, // + testing::ElementsAre( + 998340000, 619030004, 500000001, 999990000, 996280000, + 500000001, 844740004, 847210001, 999970000, 872320000, + 585480007, 933280000, 869080042, 647670031, 528240004, + 969980004, 626050008, 915930002, 833440033, 878040015)); + } + + urbg.reset(); + { + // Algorithm Joehnk (double) + absl::beta_distribution<double> dist(0.1, 0.2); + std::generate(std::begin(output), std::end(output), + [&] { return double_to_u64(dist(urbg)); }); + EXPECT_EQ(44, urbg.invocations()); + EXPECT_THAT( + output, // + testing::ElementsAre( + 99834713000000, 61903356870004, 50000000000001, 99999721170000, + 99628374770000, 99999999990000, 84474397860004, 84721276240001, + 99997407490000, 87232528120000, 58548364780007, 93328932910000, + 86908237770042, 64767917930031, 52824581970004, 96998544140004, + 62605946270008, 91593604380002, 83345031740033, 87804397230015)); + } + + urbg.reset(); + { + // Algorithm Cheng 1 + absl::beta_distribution<double> dist(0.9, 2.0); + std::generate(std::begin(output), std::end(output), + [&] { return double_to_u64(dist(urbg)); }); + EXPECT_EQ(62, urbg.invocations()); + EXPECT_THAT( + output, // + testing::ElementsAre( + 62069004780001, 64433204450001, 53607416560000, 89644295430008, + 61434586310019, 55172615890002, 62187161490000, 56433684810003, + 80454622050005, 86418558710003, 92920514700001, 64645184680001, + 58549183380000, 84881283650005, 71078728590002, 69949694970000, + 73157461710001, 68592191300001, 70747623900000, 78584696930005)); + } + + urbg.reset(); + { + // Algorithm Cheng 2 + absl::beta_distribution<double> dist(1.5, 2.5); + std::generate(std::begin(output), std::end(output), + [&] { return double_to_u64(dist(urbg)); }); + EXPECT_EQ(54, urbg.invocations()); + EXPECT_THAT( + output, // + testing::ElementsAre( + 75000029250001, 76751482860001, 53264575220000, 69193133650005, + 78028324470013, 91573587560002, 59167523770000, 60658618560002, + 80075870540000, 94141320460004, 63196592770003, 78883906300002, + 96797992590001, 76907587800001, 56645167560000, 65408302280003, + 53401156320001, 64731238570000, 83065573750001, 79788333820001)); + } +} + +// This is an implementation-specific test. If any part of the implementation +// changes, then it is likely that this test will change as well. Also, if +// dependencies of the distribution change, such as RandU64ToDouble, then this +// is also likely to change. +TEST(BetaDistributionTest, AlgorithmBounds) { + { + absl::random_internal::sequence_urbg urbg( + {0x7fbe76c8b4395800ull, 0x8000000000000000ull}); + // u=0.499, v=0.5 + absl::beta_distribution<double> dist(1e-4, 1e-4); + double a = dist(urbg); + EXPECT_EQ(a, 2.0202860861567108529e-09); + EXPECT_EQ(2, urbg.invocations()); + } + + // Test that both the float & double algorithms appropriately reject the + // initial draw. + { + // 1/alpha = 1/beta = 2. + absl::beta_distribution<float> dist(0.5, 0.5); + + // first two outputs are close to 1.0 - epsilon, + // thus: (u ^ 2 + v ^ 2) > 1.0 + absl::random_internal::sequence_urbg urbg( + {0xffff00000006e6c8ull, 0xffff00000007c7c8ull, 0x800003766295CFA9ull, + 0x11C819684E734A41ull}); + { + double y = absl::beta_distribution<double>(0.5, 0.5)(urbg); + EXPECT_EQ(4, urbg.invocations()); + EXPECT_EQ(y, 0.9810668952633862) << y; + } + + // ...and: log(u) * a ~= log(v) * b ~= -0.02 + // thus z ~= -0.02 + log(1 + e(~0)) + // ~= -0.02 + 0.69 + // thus z > 0 + urbg.reset(); + { + float x = absl::beta_distribution<float>(0.5, 0.5)(urbg); + EXPECT_EQ(4, urbg.invocations()); + EXPECT_NEAR(0.98106688261032104, x, 0.0000005) << x << "f"; + } + } +} + +} // namespace diff --git a/absl/random/discrete_distribution.cc b/absl/random/discrete_distribution.cc new file mode 100644 index 00000000..13a2dbe4 --- /dev/null +++ b/absl/random/discrete_distribution.cc @@ -0,0 +1,98 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/discrete_distribution.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// Initializes the distribution table for Walker's Aliasing algorithm, described +// in Knuth, Vol 2. as well as in https://en.wikipedia.org/wiki/Alias_method +std::vector<std::pair<double, size_t>> InitDiscreteDistribution( + std::vector<double>* probabilities) { + // The empty-case should already be handled by the constructor. + assert(probabilities); + assert(!probabilities->empty()); + + // Step 1. Normalize the input probabilities to 1.0. + double sum = std::accumulate(std::begin(*probabilities), + std::end(*probabilities), 0.0); + if (std::fabs(sum - 1.0) > 1e-6) { + // Scale `probabilities` only when the sum is too far from 1.0. Scaling + // unconditionally will alter the probabilities slightly. + for (double& item : *probabilities) { + item = item / sum; + } + } + + // Step 2. At this point `probabilities` is set to the conditional + // probabilities of each element which sum to 1.0, to within reasonable error. + // These values are used to construct the proportional probability tables for + // the selection phases of Walker's Aliasing algorithm. + // + // To construct the table, pick an element which is under-full (i.e., an + // element for which `(*probabilities)[i] < 1.0/n`), and pair it with an + // element which is over-full (i.e., an element for which + // `(*probabilities)[i] > 1.0/n`). The smaller value can always be retired. + // The larger may still be greater than 1.0/n, or may now be less than 1.0/n, + // and put back onto the appropriate collection. + const size_t n = probabilities->size(); + std::vector<std::pair<double, size_t>> q; + q.reserve(n); + + std::vector<size_t> over; + std::vector<size_t> under; + size_t idx = 0; + for (const double item : *probabilities) { + assert(item >= 0); + const double v = item * n; + q.emplace_back(v, 0); + if (v < 1.0) { + under.push_back(idx++); + } else { + over.push_back(idx++); + } + } + while (!over.empty() && !under.empty()) { + auto lo = under.back(); + under.pop_back(); + auto hi = over.back(); + over.pop_back(); + + q[lo].second = hi; + const double r = q[hi].first - (1.0 - q[lo].first); + q[hi].first = r; + if (r < 1.0) { + under.push_back(hi); + } else { + over.push_back(hi); + } + } + + // Due to rounding errors, there may be un-paired elements in either + // collection; these should all be values near 1.0. For these values, set `q` + // to 1.0 and set the alternate to the identity. + for (auto i : over) { + q[i] = {1.0, i}; + } + for (auto i : under) { + q[i] = {1.0, i}; + } + return q; +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/discrete_distribution.h b/absl/random/discrete_distribution.h new file mode 100644 index 00000000..5866fb23 --- /dev/null +++ b/absl/random/discrete_distribution.h @@ -0,0 +1,247 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_ +#define ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_ + +#include <cassert> +#include <cmath> +#include <istream> +#include <limits> +#include <numeric> +#include <type_traits> +#include <utility> +#include <vector> + +#include "absl/random/bernoulli_distribution.h" +#include "absl/random/internal/iostream_state_saver.h" +#include "absl/random/uniform_int_distribution.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// absl::discrete_distribution +// +// A discrete distribution produces random integers i, where 0 <= i < n +// distributed according to the discrete probability function: +// +// P(i|p0,...,pn−1)=pi +// +// This class is an implementation of discrete_distribution (see +// [rand.dist.samp.discrete]). +// +// The algorithm used is Walker's Aliasing algorithm, described in Knuth, Vol 2. +// absl::discrete_distribution takes O(N) time to precompute the probabilities +// (where N is the number of possible outcomes in the distribution) at +// construction, and then takes O(1) time for each variate generation. Many +// other implementations also take O(N) time to construct an ordered sequence of +// partial sums, plus O(log N) time per variate to binary search. +// +template <typename IntType = int> +class discrete_distribution { + public: + using result_type = IntType; + + class param_type { + public: + using distribution_type = discrete_distribution; + + param_type() { init(); } + + template <typename InputIterator> + explicit param_type(InputIterator begin, InputIterator end) + : p_(begin, end) { + init(); + } + + explicit param_type(std::initializer_list<double> weights) : p_(weights) { + init(); + } + + template <class UnaryOperation> + explicit param_type(size_t nw, double xmin, double xmax, + UnaryOperation fw) { + if (nw > 0) { + p_.reserve(nw); + double delta = (xmax - xmin) / static_cast<double>(nw); + assert(delta > 0); + double t = delta * 0.5; + for (size_t i = 0; i < nw; ++i) { + p_.push_back(fw(xmin + i * delta + t)); + } + } + init(); + } + + const std::vector<double>& probabilities() const { return p_; } + size_t n() const { return p_.size() - 1; } + + friend bool operator==(const param_type& a, const param_type& b) { + return a.probabilities() == b.probabilities(); + } + + friend bool operator!=(const param_type& a, const param_type& b) { + return !(a == b); + } + + private: + friend class discrete_distribution; + + void init(); + + std::vector<double> p_; // normalized probabilities + std::vector<std::pair<double, size_t>> q_; // (acceptance, alternate) pairs + + static_assert(std::is_integral<result_type>::value, + "Class-template absl::discrete_distribution<> must be " + "parameterized using an integral type."); + }; + + discrete_distribution() : param_() {} + + explicit discrete_distribution(const param_type& p) : param_(p) {} + + template <typename InputIterator> + explicit discrete_distribution(InputIterator begin, InputIterator end) + : param_(begin, end) {} + + explicit discrete_distribution(std::initializer_list<double> weights) + : param_(weights) {} + + template <class UnaryOperation> + explicit discrete_distribution(size_t nw, double xmin, double xmax, + UnaryOperation fw) + : param_(nw, xmin, xmax, std::move(fw)) {} + + void reset() {} + + // generating functions + template <typename URBG> + result_type operator()(URBG& g) { // NOLINT(runtime/references) + return (*this)(g, param_); + } + + template <typename URBG> + result_type operator()(URBG& g, // NOLINT(runtime/references) + const param_type& p); + + const param_type& param() const { return param_; } + void param(const param_type& p) { param_ = p; } + + result_type(min)() const { return 0; } + result_type(max)() const { + return static_cast<result_type>(param_.n()); + } // inclusive + + // NOTE [rand.dist.sample.discrete] returns a std::vector<double> not a + // const std::vector<double>&. + const std::vector<double>& probabilities() const { + return param_.probabilities(); + } + + friend bool operator==(const discrete_distribution& a, + const discrete_distribution& b) { + return a.param_ == b.param_; + } + friend bool operator!=(const discrete_distribution& a, + const discrete_distribution& b) { + return a.param_ != b.param_; + } + + private: + param_type param_; +}; + +// -------------------------------------------------------------------------- +// Implementation details only below +// -------------------------------------------------------------------------- + +namespace random_internal { + +// Using the vector `*probabilities`, whose values are the weights or +// probabilities of an element being selected, constructs the proportional +// probabilities used by the discrete distribution. `*probabilities` will be +// scaled, if necessary, so that its entries sum to a value sufficiently close +// to 1.0. +std::vector<std::pair<double, size_t>> InitDiscreteDistribution( + std::vector<double>* probabilities); + +} // namespace random_internal + +template <typename IntType> +void discrete_distribution<IntType>::param_type::init() { + if (p_.empty()) { + p_.push_back(1.0); + q_.emplace_back(1.0, 0); + } else { + assert(n() <= (std::numeric_limits<IntType>::max)()); + q_ = random_internal::InitDiscreteDistribution(&p_); + } +} + +template <typename IntType> +template <typename URBG> +typename discrete_distribution<IntType>::result_type +discrete_distribution<IntType>::operator()( + URBG& g, // NOLINT(runtime/references) + const param_type& p) { + const auto idx = absl::uniform_int_distribution<result_type>(0, p.n())(g); + const auto& q = p.q_[idx]; + const bool selected = absl::bernoulli_distribution(q.first)(g); + return selected ? idx : static_cast<result_type>(q.second); +} + +template <typename CharT, typename Traits, typename IntType> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const discrete_distribution<IntType>& x) { + auto saver = random_internal::make_ostream_state_saver(os); + const auto& probabilities = x.param().probabilities(); + os << probabilities.size(); + + os.precision(random_internal::stream_precision_helper<double>::kPrecision); + for (const auto& p : probabilities) { + os << os.fill() << p; + } + return os; +} + +template <typename CharT, typename Traits, typename IntType> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + discrete_distribution<IntType>& x) { // NOLINT(runtime/references) + using param_type = typename discrete_distribution<IntType>::param_type; + auto saver = random_internal::make_istream_state_saver(is); + + size_t n; + std::vector<double> p; + + is >> n; + if (is.fail()) return is; + if (n > 0) { + p.reserve(n); + for (IntType i = 0; i < n && !is.fail(); ++i) { + auto tmp = random_internal::read_floating_point<double>(is); + if (is.fail()) return is; + p.push_back(tmp); + } + } + x.param(param_type(p.begin(), p.end())); + return is; +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_ diff --git a/absl/random/discrete_distribution_test.cc b/absl/random/discrete_distribution_test.cc new file mode 100644 index 00000000..7296f0ac --- /dev/null +++ b/absl/random/discrete_distribution_test.cc @@ -0,0 +1,246 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/discrete_distribution.h" + +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <numeric> +#include <random> +#include <sstream> +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/chi_square.h" +#include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/strip.h" + +namespace { + +template <typename IntType> +class DiscreteDistributionTypeTest : public ::testing::Test {}; + +using IntTypes = ::testing::Types<int8_t, uint8_t, int16_t, uint16_t, int32_t, + uint32_t, int64_t, uint64_t>; +TYPED_TEST_SUITE(DiscreteDistributionTypeTest, IntTypes); + +TYPED_TEST(DiscreteDistributionTypeTest, ParamSerializeTest) { + using param_type = + typename absl::discrete_distribution<TypeParam>::param_type; + + absl::discrete_distribution<TypeParam> empty; + EXPECT_THAT(empty.probabilities(), testing::ElementsAre(1.0)); + + absl::discrete_distribution<TypeParam> before({1.0, 2.0, 1.0}); + + // Validate that the probabilities sum to 1.0. We picked values which + // can be represented exactly to avoid floating-point roundoff error. + double s = 0; + for (const auto& x : before.probabilities()) { + s += x; + } + EXPECT_EQ(s, 1.0); + EXPECT_THAT(before.probabilities(), testing::ElementsAre(0.25, 0.5, 0.25)); + + // Validate the same data via an initializer list. + { + std::vector<double> data({1.0, 2.0, 1.0}); + + absl::discrete_distribution<TypeParam> via_param{ + param_type(std::begin(data), std::end(data))}; + + EXPECT_EQ(via_param, before); + } + + std::stringstream ss; + ss << before; + absl::discrete_distribution<TypeParam> after; + + EXPECT_NE(before, after); + + ss >> after; + + EXPECT_EQ(before, after); +} + +TYPED_TEST(DiscreteDistributionTypeTest, Constructor) { + auto fn = [](double x) { return x; }; + { + absl::discrete_distribution<int> unary(0, 1.0, 9.0, fn); + EXPECT_THAT(unary.probabilities(), testing::ElementsAre(1.0)); + } + + { + absl::discrete_distribution<int> unary(2, 1.0, 9.0, fn); + // => fn(1.0 + 0 * 4 + 2) => 3 + // => fn(1.0 + 1 * 4 + 2) => 7 + EXPECT_THAT(unary.probabilities(), testing::ElementsAre(0.3, 0.7)); + } +} + +TEST(DiscreteDistributionTest, InitDiscreteDistribution) { + using testing::Pair; + + { + std::vector<double> p({1.0, 2.0, 3.0}); + std::vector<std::pair<double, size_t>> q = + absl::random_internal::InitDiscreteDistribution(&p); + + EXPECT_THAT(p, testing::ElementsAre(1 / 6.0, 2 / 6.0, 3 / 6.0)); + + // Each bucket is p=1/3, so bucket 0 will send half it's traffic + // to bucket 2, while the rest will retain all of their traffic. + EXPECT_THAT(q, testing::ElementsAre(Pair(0.5, 2), // + Pair(1.0, 1), // + Pair(1.0, 2))); + } + + { + std::vector<double> p({1.0, 2.0, 3.0, 5.0, 2.0}); + + std::vector<std::pair<double, size_t>> q = + absl::random_internal::InitDiscreteDistribution(&p); + + EXPECT_THAT(p, testing::ElementsAre(1 / 13.0, 2 / 13.0, 3 / 13.0, 5 / 13.0, + 2 / 13.0)); + + // A more complex bucketing solution: Each bucket has p=0.2 + // So buckets 0, 1, 4 will send their alternate traffic elsewhere, which + // happens to be bucket 3. + // However, summing up that alternate traffic gives bucket 3 too much + // traffic, so it will send some traffic to bucket 2. + constexpr double b0 = 1.0 / 13.0 / 0.2; + constexpr double b1 = 2.0 / 13.0 / 0.2; + constexpr double b3 = (5.0 / 13.0 / 0.2) - ((1 - b0) + (1 - b1) + (1 - b1)); + + EXPECT_THAT(q, testing::ElementsAre(Pair(b0, 3), // + Pair(b1, 3), // + Pair(1.0, 2), // + Pair(b3, 2), // + Pair(b1, 3))); + } +} + +TEST(DiscreteDistributionTest, ChiSquaredTest50) { + using absl::random_internal::kChiSquared; + + constexpr size_t kTrials = 10000; + constexpr int kBuckets = 50; // inclusive, so actally +1 + + // 1-in-100000 threshold, but remember, there are about 8 tests + // in this file. And the test could fail for other reasons. + // Empirically validated with --runs_per_test=10000. + const int kThreshold = + absl::random_internal::ChiSquareValue(kBuckets, 0.99999); + + std::vector<double> weights(kBuckets, 0); + std::iota(std::begin(weights), std::end(weights), 1); + absl::discrete_distribution<int> dist(std::begin(weights), std::end(weights)); + + absl::InsecureBitGen rng; + + std::vector<int32_t> counts(kBuckets, 0); + for (size_t i = 0; i < kTrials; i++) { + auto x = dist(rng); + counts[x]++; + } + + // Scale weights. + double sum = 0; + for (double x : weights) { + sum += x; + } + for (double& x : weights) { + x = kTrials * (x / sum); + } + + double chi_square = + absl::random_internal::ChiSquare(std::begin(counts), std::end(counts), + std::begin(weights), std::end(weights)); + + if (chi_square > kThreshold) { + double p_value = + absl::random_internal::ChiSquarePValue(chi_square, kBuckets); + + // Chi-squared test failed. Output does not appear to be uniform. + std::string msg; + for (size_t i = 0; i < counts.size(); i++) { + absl::StrAppend(&msg, i, ": ", counts[i], " vs ", weights[i], "\n"); + } + absl::StrAppend(&msg, kChiSquared, " p-value ", p_value, "\n"); + absl::StrAppend(&msg, "High ", kChiSquared, " value: ", chi_square, " > ", + kThreshold); + ABSL_RAW_LOG(INFO, "%s", msg.c_str()); + FAIL() << msg; + } +} + +TEST(DiscreteDistributionTest, StabilityTest) { + // absl::discrete_distribution stabilitiy relies on + // absl::uniform_int_distribution and absl::bernoulli_distribution. + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector<int> output(6); + + { + absl::discrete_distribution<int32_t> dist({1.0, 2.0, 3.0, 5.0, 2.0}); + EXPECT_EQ(0, dist.min()); + EXPECT_EQ(4, dist.max()); + for (auto& v : output) { + v = dist(urbg); + } + EXPECT_EQ(12, urbg.invocations()); + } + + // With 12 calls to urbg, each call into discrete_distribution consumes + // precisely 2 values: one for the uniform call, and a second for the + // bernoulli. + // + // Given the alt mapping: 0=>3, 1=>3, 2=>2, 3=>2, 4=>3, we can + // + // uniform: 443210143131 + // bernoulli: b0 000011100101 + // bernoulli: b1 001111101101 + // bernoulli: b2 111111111111 + // bernoulli: b3 001111101111 + // bernoulli: b4 001111101101 + // ... + EXPECT_THAT(output, testing::ElementsAre(3, 3, 1, 3, 3, 3)); + + { + urbg.reset(); + absl::discrete_distribution<int64_t> dist({1.0, 2.0, 3.0, 5.0, 2.0}); + EXPECT_EQ(0, dist.min()); + EXPECT_EQ(4, dist.max()); + for (auto& v : output) { + v = dist(urbg); + } + EXPECT_EQ(12, urbg.invocations()); + } + EXPECT_THAT(output, testing::ElementsAre(3, 3, 0, 3, 0, 4)); +} + +} // namespace diff --git a/absl/random/distribution_format_traits.h b/absl/random/distribution_format_traits.h new file mode 100644 index 00000000..838271c7 --- /dev/null +++ b/absl/random/distribution_format_traits.h @@ -0,0 +1,251 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ +#define ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ + +#include <string> +#include <tuple> +#include <typeinfo> + +#include "absl/meta/type_traits.h" +#include "absl/random/bernoulli_distribution.h" +#include "absl/random/beta_distribution.h" +#include "absl/random/exponential_distribution.h" +#include "absl/random/gaussian_distribution.h" +#include "absl/random/log_uniform_int_distribution.h" +#include "absl/random/poisson_distribution.h" +#include "absl/random/uniform_int_distribution.h" +#include "absl/random/uniform_real_distribution.h" +#include "absl/random/zipf_distribution.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// ScalarTypeName defines a preferred hierarchy of preferred type names for +// scalars, and is evaluated at compile time for the specific type +// specialization. +template <typename T> +constexpr const char* ScalarTypeName() { + static_assert(std::is_integral<T>() || std::is_floating_point<T>(), ""); + // clang-format off + return + std::is_same<T, float>::value ? "float" : + std::is_same<T, double>::value ? "double" : + std::is_same<T, long double>::value ? "long double" : + std::is_same<T, bool>::value ? "bool" : + std::is_signed<T>::value && sizeof(T) == 1 ? "int8_t" : + std::is_signed<T>::value && sizeof(T) == 2 ? "int16_t" : + std::is_signed<T>::value && sizeof(T) == 4 ? "int32_t" : + std::is_signed<T>::value && sizeof(T) == 8 ? "int64_t" : + std::is_unsigned<T>::value && sizeof(T) == 1 ? "uint8_t" : + std::is_unsigned<T>::value && sizeof(T) == 2 ? "uint16_t" : + std::is_unsigned<T>::value && sizeof(T) == 4 ? "uint32_t" : + std::is_unsigned<T>::value && sizeof(T) == 8 ? "uint64_t" : + "undefined"; + // clang-format on + + // NOTE: It would be nice to use typeid(T).name(), but that's an + // implementation-defined attribute which does not necessarily + // correspond to a name. We could potentially demangle it + // using, e.g. abi::__cxa_demangle. +} + +// Distribution traits used by DistributionCaller and internal implementation +// details of the mocking framework. +/* +struct DistributionFormatTraits { + // Returns the parameterized name of the distribution function. + static constexpr const char* FunctionName() + // Format DistrT parameters. + static std::string FormatArgs(DistrT& dist); + // Format DistrT::result_type results. + static std::string FormatResults(DistrT& dist); +}; +*/ +template <typename DistrT> +struct DistributionFormatTraits; + +template <typename R> +struct DistributionFormatTraits<absl::uniform_int_distribution<R>> { + using distribution_t = absl::uniform_int_distribution<R>; + using result_t = typename distribution_t::result_type; + + static constexpr const char* Name() { return "Uniform"; } + + static std::string FunctionName() { + return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); + } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrCat("absl::IntervalClosedClosed, ", (d.min)(), ", ", + (d.max)()); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + +template <typename R> +struct DistributionFormatTraits<absl::uniform_real_distribution<R>> { + using distribution_t = absl::uniform_real_distribution<R>; + using result_t = typename distribution_t::result_type; + + static constexpr const char* Name() { return "Uniform"; } + + static std::string FunctionName() { + return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); + } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrCat((d.min)(), ", ", (d.max)()); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + +template <typename R> +struct DistributionFormatTraits<absl::exponential_distribution<R>> { + using distribution_t = absl::exponential_distribution<R>; + using result_t = typename distribution_t::result_type; + + static constexpr const char* Name() { return "Exponential"; } + + static std::string FunctionName() { + return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); + } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrCat(d.lambda()); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + +template <typename R> +struct DistributionFormatTraits<absl::poisson_distribution<R>> { + using distribution_t = absl::poisson_distribution<R>; + using result_t = typename distribution_t::result_type; + + static constexpr const char* Name() { return "Poisson"; } + + static std::string FunctionName() { + return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); + } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrCat(d.mean()); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + +template <> +struct DistributionFormatTraits<absl::bernoulli_distribution> { + using distribution_t = absl::bernoulli_distribution; + using result_t = typename distribution_t::result_type; + + static constexpr const char* Name() { return "Bernoulli"; } + + static constexpr const char* FunctionName() { return Name(); } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrCat(d.p()); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + +template <typename R> +struct DistributionFormatTraits<absl::beta_distribution<R>> { + using distribution_t = absl::beta_distribution<R>; + using result_t = typename distribution_t::result_type; + + static constexpr const char* Name() { return "Beta"; } + + static std::string FunctionName() { + return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); + } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrCat(d.alpha(), ", ", d.beta()); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + +template <typename R> +struct DistributionFormatTraits<absl::zipf_distribution<R>> { + using distribution_t = absl::zipf_distribution<R>; + using result_t = typename distribution_t::result_type; + + static constexpr const char* Name() { return "Zipf"; } + + static std::string FunctionName() { + return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); + } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrCat(d.k(), ", ", d.v(), ", ", d.q()); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + +template <typename R> +struct DistributionFormatTraits<absl::gaussian_distribution<R>> { + using distribution_t = absl::gaussian_distribution<R>; + using result_t = typename distribution_t::result_type; + + static constexpr const char* Name() { return "Gaussian"; } + + static std::string FunctionName() { + return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); + } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrJoin(std::make_tuple(d.mean(), d.stddev()), ", "); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + +template <typename R> +struct DistributionFormatTraits<absl::log_uniform_int_distribution<R>> { + using distribution_t = absl::log_uniform_int_distribution<R>; + using result_t = typename distribution_t::result_type; + + static constexpr const char* Name() { return "LogUniform"; } + + static std::string FunctionName() { + return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">"); + } + static std::string FormatArgs(const distribution_t& d) { + return absl::StrJoin(std::make_tuple((d.min)(), (d.max)(), d.base()), ", "); + } + static std::string FormatResults(absl::Span<const result_t> results) { + return absl::StrJoin(results, ", "); + } +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_ diff --git a/absl/random/distributions.h b/absl/random/distributions.h new file mode 100644 index 00000000..f2ebfc2d --- /dev/null +++ b/absl/random/distributions.h @@ -0,0 +1,444 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: distributions.h +// ----------------------------------------------------------------------------- +// +// This header defines functions representing distributions, which you use in +// combination with an Abseil random bit generator to produce random values +// according to the rules of that distribution. +// +// The Abseil random library defines the following distributions within this +// file: +// +// * `absl::Uniform` for uniform (constant) distributions having constant +// probability +// * `absl::Bernoulli` for discrete distributions having exactly two outcomes +// * `absl::Beta` for continuous distributions parameterized through two +// free parameters +// * `absl::Exponential` for discrete distributions of events occurring +// continuously and independently at a constant average rate +// * `absl::Gaussian` (also known as "normal distributions") for continuous +// distributions using an associated quadratic function +// * `absl::LogUniform` for continuous uniform distributions where the log +// to the given base of all values is uniform +// * `absl::Poisson` for discrete probability distributions that express the +// probability of a given number of events occurring within a fixed interval +// * `absl::Zipf` for discrete probability distributions commonly used for +// modelling of rare events +// +// Prefer use of these distribution function classes over manual construction of +// your own distribution classes, as it allows library maintainers greater +// flexibility to change the underlying implementation in the future. + +#ifndef ABSL_RANDOM_DISTRIBUTIONS_H_ +#define ABSL_RANDOM_DISTRIBUTIONS_H_ + +#include <algorithm> +#include <cmath> +#include <limits> +#include <random> +#include <type_traits> + +#include "absl/base/internal/inline_variable.h" +#include "absl/random/bernoulli_distribution.h" +#include "absl/random/beta_distribution.h" +#include "absl/random/distribution_format_traits.h" +#include "absl/random/exponential_distribution.h" +#include "absl/random/gaussian_distribution.h" +#include "absl/random/internal/distributions.h" // IWYU pragma: export +#include "absl/random/internal/uniform_helper.h" // IWYU pragma: export +#include "absl/random/log_uniform_int_distribution.h" +#include "absl/random/poisson_distribution.h" +#include "absl/random/uniform_int_distribution.h" +#include "absl/random/uniform_real_distribution.h" +#include "absl/random/zipf_distribution.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalClosedClosedT, + IntervalClosedClosed, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalClosedClosedT, + IntervalClosed, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalClosedOpenT, + IntervalClosedOpen, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenOpenT, + IntervalOpenOpen, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenOpenT, + IntervalOpen, {}); +ABSL_INTERNAL_INLINE_CONSTEXPR(random_internal::IntervalOpenClosedT, + IntervalOpenClosed, {}); + +// ----------------------------------------------------------------------------- +// absl::Uniform<T>(tag, bitgen, lo, hi) +// ----------------------------------------------------------------------------- +// +// `absl::Uniform()` produces random values of type `T` uniformly distributed in +// a defined interval {lo, hi}. The interval `tag` defines the type of interval +// which should be one of the following possible values: +// +// * `absl::IntervalOpenOpen` +// * `absl::IntervalOpenClosed` +// * `absl::IntervalClosedOpen` +// * `absl::IntervalClosedClosed` +// +// where "open" refers to an exclusive value (excluded) from the output, while +// "closed" refers to an inclusive value (included) from the output. +// +// In the absence of an explicit return type `T`, `absl::Uniform()` will deduce +// the return type based on the provided endpoint arguments {A lo, B hi}. +// Given these endpoints, one of {A, B} will be chosen as the return type, if +// a type can be implicitly converted into the other in a lossless way. The +// lack of any such implcit conversion between {A, B} will produce a +// compile-time error +// +// See https://en.wikipedia.org/wiki/Uniform_distribution_(continuous) +// +// Example: +// +// absl::BitGen bitgen; +// +// // Produce a random float value between 0.0 and 1.0, inclusive +// auto x = absl::Uniform(absl::IntervalClosedClosed, bitgen, 0.0f, 1.0f); +// +// // The most common interval of `absl::IntervalClosedOpen` is available by +// // default: +// +// auto x = absl::Uniform(bitgen, 0.0f, 1.0f); +// +// // Return-types are typically inferred from the arguments, however callers +// // can optionally provide an explicit return-type to the template. +// +// auto x = absl::Uniform<float>(bitgen, 0, 1); +// +template <typename R = void, typename TagType, typename URBG> +typename absl::enable_if_t<!std::is_same<R, void>::value, R> // +Uniform(TagType tag, + URBG&& urbg, // NOLINT(runtime/references) + R lo, R hi) { + using gen_t = absl::decay_t<URBG>; + return random_internal::UniformImpl<R, TagType, gen_t>(tag, urbg, lo, hi); +} + +// absl::Uniform<T>(bitgen, lo, hi) +// +// Overload of `Uniform()` using the default closed-open interval of [lo, hi), +// and returning values of type `T` +template <typename R = void, typename URBG> +typename absl::enable_if_t<!std::is_same<R, void>::value, R> // +Uniform(URBG&& urbg, // NOLINT(runtime/references) + R lo, R hi) { + constexpr auto tag = absl::IntervalClosedOpen; + using tag_t = decltype(tag); + using gen_t = absl::decay_t<URBG>; + + return random_internal::UniformImpl<R, tag_t, gen_t>(tag, urbg, lo, hi); +} + +// absl::Uniform(tag, bitgen, lo, hi) +// +// Overload of `Uniform()` using different (but compatible) lo, hi types. Note +// that a compile-error will result if the return type cannot be deduced +// correctly from the passed types. +template <typename R = void, typename TagType, typename URBG, typename A, + typename B> +typename absl::enable_if_t<std::is_same<R, void>::value, + random_internal::uniform_inferred_return_t<A, B>> +Uniform(TagType tag, + URBG&& urbg, // NOLINT(runtime/references) + A lo, B hi) { + using gen_t = absl::decay_t<URBG>; + using return_t = typename random_internal::uniform_inferred_return_t<A, B>; + + return random_internal::UniformImpl<return_t, TagType, gen_t>(tag, urbg, lo, + hi); +} + +// absl::Uniform(bitgen, lo, hi) +// +// Overload of `Uniform()` using different (but compatible) lo, hi types and the +// default closed-open interval of [lo, hi). Note that a compile-error will +// result if the return type cannot be deduced correctly from the passed types. +template <typename R = void, typename URBG, typename A, typename B> +typename absl::enable_if_t<std::is_same<R, void>::value, + random_internal::uniform_inferred_return_t<A, B>> +Uniform(URBG&& urbg, // NOLINT(runtime/references) + A lo, B hi) { + constexpr auto tag = absl::IntervalClosedOpen; + using tag_t = decltype(tag); + using gen_t = absl::decay_t<URBG>; + using return_t = typename random_internal::uniform_inferred_return_t<A, B>; + + return random_internal::UniformImpl<return_t, tag_t, gen_t>(tag, urbg, lo, + hi); +} + +// absl::Uniform<unsigned T>(bitgen) +// +// Overload of Uniform() using the minimum and maximum values of a given type +// `T` (which must be unsigned), returning a value of type `unsigned T` +template <typename R, typename URBG> +typename absl::enable_if_t<!std::is_signed<R>::value, R> // +Uniform(URBG&& urbg) { // NOLINT(runtime/references) + constexpr auto tag = absl::IntervalClosedClosed; + constexpr auto lo = std::numeric_limits<R>::lowest(); + constexpr auto hi = (std::numeric_limits<R>::max)(); + using tag_t = decltype(tag); + using gen_t = absl::decay_t<URBG>; + + return random_internal::UniformImpl<R, tag_t, gen_t>(tag, urbg, lo, hi); +} + +// ----------------------------------------------------------------------------- +// absl::Bernoulli(bitgen, p) +// ----------------------------------------------------------------------------- +// +// `absl::Bernoulli` produces a random boolean value, with probability `p` +// (where 0.0 <= p <= 1.0) equaling `true`. +// +// Prefer `absl::Bernoulli` to produce boolean values over other alternatives +// such as comparing an `absl::Uniform()` value to a specific output. +// +// See https://en.wikipedia.org/wiki/Bernoulli_distribution +// +// Example: +// +// absl::BitGen bitgen; +// ... +// if (absl::Bernoulli(bitgen, 1.0/3721.0)) { +// std::cout << "Asteroid field navigation successful."; +// } +// +template <typename URBG> +bool Bernoulli(URBG&& urbg, // NOLINT(runtime/references) + double p) { + using gen_t = absl::decay_t<URBG>; + using distribution_t = absl::bernoulli_distribution; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, p); +} + +// ----------------------------------------------------------------------------- +// absl::Beta<T>(bitgen, alpha, beta) +// ----------------------------------------------------------------------------- +// +// `absl::Beta` produces a floating point number distributed in the closed +// interval [0,1] and parameterized by two values `alpha` and `beta` as per a +// Beta distribution. `T` must be a floating point type, but may be inferred +// from the types of `alpha` and `beta`. +// +// See https://en.wikipedia.org/wiki/Beta_distribution. +// +// Example: +// +// absl::BitGen bitgen; +// ... +// double sample = absl::Beta(bitgen, 3.0, 2.0); +// +template <typename RealType, typename URBG> +RealType Beta(URBG&& urbg, // NOLINT(runtime/references) + RealType alpha, RealType beta) { + static_assert( + std::is_floating_point<RealType>::value, + "Template-argument 'RealType' must be a floating-point type, in " + "absl::Beta<RealType, URBG>(...)"); + + using gen_t = absl::decay_t<URBG>; + using distribution_t = typename absl::beta_distribution<RealType>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, alpha, beta); +} + +// ----------------------------------------------------------------------------- +// absl::Exponential<T>(bitgen, lambda = 1) +// ----------------------------------------------------------------------------- +// +// `absl::Exponential` produces a floating point number for discrete +// distributions of events occurring continuously and independently at a +// constant average rate. `T` must be a floating point type, but may be inferred +// from the type of `lambda`. +// +// See https://en.wikipedia.org/wiki/Exponential_distribution. +// +// Example: +// +// absl::BitGen bitgen; +// ... +// double call_length = absl::Exponential(bitgen, 7.0); +// +template <typename RealType, typename URBG> +RealType Exponential(URBG&& urbg, // NOLINT(runtime/references) + RealType lambda = 1) { + static_assert( + std::is_floating_point<RealType>::value, + "Template-argument 'RealType' must be a floating-point type, in " + "absl::Exponential<RealType, URBG>(...)"); + + using gen_t = absl::decay_t<URBG>; + using distribution_t = typename absl::exponential_distribution<RealType>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, lambda); +} + +// ----------------------------------------------------------------------------- +// absl::Gaussian<T>(bitgen, mean = 0, stddev = 1) +// ----------------------------------------------------------------------------- +// +// `absl::Gaussian` produces a floating point number selected from the Gaussian +// (ie. "Normal") distribution. `T` must be a floating point type, but may be +// inferred from the types of `mean` and `stddev`. +// +// See https://en.wikipedia.org/wiki/Normal_distribution +// +// Example: +// +// absl::BitGen bitgen; +// ... +// double giraffe_height = absl::Gaussian(bitgen, 16.3, 3.3); +// +template <typename RealType, typename URBG> +RealType Gaussian(URBG&& urbg, // NOLINT(runtime/references) + RealType mean = 0, RealType stddev = 1) { + static_assert( + std::is_floating_point<RealType>::value, + "Template-argument 'RealType' must be a floating-point type, in " + "absl::Gaussian<RealType, URBG>(...)"); + + using gen_t = absl::decay_t<URBG>; + using distribution_t = typename absl::gaussian_distribution<RealType>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, mean, stddev); +} + +// ----------------------------------------------------------------------------- +// absl::LogUniform<T>(bitgen, lo, hi, base = 2) +// ----------------------------------------------------------------------------- +// +// `absl::LogUniform` produces random values distributed where the log to a +// given base of all values is uniform in a closed interval [lo, hi]. `T` must +// be an integral type, but may be inferred from the types of `lo` and `hi`. +// +// I.e., `LogUniform(0, n, b)` is uniformly distributed across buckets +// [0], [1, b-1], [b, b^2-1] .. [b^(k-1), (b^k)-1] .. [b^floor(log(n, b)), n] +// and is uniformly distributed within each bucket. +// +// The resulting probability density is inversely related to bucket size, though +// values in the final bucket may be more likely than previous values. (In the +// extreme case where n = b^i the final value will be tied with zero as the most +// probable result. +// +// If `lo` is nonzero then this distribution is shifted to the desired interval, +// so LogUniform(lo, hi, b) is equivalent to LogUniform(0, hi-lo, b)+lo. +// +// See http://ecolego.facilia.se/ecolego/show/Log-Uniform%20Distribution +// +// Example: +// +// absl::BitGen bitgen; +// ... +// int v = absl::LogUniform(bitgen, 0, 1000); +// +template <typename IntType, typename URBG> +IntType LogUniform(URBG&& urbg, // NOLINT(runtime/references) + IntType lo, IntType hi, IntType base = 2) { + static_assert(std::is_integral<IntType>::value, + "Template-argument 'IntType' must be an integral type, in " + "absl::LogUniform<IntType, URBG>(...)"); + + using gen_t = absl::decay_t<URBG>; + using distribution_t = typename absl::log_uniform_int_distribution<IntType>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, lo, hi, base); +} + +// ----------------------------------------------------------------------------- +// absl::Poisson<T>(bitgen, mean = 1) +// ----------------------------------------------------------------------------- +// +// `absl::Poisson` produces discrete probabilities for a given number of events +// occurring within a fixed interval within the closed interval [0, max]. `T` +// must be an integral type. +// +// See https://en.wikipedia.org/wiki/Poisson_distribution +// +// Example: +// +// absl::BitGen bitgen; +// ... +// int requests_per_minute = absl::Poisson<int>(bitgen, 3.2); +// +template <typename IntType, typename URBG> +IntType Poisson(URBG&& urbg, // NOLINT(runtime/references) + double mean = 1.0) { + static_assert(std::is_integral<IntType>::value, + "Template-argument 'IntType' must be an integral type, in " + "absl::Poisson<IntType, URBG>(...)"); + + using gen_t = absl::decay_t<URBG>; + using distribution_t = typename absl::poisson_distribution<IntType>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, mean); +} + +// ----------------------------------------------------------------------------- +// absl::Zipf<T>(bitgen, hi = max, q = 2, v = 1) +// ----------------------------------------------------------------------------- +// +// `absl::Zipf` produces discrete probabilities commonly used for modelling of +// rare events over the closed interval [0, hi]. The parameters `v` and `q` +// determine the skew of the distribution. `T` must be an integral type, but +// may be inferred from the type of `hi`. +// +// See http://mathworld.wolfram.com/ZipfDistribution.html +// +// Example: +// +// absl::BitGen bitgen; +// ... +// int term_rank = absl::Zipf<int>(bitgen); +// +template <typename IntType, typename URBG> +IntType Zipf(URBG&& urbg, // NOLINT(runtime/references) + IntType hi = (std::numeric_limits<IntType>::max)(), double q = 2.0, + double v = 1.0) { + static_assert(std::is_integral<IntType>::value, + "Template-argument 'IntType' must be an integral type, in " + "absl::Zipf<IntType, URBG>(...)"); + + using gen_t = absl::decay_t<URBG>; + using distribution_t = typename absl::zipf_distribution<IntType>; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + return random_internal::DistributionCaller<gen_t>::template Call< + distribution_t, format_t>(&urbg, hi, q, v); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_DISTRIBUTIONS_H_ diff --git a/absl/random/distributions_test.cc b/absl/random/distributions_test.cc new file mode 100644 index 00000000..eb82868d --- /dev/null +++ b/absl/random/distributions_test.cc @@ -0,0 +1,494 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/distributions.h" + +#include <cmath> +#include <cstdint> +#include <random> +#include <vector> + +#include "gtest/gtest.h" +#include "absl/random/internal/distribution_test_util.h" +#include "absl/random/random.h" + +namespace { + +constexpr int kSize = 400000; + +class RandomDistributionsTest : public testing::Test {}; + +TEST_F(RandomDistributionsTest, UniformBoundFunctions) { + using absl::IntervalClosedClosed; + using absl::IntervalClosedOpen; + using absl::IntervalOpenClosed; + using absl::IntervalOpenOpen; + using absl::random_internal::uniform_lower_bound; + using absl::random_internal::uniform_upper_bound; + + // absl::uniform_int_distribution natively assumes IntervalClosedClosed + // absl::uniform_real_distribution natively assumes IntervalClosedOpen + + EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, 0, 100), 1); + EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, 0, 100), 1); + EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, 0, 1.0), 0); + EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, 0, 1.0), 0); + EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, 0, 1.0), 0); + EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, 0, 1.0), 0); + + EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, 0, 100), 0); + EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, 0, 100), 0); + EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, 0, 1.0), 0); + EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, 0, 1.0), 0); + EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, 0, 1.0), 0); + EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, 0, 1.0), 0); + + EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, 0, 100), 99); + EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, 0, 100), 99); + EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, 0, 1.0), 1.0); + EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, 0, 1.0), 1.0); + EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, 0, 1.0), 1.0); + EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, 0, 1.0), 1.0); + + EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, 0, 100), 100); + EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0, 100), 100); + EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, 0, 1.0), 1.0); + EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, 0, 1.0), 1.0); + EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, 0, 1.0), 1.0); + EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, 0, 1.0), 1.0); + + // Negative value tests + EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, -100, -1), -99); + EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, -100, -1), -99); + EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, -2.0, -1.0), -2.0); + EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, -2.0, -1.0), -2.0); + EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, -2.0, -1.0), -2.0); + EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, -2.0, -1.0), -2.0); + + EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, -100, -1), -100); + EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, -100, -1), -100); + EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, -2.0, -1.0), -2.0); + EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, -2.0, -1.0), -2.0); + EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, -2.0, -1.0), + -2.0); + EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, -2.0, -1.0), -2.0); + + EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, -100, -1), -2); + EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, -100, -1), -2); + EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, -2.0, -1.0), -1.0); + EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, -2.0, -1.0), -1.0); + EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, -2.0, -1.0), -1.0); + EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, -2.0, -1.0), -1.0); + + EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, -100, -1), -1); + EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, -100, -1), -1); + EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, -2.0, -1.0), -1.0); + EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, -2.0, -1.0), -1.0); + EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, -2.0, -1.0), -1.0); + EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, -2.0, -1.0), + -1.0); + + // Edge cases: the next value toward itself is itself. + const double d = 1.0; + const float f = 1.0; + EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, d, d), d); + EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, f, f), f); + + EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, 1.0, 2.0), 1.0); + EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, +0.0), 1.0); + EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -0.0), 1.0); + EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0), 1.0); + + EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0.0f, + std::numeric_limits<float>::max()), + std::numeric_limits<float>::max()); + EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0.0, + std::numeric_limits<double>::max()), + std::numeric_limits<double>::max()); +} + +struct Invalid {}; + +template <typename A, typename B> +auto InferredUniformReturnT(int) + -> decltype(absl::Uniform(std::declval<absl::InsecureBitGen&>(), + std::declval<A>(), std::declval<B>())); + +template <typename, typename> +Invalid InferredUniformReturnT(...); + +template <typename TagType, typename A, typename B> +auto InferredTaggedUniformReturnT(int) + -> decltype(absl::Uniform(std::declval<TagType>(), + std::declval<absl::InsecureBitGen&>(), + std::declval<A>(), std::declval<B>())); + +template <typename, typename, typename> +Invalid InferredTaggedUniformReturnT(...); + +// Given types <A, B, Expect>, CheckArgsInferType() verifies that +// +// absl::Uniform(gen, A{}, B{}) +// +// returns the type "Expect". +// +// This interface can also be used to assert that a given absl::Uniform() +// overload does not exist / will not compile. Given types <A, B>, the +// expression +// +// decltype(absl::Uniform(..., std::declval<A>(), std::declval<B>())) +// +// will not compile, leaving the definition of InferredUniformReturnT<A, B> to +// resolve (via SFINAE) to the overload which returns type "Invalid". This +// allows tests to assert that an invocation such as +// +// absl::Uniform(gen, 1.23f, std::numeric_limits<int>::max() - 1) +// +// should not compile, since neither type, float nor int, can precisely +// represent both endpoint-values. Writing: +// +// CheckArgsInferType<float, int, Invalid>() +// +// will assert that this overload does not exist. +template <typename A, typename B, typename Expect> +void CheckArgsInferType() { + static_assert( + absl::conjunction< + std::is_same<Expect, decltype(InferredUniformReturnT<A, B>(0))>, + std::is_same<Expect, + decltype(InferredUniformReturnT<B, A>(0))>>::value, + ""); + static_assert( + absl::conjunction< + std::is_same<Expect, + decltype(InferredTaggedUniformReturnT< + absl::random_internal::IntervalOpenOpenT, A, B>( + 0))>, + std::is_same<Expect, + decltype(InferredTaggedUniformReturnT< + absl::random_internal::IntervalOpenOpenT, B, A>( + 0))>>::value, + ""); +} + +template <typename A, typename B, typename ExplicitRet> +auto ExplicitUniformReturnT(int) -> decltype( + absl::Uniform<ExplicitRet>(*std::declval<absl::InsecureBitGen*>(), + std::declval<A>(), std::declval<B>())); + +template <typename, typename, typename ExplicitRet> +Invalid ExplicitUniformReturnT(...); + +template <typename TagType, typename A, typename B, typename ExplicitRet> +auto ExplicitTaggedUniformReturnT(int) -> decltype(absl::Uniform<ExplicitRet>( + std::declval<TagType>(), *std::declval<absl::InsecureBitGen*>(), + std::declval<A>(), std::declval<B>())); + +template <typename, typename, typename, typename ExplicitRet> +Invalid ExplicitTaggedUniformReturnT(...); + +// Given types <A, B, Expect>, CheckArgsReturnExpectedType() verifies that +// +// absl::Uniform<Expect>(gen, A{}, B{}) +// +// returns the type "Expect", and that the function-overload has the signature +// +// Expect(URBG&, Expect, Expect) +template <typename A, typename B, typename Expect> +void CheckArgsReturnExpectedType() { + static_assert( + absl::conjunction< + std::is_same<Expect, + decltype(ExplicitUniformReturnT<A, B, Expect>(0))>, + std::is_same<Expect, decltype(ExplicitUniformReturnT<B, A, Expect>( + 0))>>::value, + ""); + static_assert( + absl::conjunction< + std::is_same<Expect, + decltype(ExplicitTaggedUniformReturnT< + absl::random_internal::IntervalOpenOpenT, A, B, + Expect>(0))>, + std::is_same<Expect, + decltype(ExplicitTaggedUniformReturnT< + absl::random_internal::IntervalOpenOpenT, B, A, + Expect>(0))>>::value, + ""); +} + +TEST_F(RandomDistributionsTest, UniformTypeInference) { + // Infers common types. + CheckArgsInferType<uint16_t, uint16_t, uint16_t>(); + CheckArgsInferType<uint32_t, uint32_t, uint32_t>(); + CheckArgsInferType<uint64_t, uint64_t, uint64_t>(); + CheckArgsInferType<int16_t, int16_t, int16_t>(); + CheckArgsInferType<int32_t, int32_t, int32_t>(); + CheckArgsInferType<int64_t, int64_t, int64_t>(); + CheckArgsInferType<float, float, float>(); + CheckArgsInferType<double, double, double>(); + + // Explicitly-specified return-values override inferences. + CheckArgsReturnExpectedType<int16_t, int16_t, int32_t>(); + CheckArgsReturnExpectedType<uint16_t, uint16_t, int32_t>(); + CheckArgsReturnExpectedType<int16_t, int16_t, int64_t>(); + CheckArgsReturnExpectedType<int16_t, int32_t, int64_t>(); + CheckArgsReturnExpectedType<int16_t, int32_t, double>(); + CheckArgsReturnExpectedType<float, float, double>(); + CheckArgsReturnExpectedType<int, int, int16_t>(); + + // Properly promotes uint16_t. + CheckArgsInferType<uint16_t, uint32_t, uint32_t>(); + CheckArgsInferType<uint16_t, uint64_t, uint64_t>(); + CheckArgsInferType<uint16_t, int32_t, int32_t>(); + CheckArgsInferType<uint16_t, int64_t, int64_t>(); + CheckArgsInferType<uint16_t, float, float>(); + CheckArgsInferType<uint16_t, double, double>(); + + // Properly promotes int16_t. + CheckArgsInferType<int16_t, int32_t, int32_t>(); + CheckArgsInferType<int16_t, int64_t, int64_t>(); + CheckArgsInferType<int16_t, float, float>(); + CheckArgsInferType<int16_t, double, double>(); + + // Invalid (u)int16_t-pairings do not compile. + // See "CheckArgsInferType" comments above, for how this is achieved. + CheckArgsInferType<uint16_t, int16_t, Invalid>(); + CheckArgsInferType<int16_t, uint32_t, Invalid>(); + CheckArgsInferType<int16_t, uint64_t, Invalid>(); + + // Properly promotes uint32_t. + CheckArgsInferType<uint32_t, uint64_t, uint64_t>(); + CheckArgsInferType<uint32_t, int64_t, int64_t>(); + CheckArgsInferType<uint32_t, double, double>(); + + // Properly promotes int32_t. + CheckArgsInferType<int32_t, int64_t, int64_t>(); + CheckArgsInferType<int32_t, double, double>(); + + // Invalid (u)int32_t-pairings do not compile. + CheckArgsInferType<uint32_t, int32_t, Invalid>(); + CheckArgsInferType<int32_t, uint64_t, Invalid>(); + CheckArgsInferType<int32_t, float, Invalid>(); + CheckArgsInferType<uint32_t, float, Invalid>(); + + // Invalid (u)int64_t-pairings do not compile. + CheckArgsInferType<uint64_t, int64_t, Invalid>(); + CheckArgsInferType<int64_t, float, Invalid>(); + CheckArgsInferType<int64_t, double, Invalid>(); + + // Properly promotes float. + CheckArgsInferType<float, double, double>(); + + // Examples. + absl::InsecureBitGen gen; + EXPECT_NE(1, absl::Uniform(gen, static_cast<uint16_t>(0), 1.0f)); + EXPECT_NE(1, absl::Uniform(gen, 0, 1.0)); + EXPECT_NE(1, absl::Uniform(absl::IntervalOpenOpen, gen, + static_cast<uint16_t>(0), 1.0f)); + EXPECT_NE(1, absl::Uniform(absl::IntervalOpenOpen, gen, 0, 1.0)); + EXPECT_NE(1, absl::Uniform(absl::IntervalOpenOpen, gen, -1, 1.0)); + EXPECT_NE(1, absl::Uniform<double>(absl::IntervalOpenOpen, gen, -1, 1)); + EXPECT_NE(1, absl::Uniform<float>(absl::IntervalOpenOpen, gen, 0, 1)); + EXPECT_NE(1, absl::Uniform<float>(gen, 0, 1)); +} + +TEST_F(RandomDistributionsTest, UniformNoBounds) { + absl::InsecureBitGen gen; + + absl::Uniform<uint8_t>(gen); + absl::Uniform<uint16_t>(gen); + absl::Uniform<uint32_t>(gen); + absl::Uniform<uint64_t>(gen); +} + +// TODO(lar): Validate properties of non-default interval-semantics. +TEST_F(RandomDistributionsTest, UniformReal) { + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + values[i] = absl::Uniform(gen, 0, 1.0); + } + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(0.5, moments.mean, 0.02); + EXPECT_NEAR(1 / 12.0, moments.variance, 0.02); + EXPECT_NEAR(0.0, moments.skewness, 0.02); + EXPECT_NEAR(9 / 5.0, moments.kurtosis, 0.02); +} + +TEST_F(RandomDistributionsTest, UniformInt) { + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + const int64_t kMax = 1000000000000ll; + int64_t j = absl::Uniform(absl::IntervalClosedClosed, gen, 0, kMax); + // convert to double. + values[i] = static_cast<double>(j) / static_cast<double>(kMax); + } + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(0.5, moments.mean, 0.02); + EXPECT_NEAR(1 / 12.0, moments.variance, 0.02); + EXPECT_NEAR(0.0, moments.skewness, 0.02); + EXPECT_NEAR(9 / 5.0, moments.kurtosis, 0.02); + + /* + // NOTE: These are not supported by absl::Uniform, which is specialized + // on integer and real valued types. + + enum E { E0, E1 }; // enum + enum S : int { S0, S1 }; // signed enum + enum U : unsigned int { U0, U1 }; // unsigned enum + + absl::Uniform(gen, E0, E1); + absl::Uniform(gen, S0, S1); + absl::Uniform(gen, U0, U1); + */ +} + +TEST_F(RandomDistributionsTest, Exponential) { + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + values[i] = absl::Exponential<double>(gen); + } + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(1.0, moments.mean, 0.02); + EXPECT_NEAR(1.0, moments.variance, 0.025); + EXPECT_NEAR(2.0, moments.skewness, 0.1); + EXPECT_LT(5.0, moments.kurtosis); +} + +TEST_F(RandomDistributionsTest, PoissonDefault) { + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + values[i] = absl::Poisson<int64_t>(gen); + } + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(1.0, moments.mean, 0.02); + EXPECT_NEAR(1.0, moments.variance, 0.02); + EXPECT_NEAR(1.0, moments.skewness, 0.025); + EXPECT_LT(2.0, moments.kurtosis); +} + +TEST_F(RandomDistributionsTest, PoissonLarge) { + constexpr double kMean = 100000000.0; + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + values[i] = absl::Poisson<int64_t>(gen, kMean); + } + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(kMean, moments.mean, kMean * 0.015); + EXPECT_NEAR(kMean, moments.variance, kMean * 0.015); + EXPECT_NEAR(std::sqrt(kMean), moments.skewness, kMean * 0.02); + EXPECT_LT(2.0, moments.kurtosis); +} + +TEST_F(RandomDistributionsTest, Bernoulli) { + constexpr double kP = 0.5151515151; + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + values[i] = absl::Bernoulli(gen, kP); + } + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(kP, moments.mean, 0.01); +} + +TEST_F(RandomDistributionsTest, Beta) { + constexpr double kAlpha = 2.0; + constexpr double kBeta = 3.0; + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + values[i] = absl::Beta(gen, kAlpha, kBeta); + } + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(0.4, moments.mean, 0.01); +} + +TEST_F(RandomDistributionsTest, Zipf) { + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + values[i] = absl::Zipf<int64_t>(gen, 100); + } + + // The mean of a zipf distribution is: H(N, s-1) / H(N,s). + // Given the parameter v = 1, this gives the following function: + // (Hn(100, 1) - Hn(1,1)) / (Hn(100,2) - Hn(1,2)) = 6.5944 + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(6.5944, moments.mean, 2000) << moments; +} + +TEST_F(RandomDistributionsTest, Gaussian) { + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + values[i] = absl::Gaussian<double>(gen); + } + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(0.0, moments.mean, 0.02); + EXPECT_NEAR(1.0, moments.variance, 0.04); + EXPECT_NEAR(0, moments.skewness, 0.2); + EXPECT_NEAR(3.0, moments.kurtosis, 0.5); +} + +TEST_F(RandomDistributionsTest, LogUniform) { + std::vector<double> values(kSize); + + absl::InsecureBitGen gen; + for (int i = 0; i < kSize; i++) { + values[i] = absl::LogUniform<int64_t>(gen, 0, (1 << 10) - 1); + } + + // The mean is the sum of the fractional means of the uniform distributions: + // [0..0][1..1][2..3][4..7][8..15][16..31][32..63] + // [64..127][128..255][256..511][512..1023] + const double mean = (0 + 1 + 1 + 2 + 3 + 4 + 7 + 8 + 15 + 16 + 31 + 32 + 63 + + 64 + 127 + 128 + 255 + 256 + 511 + 512 + 1023) / + (2.0 * 11.0); + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(mean, moments.mean, 2) << moments; +} + +} // namespace diff --git a/absl/random/examples_test.cc b/absl/random/examples_test.cc new file mode 100644 index 00000000..1dcb5146 --- /dev/null +++ b/absl/random/examples_test.cc @@ -0,0 +1,99 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cinttypes> +#include <random> +#include <sstream> +#include <vector> + +#include "gtest/gtest.h" +#include "absl/random/random.h" + +template <typename T> +void Use(T) {} + +TEST(Examples, Basic) { + absl::BitGen gen; + std::vector<int> objs = {10, 20, 30, 40, 50}; + + // Choose an element from a set. + auto elem = objs[absl::Uniform(gen, 0u, objs.size())]; + Use(elem); + + // Generate a uniform value between 1 and 6. + auto dice_roll = absl::Uniform<int>(absl::IntervalClosedClosed, gen, 1, 6); + Use(dice_roll); + + // Generate a random byte. + auto byte = absl::Uniform<uint8_t>(gen); + Use(byte); + + // Generate a fractional value from [0f, 1f). + auto fraction = absl::Uniform<float>(gen, 0, 1); + Use(fraction); + + // Toss a fair coin; 50/50 probability. + bool coin_toss = absl::Bernoulli(gen, 0.5); + Use(coin_toss); + + // Select a file size between 1k and 10MB, biased towards smaller file sizes. + auto file_size = absl::LogUniform<size_t>(gen, 1000, 10 * 1000 * 1000); + Use(file_size); + + // Randomize (shuffle) a collection. + std::shuffle(std::begin(objs), std::end(objs), gen); +} + +TEST(Examples, CreateingCorrelatedVariateSequences) { + // Unexpected PRNG correlation is often a source of bugs, + // so when using absl::BitGen it must be an intentional choice. + // NOTE: All of these only exhibit process-level stability. + + // Create a correlated sequence from system entropy. + { + auto my_seed = absl::MakeSeedSeq(); + + absl::BitGen gen_1(my_seed); + absl::BitGen gen_2(my_seed); // Produces same variates as gen_1. + + EXPECT_EQ(absl::Bernoulli(gen_1, 0.5), absl::Bernoulli(gen_2, 0.5)); + EXPECT_EQ(absl::Uniform<uint32_t>(gen_1), absl::Uniform<uint32_t>(gen_2)); + } + + // Create a correlated sequence from an existing URBG. + { + absl::BitGen gen; + + auto my_seed = absl::CreateSeedSeqFrom(&gen); + absl::BitGen gen_1(my_seed); + absl::BitGen gen_2(my_seed); + + EXPECT_EQ(absl::Bernoulli(gen_1, 0.5), absl::Bernoulli(gen_2, 0.5)); + EXPECT_EQ(absl::Uniform<uint32_t>(gen_1), absl::Uniform<uint32_t>(gen_2)); + } + + // An alternate construction which uses user-supplied data + // instead of a random seed. + { + const char kData[] = "A simple seed string"; + std::seed_seq my_seed(std::begin(kData), std::end(kData)); + + absl::BitGen gen_1(my_seed); + absl::BitGen gen_2(my_seed); + + EXPECT_EQ(absl::Bernoulli(gen_1, 0.5), absl::Bernoulli(gen_2, 0.5)); + EXPECT_EQ(absl::Uniform<uint32_t>(gen_1), absl::Uniform<uint32_t>(gen_2)); + } +} + diff --git a/absl/random/exponential_distribution.h b/absl/random/exponential_distribution.h new file mode 100644 index 00000000..ed5551ae --- /dev/null +++ b/absl/random/exponential_distribution.h @@ -0,0 +1,159 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_ +#define ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_ + +#include <cassert> +#include <cmath> +#include <istream> +#include <limits> +#include <type_traits> + +#include "absl/random/internal/distribution_impl.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/iostream_state_saver.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// absl::exponential_distribution: +// Generates a number conforming to an exponential distribution and is +// equivalent to the standard [rand.dist.pois.exp] distribution. +template <typename RealType = double> +class exponential_distribution { + public: + using result_type = RealType; + + class param_type { + public: + using distribution_type = exponential_distribution; + + explicit param_type(result_type lambda = 1) : lambda_(lambda) { + assert(lambda > 0); + neg_inv_lambda_ = -result_type(1) / lambda_; + } + + result_type lambda() const { return lambda_; } + + friend bool operator==(const param_type& a, const param_type& b) { + return a.lambda_ == b.lambda_; + } + + friend bool operator!=(const param_type& a, const param_type& b) { + return !(a == b); + } + + private: + friend class exponential_distribution; + + result_type lambda_; + result_type neg_inv_lambda_; + + static_assert( + std::is_floating_point<RealType>::value, + "Class-template absl::exponential_distribution<> must be parameterized " + "using a floating-point type."); + }; + + exponential_distribution() : exponential_distribution(1) {} + + explicit exponential_distribution(result_type lambda) : param_(lambda) {} + + explicit exponential_distribution(const param_type& p) : param_(p) {} + + void reset() {} + + // Generating functions + template <typename URBG> + result_type operator()(URBG& g) { // NOLINT(runtime/references) + return (*this)(g, param_); + } + + template <typename URBG> + result_type operator()(URBG& g, // NOLINT(runtime/references) + const param_type& p); + + param_type param() const { return param_; } + void param(const param_type& p) { param_ = p; } + + result_type(min)() const { return 0; } + result_type(max)() const { + return std::numeric_limits<result_type>::infinity(); + } + + result_type lambda() const { return param_.lambda(); } + + friend bool operator==(const exponential_distribution& a, + const exponential_distribution& b) { + return a.param_ == b.param_; + } + friend bool operator!=(const exponential_distribution& a, + const exponential_distribution& b) { + return a.param_ != b.param_; + } + + private: + param_type param_; + random_internal::FastUniformBits<uint64_t> fast_u64_; +}; + +// -------------------------------------------------------------------------- +// Implementation details follow +// -------------------------------------------------------------------------- + +template <typename RealType> +template <typename URBG> +typename exponential_distribution<RealType>::result_type +exponential_distribution<RealType>::operator()( + URBG& g, // NOLINT(runtime/references) + const param_type& p) { + using random_internal::NegativeValueT; + const result_type u = random_internal::RandU64ToReal< + result_type>::template Value<NegativeValueT, false>(fast_u64_(g)); + // log1p(-x) is mathematically equivalent to log(1 - x) but has more + // accuracy for x near zero. + return p.neg_inv_lambda_ * std::log1p(u); +} + +template <typename CharT, typename Traits, typename RealType> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const exponential_distribution<RealType>& x) { + auto saver = random_internal::make_ostream_state_saver(os); + os.precision(random_internal::stream_precision_helper<RealType>::kPrecision); + os << x.lambda(); + return os; +} + +template <typename CharT, typename Traits, typename RealType> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + exponential_distribution<RealType>& x) { // NOLINT(runtime/references) + using result_type = typename exponential_distribution<RealType>::result_type; + using param_type = typename exponential_distribution<RealType>::param_type; + result_type lambda; + + auto saver = random_internal::make_istream_state_saver(is); + lambda = random_internal::read_floating_point<result_type>(is); + if (!is.fail()) { + x.param(param_type(lambda)); + } + return is; +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_ diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc new file mode 100644 index 00000000..6f8865c2 --- /dev/null +++ b/absl/random/exponential_distribution_test.cc @@ -0,0 +1,422 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/exponential_distribution.h" + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <limits> +#include <random> +#include <sstream> +#include <string> +#include <type_traits> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/random/internal/chi_square.h" +#include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/strip.h" + +namespace { + +using absl::random_internal::kChiSquared; + +template <typename RealType> +class ExponentialDistributionTypedTest : public ::testing::Test {}; + +using RealTypes = ::testing::Types<float, double, long double>; +TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes); + +TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) { + using param_type = + typename absl::exponential_distribution<TypeParam>::param_type; + + const TypeParam kParams[] = { + // Cases around 1. + 1, // + std::nextafter(TypeParam(1), TypeParam(0)), // 1 - epsilon + std::nextafter(TypeParam(1), TypeParam(2)), // 1 + epsilon + // Typical cases. + TypeParam(1e-8), TypeParam(1e-4), TypeParam(1), TypeParam(2), + TypeParam(1e4), TypeParam(1e8), TypeParam(1e20), TypeParam(2.5), + // Boundary cases. + std::numeric_limits<TypeParam>::max(), + std::numeric_limits<TypeParam>::epsilon(), + std::nextafter(std::numeric_limits<TypeParam>::min(), + TypeParam(1)), // min + epsilon + std::numeric_limits<TypeParam>::min(), // smallest normal + // There are some errors dealing with denorms on apple platforms. + std::numeric_limits<TypeParam>::denorm_min(), // smallest denorm + std::numeric_limits<TypeParam>::min() / 2, // denorm + std::nextafter(std::numeric_limits<TypeParam>::min(), + TypeParam(0)), // denorm_max + }; + + constexpr int kCount = 1000; + absl::InsecureBitGen gen; + + for (const TypeParam lambda : kParams) { + // Some values may be invalid; skip those. + if (!std::isfinite(lambda)) continue; + ABSL_ASSERT(lambda > 0); + + const param_type param(lambda); + + absl::exponential_distribution<TypeParam> before(lambda); + EXPECT_EQ(before.lambda(), param.lambda()); + + { + absl::exponential_distribution<TypeParam> via_param(param); + EXPECT_EQ(via_param, before); + EXPECT_EQ(via_param.param(), before.param()); + } + + // Smoke test. + auto sample_min = before.max(); + auto sample_max = before.min(); + for (int i = 0; i < kCount; i++) { + auto sample = before(gen); + EXPECT_GE(sample, before.min()) << before; + EXPECT_LE(sample, before.max()) << before; + if (sample > sample_max) sample_max = sample; + if (sample < sample_min) sample_min = sample; + } + if (!std::is_same<TypeParam, long double>::value) { + ABSL_INTERNAL_LOG(INFO, + absl::StrFormat("Range {%f}: %f, %f, lambda=%f", lambda, + sample_min, sample_max, lambda)); + } + + std::stringstream ss; + ss << before; + + if (!std::isfinite(lambda)) { + // Streams do not deserialize inf/nan correctly. + continue; + } + // Validate stream serialization. + absl::exponential_distribution<TypeParam> after(34.56f); + + EXPECT_NE(before.lambda(), after.lambda()); + EXPECT_NE(before.param(), after.param()); + EXPECT_NE(before, after); + + ss >> after; + +#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ + defined(__ppc__) || defined(__PPC__) + if (std::is_same<TypeParam, long double>::value) { + // Roundtripping floating point values requires sufficient precision to + // reconstruct the exact value. It turns out that long double has some + // errors doing this on ppc, particularly for values + // near {1.0 +/- epsilon}. + if (lambda <= std::numeric_limits<double>::max() && + lambda >= std::numeric_limits<double>::lowest()) { + EXPECT_EQ(static_cast<double>(before.lambda()), + static_cast<double>(after.lambda())) + << ss.str(); + } + continue; + } +#endif + + EXPECT_EQ(before.lambda(), after.lambda()) // + << ss.str() << " " // + << (ss.good() ? "good " : "") // + << (ss.bad() ? "bad " : "") // + << (ss.eof() ? "eof " : "") // + << (ss.fail() ? "fail " : ""); + } +} + +// http://www.itl.nist.gov/div898/handbook/eda/section3/eda3667.htm + +class ExponentialModel { + public: + explicit ExponentialModel(double lambda) + : lambda_(lambda), beta_(1.0 / lambda) {} + + double lambda() const { return lambda_; } + + double mean() const { return beta_; } + double variance() const { return beta_ * beta_; } + double stddev() const { return std::sqrt(variance()); } + double skew() const { return 2; } + double kurtosis() const { return 6.0; } + + double CDF(double x) { return 1.0 - std::exp(-lambda_ * x); } + + // The inverse CDF, or PercentPoint function of the distribution + double InverseCDF(double p) { + ABSL_ASSERT(p >= 0.0); + ABSL_ASSERT(p < 1.0); + return -beta_ * std::log(1.0 - p); + } + + private: + const double lambda_; + const double beta_; +}; + +struct Param { + double lambda; + double p_fail; + int trials; +}; + +class ExponentialDistributionTests : public testing::TestWithParam<Param>, + public ExponentialModel { + public: + ExponentialDistributionTests() : ExponentialModel(GetParam().lambda) {} + + // SingleZTest provides a basic z-squared test of the mean vs. expected + // mean for data generated by the poisson distribution. + template <typename D> + bool SingleZTest(const double p, const size_t samples); + + // SingleChiSquaredTest provides a basic chi-squared test of the normal + // distribution. + template <typename D> + double SingleChiSquaredTest(); + + absl::InsecureBitGen rng_; +}; + +template <typename D> +bool ExponentialDistributionTests::SingleZTest(const double p, + const size_t samples) { + D dis(lambda()); + + std::vector<double> data; + data.reserve(samples); + for (size_t i = 0; i < samples; i++) { + const double x = dis(rng_); + data.push_back(x); + } + + const auto m = absl::random_internal::ComputeDistributionMoments(data); + const double max_err = absl::random_internal::MaxErrorTolerance(p); + const double z = absl::random_internal::ZScore(mean(), m); + const bool pass = absl::random_internal::Near("z", z, 0.0, max_err); + + if (!pass) { + ABSL_INTERNAL_LOG( + INFO, absl::StrFormat("p=%f max_err=%f\n" + " lambda=%f\n" + " mean=%f vs. %f\n" + " stddev=%f vs. %f\n" + " skewness=%f vs. %f\n" + " kurtosis=%f vs. %f\n" + " z=%f vs. 0", + p, max_err, lambda(), m.mean, mean(), + std::sqrt(m.variance), stddev(), m.skewness, + skew(), m.kurtosis, kurtosis(), z)); + } + return pass; +} + +template <typename D> +double ExponentialDistributionTests::SingleChiSquaredTest() { + const size_t kSamples = 10000; + const int kBuckets = 50; + + // The InverseCDF is the percent point function of the distribution, and can + // be used to assign buckets roughly uniformly. + std::vector<double> cutoffs; + const double kInc = 1.0 / static_cast<double>(kBuckets); + for (double p = kInc; p < 1.0; p += kInc) { + cutoffs.push_back(InverseCDF(p)); + } + if (cutoffs.back() != std::numeric_limits<double>::infinity()) { + cutoffs.push_back(std::numeric_limits<double>::infinity()); + } + + D dis(lambda()); + + std::vector<int32_t> counts(cutoffs.size(), 0); + for (int j = 0; j < kSamples; j++) { + const double x = dis(rng_); + auto it = std::upper_bound(cutoffs.begin(), cutoffs.end(), x); + counts[std::distance(cutoffs.begin(), it)]++; + } + + // Null-hypothesis is that the distribution is exponentially distributed + // with the provided lambda (not estimated from the data). + const int dof = static_cast<int>(counts.size()) - 1; + + // Our threshold for logging is 1-in-50. + const double threshold = absl::random_internal::ChiSquareValue(dof, 0.98); + + const double expected = + static_cast<double>(kSamples) / static_cast<double>(counts.size()); + + double chi_square = absl::random_internal::ChiSquareWithExpected( + std::begin(counts), std::end(counts), expected); + double p = absl::random_internal::ChiSquarePValue(chi_square, dof); + + if (chi_square > threshold) { + for (int i = 0; i < cutoffs.size(); i++) { + ABSL_INTERNAL_LOG( + INFO, absl::StrFormat("%d : (%f) = %d", i, cutoffs[i], counts[i])); + } + + ABSL_INTERNAL_LOG(INFO, + absl::StrCat("lambda ", lambda(), "\n", // + " expected ", expected, "\n", // + kChiSquared, " ", chi_square, " (", p, ")\n", + kChiSquared, " @ 0.98 = ", threshold)); + } + return p; +} + +TEST_P(ExponentialDistributionTests, ZTest) { + const size_t kSamples = 10000; + const auto& param = GetParam(); + const int expected_failures = + std::max(1, static_cast<int>(std::ceil(param.trials * param.p_fail))); + const double p = absl::random_internal::RequiredSuccessProbability( + param.p_fail, param.trials); + + int failures = 0; + for (int i = 0; i < param.trials; i++) { + failures += SingleZTest<absl::exponential_distribution<double>>(p, kSamples) + ? 0 + : 1; + } + EXPECT_LE(failures, expected_failures); +} + +TEST_P(ExponentialDistributionTests, ChiSquaredTest) { + const int kTrials = 20; + int failures = 0; + + for (int i = 0; i < kTrials; i++) { + double p_value = + SingleChiSquaredTest<absl::exponential_distribution<double>>(); + if (p_value < 0.005) { // 1/200 + failures++; + } + } + + // There is a 0.10% chance of producing at least one failure, so raise the + // failure threshold high enough to allow for a flake rate < 10,000. + EXPECT_LE(failures, 4); +} + +std::vector<Param> GenParams() { + return { + Param{1.0, 0.02, 100}, + Param{2.5, 0.02, 100}, + Param{10, 0.02, 100}, + // large + Param{1e4, 0.02, 100}, + Param{1e9, 0.02, 100}, + // small + Param{0.1, 0.02, 100}, + Param{1e-3, 0.02, 100}, + Param{1e-5, 0.02, 100}, + }; +} + +std::string ParamName(const ::testing::TestParamInfo<Param>& info) { + const auto& p = info.param; + std::string name = absl::StrCat("lambda_", absl::SixDigits(p.lambda)); + return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); +} + +INSTANTIATE_TEST_CASE_P(, ExponentialDistributionTests, + ::testing::ValuesIn(GenParams()), ParamName); + +// NOTE: absl::exponential_distribution is not guaranteed to be stable. +TEST(ExponentialDistributionTest, StabilityTest) { + // absl::exponential_distribution stability relies on std::log1p and + // absl::uniform_real_distribution. + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector<int> output(14); + + { + absl::exponential_distribution<double> dist; + std::generate(std::begin(output), std::end(output), + [&] { return static_cast<int>(10000.0 * dist(urbg)); }); + + EXPECT_EQ(14, urbg.invocations()); + EXPECT_THAT(output, + testing::ElementsAre(0, 71913, 14375, 5039, 1835, 861, 25936, + 804, 126, 12337, 17984, 27002, 0, 71913)); + } + + urbg.reset(); + { + absl::exponential_distribution<float> dist; + std::generate(std::begin(output), std::end(output), + [&] { return static_cast<int>(10000.0f * dist(urbg)); }); + + EXPECT_EQ(14, urbg.invocations()); + EXPECT_THAT(output, + testing::ElementsAre(0, 71913, 14375, 5039, 1835, 861, 25936, + 804, 126, 12337, 17984, 27002, 0, 71913)); + } +} + +TEST(ExponentialDistributionTest, AlgorithmBounds) { + // Relies on absl::uniform_real_distribution, so some of these comments + // reference that. + absl::exponential_distribution<double> dist; + + { + // This returns the smallest value >0 from absl::uniform_real_distribution. + absl::random_internal::sequence_urbg urbg({0x0000000000000001ull}); + double a = dist(urbg); + EXPECT_EQ(a, 5.42101086242752217004e-20); + } + + { + // This returns a value very near 0.5 from absl::uniform_real_distribution. + absl::random_internal::sequence_urbg urbg({0x7fffffffffffffefull}); + double a = dist(urbg); + EXPECT_EQ(a, 0.693147180559945175204); + } + + { + // This returns the largest value <1 from absl::uniform_real_distribution. + // WolframAlpha: ~39.1439465808987766283058547296341915292187253 + absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFeFull}); + double a = dist(urbg); + EXPECT_EQ(a, 36.7368005696771007251); + } + { + // This *ALSO* returns the largest value <1. + absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFFFull}); + double a = dist(urbg); + EXPECT_EQ(a, 36.7368005696771007251); + } +} + +} // namespace diff --git a/absl/random/gaussian_distribution.cc b/absl/random/gaussian_distribution.cc new file mode 100644 index 00000000..dbc2d848 --- /dev/null +++ b/absl/random/gaussian_distribution.cc @@ -0,0 +1,104 @@ +// BEGIN GENERATED CODE; DO NOT EDIT +// clang-format off + +#include "absl/random/gaussian_distribution.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +const gaussian_distribution_base::Tables + gaussian_distribution_base::zg_ = { + {3.7130862467425505, 3.442619855899000214, 3.223084984581141565, + 3.083228858216868318, 2.978696252647779819, 2.894344007021528942, + 2.82312535054891045, 2.761169372387176857, 2.706113573121819549, + 2.656406411261359679, 2.610972248431847387, 2.56903362592493778, + 2.530009672388827457, 2.493454522095372106, 2.459018177411830486, + 2.426420645533749809, 2.395434278011062457, 2.365871370117638595, + 2.337575241339236776, 2.310413683698762988, 2.284274059677471769, + 2.25905957386919809, 2.234686395590979036, 2.21108140887870297, + 2.188180432076048731, 2.165926793748921497, 2.144270182360394905, + 2.123165708673976138, 2.102573135189237608, 2.082456237992015957, + 2.062782274508307978, 2.043521536655067194, 2.02464697337738464, + 2.006133869963471206, 1.987959574127619033, 1.970103260854325633, + 1.952545729553555764, 1.935269228296621957, 1.918257300864508963, + 1.901494653105150423, 1.884967035707758143, 1.868661140994487768, + 1.852564511728090002, 1.836665460258444904, 1.820952996596124418, + 1.805416764219227366, 1.790046982599857506, 1.77483439558606837, + 1.759770224899592339, 1.744846128113799244, 1.730054160563729182, + 1.71538674071366648, 1.700836618569915748, 1.686396846779167014, + 1.6720607540975998, 1.657821920954023254, 1.643674156862867441, + 1.629611479470633562, 1.615628095043159629, 1.601718380221376581, + 1.587876864890574558, 1.574098216022999264, 1.560377222366167382, + 1.546708779859908844, 1.533087877674041755, 1.519509584765938559, + 1.505969036863201937, 1.492461423781352714, 1.478981976989922842, + 1.465525957342709296, 1.452088642889222792, 1.438665316684561546, + 1.425251254514058319, 1.411841712447055919, 1.398431914131003539, + 1.385017037732650058, 1.371592202427340812, 1.358152454330141534, + 1.34469275175354519, 1.331207949665625279, 1.317692783209412299, + 1.304141850128615054, 1.290549591926194894, 1.27691027356015363, + 1.263217961454619287, 1.249466499573066436, 1.23564948326336066, + 1.221760230539994385, 1.207791750415947662, 1.193736707833126465, + 1.17958738466398616, 1.165335636164750222, 1.150972842148865416, + 1.136489852013158774, 1.121876922582540237, 1.107123647534034028, + 1.092218876907275371, 1.077150624892893482, 1.061905963694822042, + 1.046470900764042922, 1.030830236068192907, 1.014967395251327842, + 0.9988642334929808131, 0.9825008035154263464, 0.9658550794011470098, + 0.9489026255113034436, 0.9316161966151479401, 0.9139652510230292792, + 0.8959153525809346874, 0.8774274291129204872, 0.8584568431938099931, + 0.8389522142975741614, 0.8188539067003538507, 0.7980920606440534693, + 0.7765839878947563557, 0.7542306644540520688, 0.7309119106424850631, + 0.7064796113354325779, 0.6807479186691505202, 0.6534786387399710295, + 0.6243585973360461505, 0.5929629424714434327, 0.5586921784081798625, + 0.5206560387620546848, 0.4774378372966830431, 0.4265479863554152429, + 0.3628714310970211909, 0.2723208648139477384, 0}, + {0.001014352564120377413, 0.002669629083880922793, 0.005548995220771345792, + 0.008624484412859888607, 0.01183947865788486861, 0.01516729801054656976, + 0.01859210273701129151, 0.02210330461592709475, 0.02569329193593428151, + 0.02935631744000685023, 0.03308788614622575758, 0.03688438878665621645, + 0.04074286807444417458, 0.04466086220049143157, 0.04863629585986780496, + 0.05266740190305100461, 0.05675266348104984759, 0.06089077034804041277, + 0.06508058521306804567, 0.06932111739357792179, 0.07361150188411341722, + 0.07795098251397346301, 0.08233889824223575293, 0.08677467189478028919, + 0.09125780082683036809, 0.095787849121731522, 0.1003644410286559929, + 0.1049872554094214289, 0.1096560210148404546, 0.1143705124488661323, + 0.1191305467076509556, 0.1239359802028679736, 0.1287867061959434012, + 0.1336826525834396151, 0.1386237799845948804, 0.1436100800906280339, + 0.1486415742423425057, 0.1537183122081819397, 0.1588403711394795748, + 0.1640078546834206341, 0.1692208922373653057, 0.1744796383307898324, + 0.1797842721232958407, 0.1851349970089926078, 0.1905320403191375633, + 0.1959756531162781534, 0.2014661100743140865, 0.2070037094399269362, + 0.2125887730717307134, 0.2182216465543058426, 0.2239026993850088965, + 0.229632325232116602, 0.2354109422634795556, 0.2412389935454402889, + 0.2471169475123218551, 0.2530452985073261551, 0.2590245673962052742, + 0.2650553022555897087, 0.271138079138385224, 0.2772735029191887857, + 0.2834622082232336471, 0.2897048604429605656, 0.2960021568469337061, + 0.3023548277864842593, 0.3087636380061818397, 0.3152293880650116065, + 0.3217529158759855901, 0.3283350983728509642, 0.3349768533135899506, + 0.3416791412315512977, 0.3484429675463274756, 0.355269384847918035, + 0.3621594953693184626, 0.3691144536644731522, 0.376135469510563536, + 0.3832238110559021416, 0.3903808082373155797, 0.3976078564938743676, + 0.404906420807223999, 0.4122780401026620578, 0.4197243320495753771, + 0.4272469983049970721, 0.4348478302499918513, 0.4425287152754694975, + 0.4502916436820402768, 0.458138716267873114, 0.4660721526894572309, + 0.4740943006930180559, 0.4822076463294863724, 0.4904148252838453348, + 0.4987186354709807201, 0.5071220510755701794, 0.5156282382440030565, + 0.5242405726729852944, 0.5329626593838373561, 0.5417983550254266145, + 0.5507517931146057588, 0.5598274127040882009, 0.5690299910679523787, + 0.5783646811197646898, 0.5878370544347081283, 0.5974531509445183408, + 0.6072195366251219584, 0.6171433708188825973, 0.6272324852499290282, + 0.6374954773350440806, 0.6479418211102242475, 0.6585820000500898219, + 0.6694276673488921414, 0.6804918409973358395, 0.6917891434366769676, + 0.7033360990161600101, 0.7151515074105005976, 0.7272569183441868201, + 0.7396772436726493094, 0.7524415591746134169, 0.7655841738977066102, + 0.7791460859296898134, 0.7931770117713072832, 0.8077382946829627652, + 0.8229072113814113187, 0.8387836052959920519, 0.8555006078694531446, + 0.873243048910072206, 0.8922816507840289901, 0.9130436479717434217, + 0.9362826816850632339, 0.9635996931270905952, 1}}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +// clang-format on +// END GENERATED CODE diff --git a/absl/random/gaussian_distribution.h b/absl/random/gaussian_distribution.h new file mode 100644 index 00000000..8ee95148 --- /dev/null +++ b/absl/random/gaussian_distribution.h @@ -0,0 +1,262 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_ +#define ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_ + +// absl::gaussian_distribution implements the Ziggurat algorithm +// for generating random gaussian numbers. +// +// Implementation based on "The Ziggurat Method for Generating Random Variables" +// by George Marsaglia and Wai Wan Tsang: http://www.jstatsoft.org/v05/i08/ +// + +#include <cmath> +#include <cstdint> +#include <istream> +#include <limits> +#include <type_traits> + +#include "absl/random/internal/distribution_impl.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/iostream_state_saver.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// absl::gaussian_distribution_base implements the underlying ziggurat algorithm +// using the ziggurat tables generated by the gaussian_distribution_gentables +// binary. +// +// The specific algorithm has some of the improvements suggested by the +// 2005 paper, "An Improved Ziggurat Method to Generate Normal Random Samples", +// Jurgen A Doornik. (https://www.doornik.com/research/ziggurat.pdf) +class gaussian_distribution_base { + public: + template <typename URBG> + inline double zignor(URBG& g); // NOLINT(runtime/references) + + private: + friend class TableGenerator; + + template <typename URBG> + inline double zignor_fallback(URBG& g, // NOLINT(runtime/references) + bool neg); + + // Constants used for the gaussian distribution. + static constexpr double kR = 3.442619855899; // Start of the tail. + static constexpr double kRInv = 0.29047645161474317; // ~= (1.0 / kR) . + static constexpr double kV = 9.91256303526217e-3; + static constexpr uint64_t kMask = 0x07f; + + // The ziggurat tables store the pdf(f) and inverse-pdf(x) for equal-area + // points on one-half of the normal distribution, where the pdf function, + // pdf = e ^ (-1/2 *x^2), assumes that the mean = 0 & stddev = 1. + // + // These tables are just over 2kb in size; larger tables might improve the + // distributions, but also lead to more cache pollution. + // + // x = {3.71308, 3.44261, 3.22308, ..., 0} + // f = {0.00101, 0.00266, 0.00554, ..., 1} + struct Tables { + double x[kMask + 2]; + double f[kMask + 2]; + }; + static const Tables zg_; + random_internal::FastUniformBits<uint64_t> fast_u64_; +}; + +} // namespace random_internal + +// absl::gaussian_distribution: +// Generates a number conforming to a Gaussian distribution. +template <typename RealType = double> +class gaussian_distribution : random_internal::gaussian_distribution_base { + public: + using result_type = RealType; + + class param_type { + public: + using distribution_type = gaussian_distribution; + + explicit param_type(result_type mean = 0, result_type stddev = 1) + : mean_(mean), stddev_(stddev) {} + + // Returns the mean distribution parameter. The mean specifies the location + // of the peak. The default value is 0.0. + result_type mean() const { return mean_; } + + // Returns the deviation distribution parameter. The default value is 1.0. + result_type stddev() const { return stddev_; } + + friend bool operator==(const param_type& a, const param_type& b) { + return a.mean_ == b.mean_ && a.stddev_ == b.stddev_; + } + + friend bool operator!=(const param_type& a, const param_type& b) { + return !(a == b); + } + + private: + result_type mean_; + result_type stddev_; + + static_assert( + std::is_floating_point<RealType>::value, + "Class-template absl::gaussian_distribution<> must be parameterized " + "using a floating-point type."); + }; + + gaussian_distribution() : gaussian_distribution(0) {} + + explicit gaussian_distribution(result_type mean, result_type stddev = 1) + : param_(mean, stddev) {} + + explicit gaussian_distribution(const param_type& p) : param_(p) {} + + void reset() {} + + // Generating functions + template <typename URBG> + result_type operator()(URBG& g) { // NOLINT(runtime/references) + return (*this)(g, param_); + } + + template <typename URBG> + result_type operator()(URBG& g, // NOLINT(runtime/references) + const param_type& p); + + param_type param() const { return param_; } + void param(const param_type& p) { param_ = p; } + + result_type(min)() const { + return -std::numeric_limits<result_type>::infinity(); + } + result_type(max)() const { + return std::numeric_limits<result_type>::infinity(); + } + + result_type mean() const { return param_.mean(); } + result_type stddev() const { return param_.stddev(); } + + friend bool operator==(const gaussian_distribution& a, + const gaussian_distribution& b) { + return a.param_ == b.param_; + } + friend bool operator!=(const gaussian_distribution& a, + const gaussian_distribution& b) { + return a.param_ != b.param_; + } + + private: + param_type param_; +}; + +// -------------------------------------------------------------------------- +// Implementation details only below +// -------------------------------------------------------------------------- + +template <typename RealType> +template <typename URBG> +typename gaussian_distribution<RealType>::result_type +gaussian_distribution<RealType>::operator()( + URBG& g, // NOLINT(runtime/references) + const param_type& p) { + return p.mean() + p.stddev() * static_cast<result_type>(zignor(g)); +} + +template <typename CharT, typename Traits, typename RealType> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const gaussian_distribution<RealType>& x) { + auto saver = random_internal::make_ostream_state_saver(os); + os.precision(random_internal::stream_precision_helper<RealType>::kPrecision); + os << x.mean() << os.fill() << x.stddev(); + return os; +} + +template <typename CharT, typename Traits, typename RealType> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + gaussian_distribution<RealType>& x) { // NOLINT(runtime/references) + using result_type = typename gaussian_distribution<RealType>::result_type; + using param_type = typename gaussian_distribution<RealType>::param_type; + + auto saver = random_internal::make_istream_state_saver(is); + auto mean = random_internal::read_floating_point<result_type>(is); + if (is.fail()) return is; + auto stddev = random_internal::read_floating_point<result_type>(is); + if (!is.fail()) { + x.param(param_type(mean, stddev)); + } + return is; +} + +namespace random_internal { + +template <typename URBG> +inline double gaussian_distribution_base::zignor_fallback(URBG& g, bool neg) { + // This fallback path happens approximately 0.05% of the time. + double x, y; + do { + // kRInv = 1/r, U(0, 1) + x = kRInv * std::log(RandU64ToDouble<PositiveValueT, false>(fast_u64_(g))); + y = -std::log(RandU64ToDouble<PositiveValueT, false>(fast_u64_(g))); + } while ((y + y) < (x * x)); + return neg ? (x - kR) : (kR - x); +} + +template <typename URBG> +inline double gaussian_distribution_base::zignor( + URBG& g) { // NOLINT(runtime/references) + while (true) { + // We use a single uint64_t to generate both a double and a strip. + // These bits are unused when the generated double is > 1/2^5. + // This may introduce some bias from the duplicated low bits of small + // values (those smaller than 1/2^5, which all end up on the left tail). + uint64_t bits = fast_u64_(g); + int i = static_cast<int>(bits & kMask); // pick a random strip + double j = RandU64ToDouble<SignedValueT, false>(bits); // U(-1, 1) + const double x = j * zg_.x[i]; + + // Retangular box. Handles >97% of all cases. + // For any given box, this handles between 75% and 99% of values. + // Equivalent to U(01) < (x[i+1] / x[i]), and when i == 0, ~93.5% + if (std::abs(x) < zg_.x[i + 1]) { + return x; + } + + // i == 0: Base box. Sample using a ratio of uniforms. + if (i == 0) { + // This path happens about 0.05% of the time. + return zignor_fallback(g, j < 0); + } + + // i > 0: Wedge samples using precomputed values. + double v = RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)); // U(0, 1) + if ((zg_.f[i + 1] + v * (zg_.f[i] - zg_.f[i + 1])) < + std::exp(-0.5 * x * x)) { + return x; + } + + // The wedge was missed; reject the value and try again. + } +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_ diff --git a/absl/random/gaussian_distribution_test.cc b/absl/random/gaussian_distribution_test.cc new file mode 100644 index 00000000..47c2989d --- /dev/null +++ b/absl/random/gaussian_distribution_test.cc @@ -0,0 +1,573 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/gaussian_distribution.h" + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <ios> +#include <iterator> +#include <random> +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/random/internal/chi_square.h" +#include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/strip.h" + +namespace { + +using absl::random_internal::kChiSquared; + +template <typename RealType> +class GaussianDistributionInterfaceTest : public ::testing::Test {}; + +using RealTypes = ::testing::Types<float, double, long double>; +TYPED_TEST_CASE(GaussianDistributionInterfaceTest, RealTypes); + +TYPED_TEST(GaussianDistributionInterfaceTest, SerializeTest) { + using param_type = + typename absl::gaussian_distribution<TypeParam>::param_type; + + const TypeParam kParams[] = { + // Cases around 1. + 1, // + std::nextafter(TypeParam(1), TypeParam(0)), // 1 - epsilon + std::nextafter(TypeParam(1), TypeParam(2)), // 1 + epsilon + // Arbitrary values. + TypeParam(1e-8), TypeParam(1e-4), TypeParam(2), TypeParam(1e4), + TypeParam(1e8), TypeParam(1e20), TypeParam(2.5), + // Boundary cases. + std::numeric_limits<TypeParam>::infinity(), + std::numeric_limits<TypeParam>::max(), + std::numeric_limits<TypeParam>::epsilon(), + std::nextafter(std::numeric_limits<TypeParam>::min(), + TypeParam(1)), // min + epsilon + std::numeric_limits<TypeParam>::min(), // smallest normal + // There are some errors dealing with denorms on apple platforms. + std::numeric_limits<TypeParam>::denorm_min(), // smallest denorm + std::numeric_limits<TypeParam>::min() / 2, + std::nextafter(std::numeric_limits<TypeParam>::min(), + TypeParam(0)), // denorm_max + }; + + constexpr int kCount = 1000; + absl::InsecureBitGen gen; + + // Use a loop to generate the combinations of {+/-x, +/-y}, and assign x, y to + // all values in kParams, + for (const auto mod : {0, 1, 2, 3}) { + for (const auto x : kParams) { + if (!std::isfinite(x)) continue; + for (const auto y : kParams) { + const TypeParam mean = (mod & 0x1) ? -x : x; + const TypeParam stddev = (mod & 0x2) ? -y : y; + const param_type param(mean, stddev); + + absl::gaussian_distribution<TypeParam> before(mean, stddev); + EXPECT_EQ(before.mean(), param.mean()); + EXPECT_EQ(before.stddev(), param.stddev()); + + { + absl::gaussian_distribution<TypeParam> via_param(param); + EXPECT_EQ(via_param, before); + EXPECT_EQ(via_param.param(), before.param()); + } + + // Smoke test. + auto sample_min = before.max(); + auto sample_max = before.min(); + for (int i = 0; i < kCount; i++) { + auto sample = before(gen); + if (sample > sample_max) sample_max = sample; + if (sample < sample_min) sample_min = sample; + EXPECT_GE(sample, before.min()) << before; + EXPECT_LE(sample, before.max()) << before; + } + if (!std::is_same<TypeParam, long double>::value) { + ABSL_INTERNAL_LOG( + INFO, absl::StrFormat("Range{%f, %f}: %f, %f", mean, stddev, + sample_min, sample_max)); + } + + std::stringstream ss; + ss << before; + + if (!std::isfinite(mean) || !std::isfinite(stddev)) { + // Streams do not parse inf/nan. + continue; + } + + // Validate stream serialization. + absl::gaussian_distribution<TypeParam> after(-0.53f, 2.3456f); + + EXPECT_NE(before.mean(), after.mean()); + EXPECT_NE(before.stddev(), after.stddev()); + EXPECT_NE(before.param(), after.param()); + EXPECT_NE(before, after); + + ss >> after; + +#if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ + defined(__ppc__) || defined(__PPC__) + if (std::is_same<TypeParam, long double>::value) { + // Roundtripping floating point values requires sufficient precision + // to reconstruct the exact value. It turns out that long double + // has some errors doing this on ppc, particularly for values + // near {1.0 +/- epsilon}. + if (mean <= std::numeric_limits<double>::max() && + mean >= std::numeric_limits<double>::lowest()) { + EXPECT_EQ(static_cast<double>(before.mean()), + static_cast<double>(after.mean())) + << ss.str(); + } + if (stddev <= std::numeric_limits<double>::max() && + stddev >= std::numeric_limits<double>::lowest()) { + EXPECT_EQ(static_cast<double>(before.stddev()), + static_cast<double>(after.stddev())) + << ss.str(); + } + continue; + } +#endif + + EXPECT_EQ(before.mean(), after.mean()); + EXPECT_EQ(before.stddev(), after.stddev()) // + << ss.str() << " " // + << (ss.good() ? "good " : "") // + << (ss.bad() ? "bad " : "") // + << (ss.eof() ? "eof " : "") // + << (ss.fail() ? "fail " : ""); + } + } + } +} + +// http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm + +class GaussianModel { + public: + GaussianModel(double mean, double stddev) : mean_(mean), stddev_(stddev) {} + + double mean() const { return mean_; } + double variance() const { return stddev() * stddev(); } + double stddev() const { return stddev_; } + double skew() const { return 0; } + double kurtosis() const { return 3.0; } + + // The inverse CDF, or PercentPoint function. + double InverseCDF(double p) { + ABSL_ASSERT(p >= 0.0); + ABSL_ASSERT(p < 1.0); + return mean() + stddev() * -absl::random_internal::InverseNormalSurvival(p); + } + + private: + const double mean_; + const double stddev_; +}; + +struct Param { + double mean; + double stddev; + double p_fail; // Z-Test probability of failure. + int trials; // Z-Test trials. +}; + +// GaussianDistributionTests implements a z-test for the gaussian +// distribution. +class GaussianDistributionTests : public testing::TestWithParam<Param>, + public GaussianModel { + public: + GaussianDistributionTests() + : GaussianModel(GetParam().mean, GetParam().stddev) {} + + // SingleZTest provides a basic z-squared test of the mean vs. expected + // mean for data generated by the poisson distribution. + template <typename D> + bool SingleZTest(const double p, const size_t samples); + + // SingleChiSquaredTest provides a basic chi-squared test of the normal + // distribution. + template <typename D> + double SingleChiSquaredTest(); + + absl::InsecureBitGen rng_; +}; + +template <typename D> +bool GaussianDistributionTests::SingleZTest(const double p, + const size_t samples) { + D dis(mean(), stddev()); + + std::vector<double> data; + data.reserve(samples); + for (size_t i = 0; i < samples; i++) { + const double x = dis(rng_); + data.push_back(x); + } + + const double max_err = absl::random_internal::MaxErrorTolerance(p); + const auto m = absl::random_internal::ComputeDistributionMoments(data); + const double z = absl::random_internal::ZScore(mean(), m); + const bool pass = absl::random_internal::Near("z", z, 0.0, max_err); + + // NOTE: Informational statistical test: + // + // Compute the Jarque-Bera test statistic given the excess skewness + // and kurtosis. The statistic is drawn from a chi-square(2) distribution. + // https://en.wikipedia.org/wiki/Jarque%E2%80%93Bera_test + // + // The null-hypothesis (normal distribution) is rejected when + // (p = 0.05 => jb > 5.99) + // (p = 0.01 => jb > 9.21) + // NOTE: JB has a large type-I error rate, so it will reject the + // null-hypothesis even when it is true more often than the z-test. + // + const double jb = + static_cast<double>(m.n) / 6.0 * + (std::pow(m.skewness, 2.0) + std::pow(m.kurtosis - 3.0, 2.0) / 4.0); + + if (!pass || jb > 9.21) { + ABSL_INTERNAL_LOG( + INFO, absl::StrFormat("p=%f max_err=%f\n" + " mean=%f vs. %f\n" + " stddev=%f vs. %f\n" + " skewness=%f vs. %f\n" + " kurtosis=%f vs. %f\n" + " z=%f vs. 0\n" + " jb=%f vs. 9.21", + p, max_err, m.mean, mean(), std::sqrt(m.variance), + stddev(), m.skewness, skew(), m.kurtosis, + kurtosis(), z, jb)); + } + return pass; +} + +template <typename D> +double GaussianDistributionTests::SingleChiSquaredTest() { + const size_t kSamples = 10000; + const int kBuckets = 50; + + // The InverseCDF is the percent point function of the + // distribution, and can be used to assign buckets + // roughly uniformly. + std::vector<double> cutoffs; + const double kInc = 1.0 / static_cast<double>(kBuckets); + for (double p = kInc; p < 1.0; p += kInc) { + cutoffs.push_back(InverseCDF(p)); + } + if (cutoffs.back() != std::numeric_limits<double>::infinity()) { + cutoffs.push_back(std::numeric_limits<double>::infinity()); + } + + D dis(mean(), stddev()); + + std::vector<int32_t> counts(cutoffs.size(), 0); + for (int j = 0; j < kSamples; j++) { + const double x = dis(rng_); + auto it = std::upper_bound(cutoffs.begin(), cutoffs.end(), x); + counts[std::distance(cutoffs.begin(), it)]++; + } + + // Null-hypothesis is that the distribution is a gaussian distribution + // with the provided mean and stddev (not estimated from the data). + const int dof = static_cast<int>(counts.size()) - 1; + + // Our threshold for logging is 1-in-50. + const double threshold = absl::random_internal::ChiSquareValue(dof, 0.98); + + const double expected = + static_cast<double>(kSamples) / static_cast<double>(counts.size()); + + double chi_square = absl::random_internal::ChiSquareWithExpected( + std::begin(counts), std::end(counts), expected); + double p = absl::random_internal::ChiSquarePValue(chi_square, dof); + + // Log if the chi_square value is above the threshold. + if (chi_square > threshold) { + for (int i = 0; i < cutoffs.size(); i++) { + ABSL_INTERNAL_LOG( + INFO, absl::StrFormat("%d : (%f) = %d", i, cutoffs[i], counts[i])); + } + + ABSL_INTERNAL_LOG( + INFO, absl::StrCat("mean=", mean(), " stddev=", stddev(), "\n", // + " expected ", expected, "\n", // + kChiSquared, " ", chi_square, " (", p, ")\n", // + kChiSquared, " @ 0.98 = ", threshold)); + } + return p; +} + +TEST_P(GaussianDistributionTests, ZTest) { + // TODO(absl-team): Run these tests against std::normal_distribution<double> + // to validate outcomes are similar. + const size_t kSamples = 10000; + const auto& param = GetParam(); + const int expected_failures = + std::max(1, static_cast<int>(std::ceil(param.trials * param.p_fail))); + const double p = absl::random_internal::RequiredSuccessProbability( + param.p_fail, param.trials); + + int failures = 0; + for (int i = 0; i < param.trials; i++) { + failures += + SingleZTest<absl::gaussian_distribution<double>>(p, kSamples) ? 0 : 1; + } + EXPECT_LE(failures, expected_failures); +} + +TEST_P(GaussianDistributionTests, ChiSquaredTest) { + const int kTrials = 20; + int failures = 0; + + for (int i = 0; i < kTrials; i++) { + double p_value = + SingleChiSquaredTest<absl::gaussian_distribution<double>>(); + if (p_value < 0.0025) { // 1/400 + failures++; + } + } + // There is a 0.05% chance of producing at least one failure, so raise the + // failure threshold high enough to allow for a flake rate of less than one in + // 10,000. + EXPECT_LE(failures, 4); +} + +std::vector<Param> GenParams() { + return { + // Mean around 0. + Param{0.0, 1.0, 0.01, 100}, + Param{0.0, 1e2, 0.01, 100}, + Param{0.0, 1e4, 0.01, 100}, + Param{0.0, 1e8, 0.01, 100}, + Param{0.0, 1e16, 0.01, 100}, + Param{0.0, 1e-3, 0.01, 100}, + Param{0.0, 1e-5, 0.01, 100}, + Param{0.0, 1e-9, 0.01, 100}, + Param{0.0, 1e-17, 0.01, 100}, + + // Mean around 1. + Param{1.0, 1.0, 0.01, 100}, + Param{1.0, 1e2, 0.01, 100}, + Param{1.0, 1e-2, 0.01, 100}, + + // Mean around 100 / -100 + Param{1e2, 1.0, 0.01, 100}, + Param{-1e2, 1.0, 0.01, 100}, + Param{1e2, 1e6, 0.01, 100}, + Param{-1e2, 1e6, 0.01, 100}, + + // More extreme + Param{1e4, 1e4, 0.01, 100}, + Param{1e8, 1e4, 0.01, 100}, + Param{1e12, 1e4, 0.01, 100}, + }; +} + +std::string ParamName(const ::testing::TestParamInfo<Param>& info) { + const auto& p = info.param; + std::string name = absl::StrCat("mean_", absl::SixDigits(p.mean), "__stddev_", + absl::SixDigits(p.stddev)); + return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); +} + +INSTANTIATE_TEST_SUITE_P(, GaussianDistributionTests, + ::testing::ValuesIn(GenParams()), ParamName); + +// NOTE: absl::gaussian_distribution is not guaranteed to be stable. +TEST(GaussianDistributionTest, StabilityTest) { + // absl::gaussian_distribution stability relies on the underlying zignor + // data, absl::random_interna::RandU64ToDouble, std::exp, std::log, and + // std::abs. + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector<int> output(11); + + { + absl::gaussian_distribution<double> dist; + std::generate(std::begin(output), std::end(output), + [&] { return static_cast<int>(10000000.0 * dist(urbg)); }); + + EXPECT_EQ(13, urbg.invocations()); + EXPECT_THAT(output, // + testing::ElementsAre(1494, 25518841, 9991550, 1351856, + -20373238, 3456682, 333530, -6804981, + -15279580, -16459654, 1494)); + } + + urbg.reset(); + { + absl::gaussian_distribution<float> dist; + std::generate(std::begin(output), std::end(output), + [&] { return static_cast<int>(1000000.0f * dist(urbg)); }); + + EXPECT_EQ(13, urbg.invocations()); + EXPECT_THAT( + output, // + testing::ElementsAre(149, 2551884, 999155, 135185, -2037323, 345668, + 33353, -680498, -1527958, -1645965, 149)); + } +} + +// This is an implementation-specific test. If any part of the implementation +// changes, then it is likely that this test will change as well. +// Also, if dependencies of the distribution change, such as RandU64ToDouble, +// then this is also likely to change. +TEST(GaussianDistributionTest, AlgorithmBounds) { + absl::gaussian_distribution<double> dist; + + // In ~95% of cases, a single value is used to generate the output. + // for all inputs where |x| < 0.750461021389 this should be the case. + // + // The exact constraints are based on the ziggurat tables, and any + // changes to the ziggurat tables may require adjusting these bounds. + // + // for i in range(0, len(X)-1): + // print i, X[i+1]/X[i], (X[i+1]/X[i] > 0.984375) + // + // 0.125 <= |values| <= 0.75 + const uint64_t kValues[] = { + 0x1000000000000100ull, 0x2000000000000100ull, 0x3000000000000100ull, + 0x4000000000000100ull, 0x5000000000000100ull, 0x6000000000000100ull, + // negative values + 0x9000000000000100ull, 0xa000000000000100ull, 0xb000000000000100ull, + 0xc000000000000100ull, 0xd000000000000100ull, 0xe000000000000100ull}; + + // 0.875 <= |values| <= 0.984375 + const uint64_t kExtraValues[] = { + 0x7000000000000100ull, 0x7800000000000100ull, // + 0x7c00000000000100ull, 0x7e00000000000100ull, // + // negative values + 0xf000000000000100ull, 0xf800000000000100ull, // + 0xfc00000000000100ull, 0xfe00000000000100ull}; + + auto make_box = [](uint64_t v, uint64_t box) { + return (v & 0xffffffffffffff80ull) | box; + }; + + // The box is the lower 7 bits of the value. When the box == 0, then + // the algorithm uses an escape hatch to select the result for large + // outputs. + for (uint64_t box = 0; box < 0x7f; box++) { + for (const uint64_t v : kValues) { + // Extra values are added to the sequence to attempt to avoid + // infinite loops from rejection sampling on bugs/errors. + absl::random_internal::sequence_urbg urbg( + {make_box(v, box), 0x0003eb76f6f7f755ull, 0x5FCEA50FDB2F953Bull}); + + auto a = dist(urbg); + EXPECT_EQ(1, urbg.invocations()) << box << " " << std::hex << v; + if (v & 0x8000000000000000ull) { + EXPECT_LT(a, 0.0) << box << " " << std::hex << v; + } else { + EXPECT_GT(a, 0.0) << box << " " << std::hex << v; + } + } + if (box > 10 && box < 100) { + // The center boxes use the fast algorithm for more + // than 98.4375% of values. + for (const uint64_t v : kExtraValues) { + absl::random_internal::sequence_urbg urbg( + {make_box(v, box), 0x0003eb76f6f7f755ull, 0x5FCEA50FDB2F953Bull}); + + auto a = dist(urbg); + EXPECT_EQ(1, urbg.invocations()) << box << " " << std::hex << v; + if (v & 0x8000000000000000ull) { + EXPECT_LT(a, 0.0) << box << " " << std::hex << v; + } else { + EXPECT_GT(a, 0.0) << box << " " << std::hex << v; + } + } + } + } + + // When the box == 0, the fallback algorithm uses a ratio of uniforms, + // which consumes 2 additional values from the urbg. + // Fallback also requires that the initial value be > 0.9271586026096681. + auto make_fallback = [](uint64_t v) { return (v & 0xffffffffffffff80ull); }; + + double tail[2]; + { + // 0.9375 + absl::random_internal::sequence_urbg urbg( + {make_fallback(0x7800000000000000ull), 0x13CCA830EB61BD96ull, + 0x00000076f6f7f755ull}); + tail[0] = dist(urbg); + EXPECT_EQ(3, urbg.invocations()); + EXPECT_GT(tail[0], 0); + } + { + // -0.9375 + absl::random_internal::sequence_urbg urbg( + {make_fallback(0xf800000000000000ull), 0x13CCA830EB61BD96ull, + 0x00000076f6f7f755ull}); + tail[1] = dist(urbg); + EXPECT_EQ(3, urbg.invocations()); + EXPECT_LT(tail[1], 0); + } + EXPECT_EQ(tail[0], -tail[1]); + EXPECT_EQ(418610, static_cast<int64_t>(tail[0] * 100000.0)); + + // When the box != 0, the fallback algorithm computes a wedge function. + // Depending on the box, the threshold for varies as high as + // 0.991522480228. + { + // 0.9921875, 0.875 + absl::random_internal::sequence_urbg urbg( + {make_box(0x7f00000000000000ull, 120), 0xe000000000000001ull, + 0x13CCA830EB61BD96ull}); + tail[0] = dist(urbg); + EXPECT_EQ(2, urbg.invocations()); + EXPECT_GT(tail[0], 0); + } + { + // -0.9921875, 0.875 + absl::random_internal::sequence_urbg urbg( + {make_box(0xff00000000000000ull, 120), 0xe000000000000001ull, + 0x13CCA830EB61BD96ull}); + tail[1] = dist(urbg); + EXPECT_EQ(2, urbg.invocations()); + EXPECT_LT(tail[1], 0); + } + EXPECT_EQ(tail[0], -tail[1]); + EXPECT_EQ(61948, static_cast<int64_t>(tail[0] * 100000.0)); + + // Fallback rejected, try again. + { + // -0.9921875, 0.0625 + absl::random_internal::sequence_urbg urbg( + {make_box(0xff00000000000000ull, 120), 0x1000000000000001, + make_box(0x1000000000000100ull, 50), 0x13CCA830EB61BD96ull}); + dist(urbg); + EXPECT_EQ(3, urbg.invocations()); + } +} + +} // namespace diff --git a/absl/random/generators_test.cc b/absl/random/generators_test.cc new file mode 100644 index 00000000..41725f13 --- /dev/null +++ b/absl/random/generators_test.cc @@ -0,0 +1,179 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cstddef> +#include <cstdint> +#include <random> +#include <vector> + +#include "gtest/gtest.h" +#include "absl/random/distributions.h" +#include "absl/random/random.h" + +namespace { + +template <typename URBG> +void TestUniform(URBG* gen) { + // [a, b) default-semantics, inferred types. + absl::Uniform(*gen, 0, 100); // int + absl::Uniform(*gen, 0, 1.0); // Promoted to double + absl::Uniform(*gen, 0.0f, 1.0); // Promoted to double + absl::Uniform(*gen, 0.0, 1.0); // double + absl::Uniform(*gen, -1, 1L); // Promoted to long + + // Roll a die. + absl::Uniform(absl::IntervalClosedClosed, *gen, 1, 6); + + // Get a fraction. + absl::Uniform(absl::IntervalOpenOpen, *gen, 0.0, 1.0); + + // Assign a value to a random element. + std::vector<int> elems = {10, 20, 30, 40, 50}; + elems[absl::Uniform(*gen, 0u, elems.size())] = 5; + elems[absl::Uniform<size_t>(*gen, 0, elems.size())] = 3; + + // Choose some epsilon around zero. + absl::Uniform(absl::IntervalOpenOpen, *gen, -1.0, 1.0); + + // (a, b) semantics, inferred types. + absl::Uniform(absl::IntervalOpenOpen, *gen, 0, 1.0); // Promoted to double + + // Explict overriding of types. + absl::Uniform<int>(*gen, 0, 100); + absl::Uniform<int8_t>(*gen, 0, 100); + absl::Uniform<int16_t>(*gen, 0, 100); + absl::Uniform<uint16_t>(*gen, 0, 100); + absl::Uniform<int32_t>(*gen, 0, 1 << 10); + absl::Uniform<uint32_t>(*gen, 0, 1 << 10); + absl::Uniform<int64_t>(*gen, 0, 1 << 10); + absl::Uniform<uint64_t>(*gen, 0, 1 << 10); + + absl::Uniform<float>(*gen, 0.0, 1.0); + absl::Uniform<float>(*gen, 0, 1); + absl::Uniform<float>(*gen, -1, 1); + absl::Uniform<double>(*gen, 0.0, 1.0); + + absl::Uniform<float>(*gen, -1.0, 0); + absl::Uniform<double>(*gen, -1.0, 0); + + // Tagged + absl::Uniform<double>(absl::IntervalClosedClosed, *gen, 0, 1); + absl::Uniform<double>(absl::IntervalClosedOpen, *gen, 0, 1); + absl::Uniform<double>(absl::IntervalOpenOpen, *gen, 0, 1); + absl::Uniform<double>(absl::IntervalOpenClosed, *gen, 0, 1); + absl::Uniform<double>(absl::IntervalClosedClosed, *gen, 0, 1); + absl::Uniform<double>(absl::IntervalOpenOpen, *gen, 0, 1); + + absl::Uniform<int>(absl::IntervalClosedClosed, *gen, 0, 100); + absl::Uniform<int>(absl::IntervalClosedOpen, *gen, 0, 100); + absl::Uniform<int>(absl::IntervalOpenOpen, *gen, 0, 100); + absl::Uniform<int>(absl::IntervalOpenClosed, *gen, 0, 100); + absl::Uniform<int>(absl::IntervalClosedClosed, *gen, 0, 100); + absl::Uniform<int>(absl::IntervalOpenOpen, *gen, 0, 100); + + // With *generator as an R-value reference. + absl::Uniform<int>(URBG(), 0, 100); + absl::Uniform<double>(URBG(), 0.0, 1.0); +} + +template <typename URBG> +void TestExponential(URBG* gen) { + absl::Exponential<float>(*gen); + absl::Exponential<double>(*gen); + absl::Exponential<double>(URBG()); +} + +template <typename URBG> +void TestPoisson(URBG* gen) { + // [rand.dist.pois] Indicates that the std::poisson_distribution + // is parameterized by IntType, however MSVC does not allow 8-bit + // types. + absl::Poisson<int>(*gen); + absl::Poisson<int16_t>(*gen); + absl::Poisson<uint16_t>(*gen); + absl::Poisson<int32_t>(*gen); + absl::Poisson<uint32_t>(*gen); + absl::Poisson<int64_t>(*gen); + absl::Poisson<uint64_t>(*gen); + absl::Poisson<uint64_t>(URBG()); +} + +template <typename URBG> +void TestBernoulli(URBG* gen) { + absl::Bernoulli(*gen, 0.5); + absl::Bernoulli(*gen, 0.5); +} + +template <typename URBG> +void TestZipf(URBG* gen) { + absl::Zipf<int>(*gen, 100); + absl::Zipf<int8_t>(*gen, 100); + absl::Zipf<int16_t>(*gen, 100); + absl::Zipf<uint16_t>(*gen, 100); + absl::Zipf<int32_t>(*gen, 1 << 10); + absl::Zipf<uint32_t>(*gen, 1 << 10); + absl::Zipf<int64_t>(*gen, 1 << 10); + absl::Zipf<uint64_t>(*gen, 1 << 10); + absl::Zipf<uint64_t>(URBG(), 1 << 10); +} + +template <typename URBG> +void TestGaussian(URBG* gen) { + absl::Gaussian<float>(*gen, 1.0, 1.0); + absl::Gaussian<double>(*gen, 1.0, 1.0); + absl::Gaussian<double>(URBG(), 1.0, 1.0); +} + +template <typename URBG> +void TestLogNormal(URBG* gen) { + absl::LogUniform<int>(*gen, 0, 100); + absl::LogUniform<int8_t>(*gen, 0, 100); + absl::LogUniform<int16_t>(*gen, 0, 100); + absl::LogUniform<uint16_t>(*gen, 0, 100); + absl::LogUniform<int32_t>(*gen, 0, 1 << 10); + absl::LogUniform<uint32_t>(*gen, 0, 1 << 10); + absl::LogUniform<int64_t>(*gen, 0, 1 << 10); + absl::LogUniform<uint64_t>(*gen, 0, 1 << 10); + absl::LogUniform<uint64_t>(URBG(), 0, 1 << 10); +} + +template <typename URBG> +void CompatibilityTest() { + URBG gen; + + TestUniform(&gen); + TestExponential(&gen); + TestPoisson(&gen); + TestBernoulli(&gen); + TestZipf(&gen); + TestGaussian(&gen); + TestLogNormal(&gen); +} + +TEST(std_mt19937_64, Compatibility) { + // Validate with std::mt19937_64 + CompatibilityTest<std::mt19937_64>(); +} + +TEST(BitGen, Compatibility) { + // Validate with absl::BitGen + CompatibilityTest<absl::BitGen>(); +} + +TEST(InsecureBitGen, Compatibility) { + // Validate with absl::InsecureBitGen + CompatibilityTest<absl::InsecureBitGen>(); +} + +} // namespace diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel new file mode 100644 index 00000000..fd5471a6 --- /dev/null +++ b/absl/random/internal/BUILD.bazel @@ -0,0 +1,658 @@ +# Internal-only implementation classes for Abseil Random +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", + "ABSL_RANDOM_RANDEN_COPTS", + "ABSL_TEST_COPTS", + "absl_random_randen_copts_init", +) + +package(default_visibility = [ + "//absl/random:__pkg__", +]) + +licenses(["notice"]) # Apache 2.0 + +cc_library( + name = "traits", + hdrs = ["traits.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/random:__pkg__", + ], + deps = ["//absl/base:config"], +) + +cc_library( + name = "distribution_caller", + hdrs = ["distribution_caller.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/random:__pkg__", + ], +) + +cc_library( + name = "distributions", + hdrs = [ + "distributions.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distribution_caller", + ":fast_uniform_bits", + ":fastmath", + ":traits", + ":uniform_helper", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/types:span", + ], +) + +cc_library( + name = "fast_uniform_bits", + hdrs = [ + "fast_uniform_bits.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/random:__pkg__", + ], +) + +cc_library( + name = "seed_material", + srcs = [ + "seed_material.cc", + ], + hdrs = [ + "seed_material.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":fast_uniform_bits", + "//absl/base", + "//absl/base:core_headers", + "//absl/strings", + "//absl/types:optional", + "//absl/types:span", + ], +) + +cc_library( + name = "pool_urbg", + srcs = [ + "pool_urbg.cc", + ], + hdrs = [ + "pool_urbg.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = select({ + "//absl:windows": [], + "//conditions:default": ["-pthread"], + }) + ABSL_DEFAULT_LINKOPTS, + deps = [ + ":randen", + ":seed_material", + ":traits", + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:endian", + "//absl/random:seed_gen_exception", + "//absl/types:span", + ], +) + +cc_library( + name = "explicit_seed_seq", + testonly = 1, + hdrs = [ + "explicit_seed_seq.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, +) + +cc_library( + name = "sequence_urbg", + testonly = 1, + hdrs = [ + "sequence_urbg.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, +) + +cc_library( + name = "salted_seed_seq", + hdrs = [ + "salted_seed_seq.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":seed_material", + "//absl/container:inlined_vector", + "//absl/meta:type_traits", + "//absl/types:optional", + "//absl/types:span", + ], +) + +cc_library( + name = "iostream_state_saver", + hdrs = ["iostream_state_saver.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/meta:type_traits", + "//absl/numeric:int128", + ], +) + +cc_library( + name = "distribution_impl", + hdrs = [ + "distribution_impl.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":fastmath", + ":traits", + "//absl/base:bits", + "//absl/base:config", + "//absl/numeric:int128", + ], +) + +cc_library( + name = "fastmath", + hdrs = [ + "fastmath.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = ["//absl/base:bits"], +) + +cc_library( + name = "nonsecure_base", + hdrs = ["nonsecure_base.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":pool_urbg", + ":salted_seed_seq", + ":seed_material", + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/types:optional", + "//absl/types:span", + ], +) + +cc_library( + name = "pcg_engine", + hdrs = ["pcg_engine.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":fastmath", + ":iostream_state_saver", + "//absl/base:config", + "//absl/meta:type_traits", + "//absl/numeric:int128", + ], +) + +cc_library( + name = "randen_engine", + hdrs = ["randen_engine.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":iostream_state_saver", + ":randen", + "//absl/meta:type_traits", + ], +) + +cc_library( + name = "platform", + hdrs = [ + "randen_traits.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + textual_hdrs = [ + "randen-keys.inc", + "platform.h", + ], +) + +cc_library( + name = "randen", + srcs = [ + "randen.cc", + ], + hdrs = [ + "randen.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":platform", + ":randen_hwaes", + ":randen_slow", + "//absl/base", + ], +) + +cc_library( + name = "randen_slow", + srcs = ["randen_slow.cc"], + hdrs = ["randen_slow.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":platform", + ], +) + +absl_random_randen_copts_init() + +cc_library( + name = "randen_hwaes", + srcs = [ + "randen_detect.cc", + ], + hdrs = [ + "randen_detect.h", + "randen_hwaes.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":platform", + ":randen_hwaes_impl", + ], +) + +# build with --save_temps to see assembly language output. +cc_library( + name = "randen_hwaes_impl", + srcs = [ + "randen_hwaes.cc", + "randen_hwaes.h", + ], + copts = ABSL_DEFAULT_COPTS + ABSL_RANDOM_RANDEN_COPTS + select({ + "//absl:windows": [], + "//conditions:default": ["-Wno-pass-failed"], + }), + # copts in RANDEN_HWAES_COPTS can make this target unusable as a module + # leading to a Clang diagnostic. Furthermore, it only has a private header + # anyway and thus there wouldn't be any gain from using it as a module. + features = ["-header_modules"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [":platform"], +) + +cc_binary( + name = "gaussian_distribution_gentables", + srcs = [ + "gaussian_distribution_gentables.cc", + ], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:core_headers", + "//absl/random:distributions", + ], +) + +cc_library( + name = "distribution_test_util", + testonly = 1, + srcs = [ + "chi_square.cc", + "distribution_test_util.cc", + ], + hdrs = [ + "chi_square.h", + "distribution_test_util.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base", + "//absl/base:core_headers", + "//absl/strings", + "//absl/strings:str_format", + "//absl/types:span", + ], +) + +# Common tags for tests, etc. +ABSL_RANDOM_NONPORTABLE_TAGS = [ + "no_test_android_arm", + "no_test_android_arm64", + "no_test_android_x86", + "no_test_darwin_x86_64", + "no_test_ios_x86_64", + "no_test_loonix", + "no_test_msvc_x64", + "no_test_wasm", +] + +cc_test( + name = "traits_test", + size = "small", + srcs = ["traits_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":traits", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "distribution_impl_test", + size = "small", + srcs = ["distribution_impl_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distribution_impl", + "//absl/base:bits", + "//absl/flags:flag", + "//absl/numeric:int128", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "distribution_test_util_test", + size = "small", + srcs = ["distribution_test_util_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distribution_test_util", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "fastmath_test", + size = "small", + srcs = ["fastmath_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":fastmath", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "explicit_seed_seq_test", + size = "small", + srcs = ["explicit_seed_seq_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":explicit_seed_seq", + "//absl/random:seed_sequences", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "salted_seed_seq_test", + size = "small", + srcs = ["salted_seed_seq_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":salted_seed_seq", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "chi_square_test", + size = "small", + srcs = [ + "chi_square_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distribution_test_util", + "//absl/base:core_headers", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "fast_uniform_bits_test", + size = "small", + srcs = [ + "fast_uniform_bits_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":fast_uniform_bits", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "nonsecure_base_test", + size = "small", + srcs = [ + "nonsecure_base_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":nonsecure_base", + "//absl/random", + "//absl/random:distributions", + "//absl/random:seed_sequences", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "seed_material_test", + size = "small", + srcs = ["seed_material_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":seed_material", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "pool_urbg_test", + size = "small", + srcs = [ + "pool_urbg_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":pool_urbg", + "//absl/meta:type_traits", + "//absl/types:span", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "pcg_engine_test", + size = "medium", # Trying to measure accuracy. + srcs = ["pcg_engine_test.cc"], + copts = ABSL_TEST_COPTS, + flaky = 1, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":explicit_seed_seq", + ":pcg_engine", + "//absl/time", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "randen_engine_test", + size = "medium", + srcs = [ + "randen_engine_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":explicit_seed_seq", + ":randen_engine", + "//absl/base", + "//absl/strings", + "//absl/time", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "randen_test", + size = "small", + srcs = ["randen_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":randen", + "//absl/meta:type_traits", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "randen_slow_test", + size = "small", + srcs = ["randen_slow_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":randen_slow", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "randen_hwaes_test", + size = "small", + srcs = ["randen_hwaes_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ABSL_RANDOM_NONPORTABLE_TAGS, + deps = [ + ":platform", + ":randen_hwaes", + ":randen_hwaes_impl", # build_cleaner: keep + "//absl/base", + "//absl/strings:str_format", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "nanobenchmark", + srcs = ["nanobenchmark.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, + textual_hdrs = ["nanobenchmark.h"], + deps = [ + ":platform", + ":randen_engine", + "//absl/base", + ], +) + +cc_library( + name = "uniform_helper", + hdrs = ["uniform_helper.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distribution_impl", + ":fast_uniform_bits", + ":iostream_state_saver", + ":traits", + "//absl/base:core_headers", + "//absl/meta:type_traits", + ], +) + +cc_test( + name = "nanobenchmark_test", + size = "small", + srcs = ["nanobenchmark_test.cc"], + flaky = 1, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "benchmark", + "no_test_ios_x86_64", + "no_test_loonix", # Crashing. + ], + deps = [ + ":nanobenchmark", + "//absl/base", + "//absl/strings", + ], +) + +cc_test( + name = "randen_benchmarks", + size = "medium", + srcs = ["randen_benchmarks.cc"], + copts = ABSL_TEST_COPTS + ABSL_RANDOM_RANDEN_COPTS, + flaky = 1, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ABSL_RANDOM_NONPORTABLE_TAGS + ["benchmark"], + deps = [ + ":nanobenchmark", + ":platform", + ":randen", + ":randen_engine", + ":randen_hwaes", + ":randen_hwaes_impl", + ":randen_slow", + "//absl/base", + "//absl/strings", + ], +) + +cc_test( + name = "iostream_state_saver_test", + size = "small", + srcs = ["iostream_state_saver_test.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":iostream_state_saver", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/random/internal/chi_square.cc b/absl/random/internal/chi_square.cc new file mode 100644 index 00000000..45671a3e --- /dev/null +++ b/absl/random/internal/chi_square.cc @@ -0,0 +1,232 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/chi_square.h" + +#include <cmath> + +#include "absl/random/internal/distribution_test_util.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { +namespace { + +#if defined(__EMSCRIPTEN__) +// Workaround __EMSCRIPTEN__ error: llvm_fma_f64 not found. +inline double fma(double x, double y, double z) { + return (x * y) + z; +} +#endif + +// Use Horner's method to evaluate a polynomial. +template <typename T, unsigned N> +inline T EvaluatePolynomial(T x, const T (&poly)[N]) { +#if !defined(__EMSCRIPTEN__) + using std::fma; +#endif + T p = poly[N - 1]; + for (unsigned i = 2; i <= N; i++) { + p = fma(p, x, poly[N - i]); + } + return p; +} + +static constexpr int kLargeDOF = 150; + +// Returns the probability of a normal z-value. +// +// Adapted from the POZ function in: +// Ibbetson D, Algorithm 209 +// Collected Algorithms of the CACM 1963 p. 616 +// +double POZ(double z) { + static constexpr double kP1[] = { + 0.797884560593, -0.531923007300, 0.319152932694, + -0.151968751364, 0.059054035642, -0.019198292004, + 0.005198775019, -0.001075204047, 0.000124818987, + }; + static constexpr double kP2[] = { + 0.999936657524, 0.000535310849, -0.002141268741, 0.005353579108, + -0.009279453341, 0.011630447319, -0.010557625006, 0.006549791214, + -0.002034254874, -0.000794620820, 0.001390604284, -0.000676904986, + -0.000019538132, 0.000152529290, -0.000045255659, + }; + + const double kZMax = 6.0; // Maximum meaningful z-value. + if (z == 0.0) { + return 0.5; + } + double x; + double y = 0.5 * std::fabs(z); + if (y >= (kZMax * 0.5)) { + x = 1.0; + } else if (y < 1.0) { + double w = y * y; + x = EvaluatePolynomial(w, kP1) * y * 2.0; + } else { + y -= 2.0; + x = EvaluatePolynomial(y, kP2); + } + return z > 0.0 ? ((x + 1.0) * 0.5) : ((1.0 - x) * 0.5); +} + +// Approximates the survival function of the normal distribution. +// +// Algorithm 26.2.18, from: +// [Abramowitz and Stegun, Handbook of Mathematical Functions,p.932] +// http://people.math.sfu.ca/~cbm/aands/abramowitz_and_stegun.pdf +// +double normal_survival(double z) { + // Maybe replace with the alternate formulation. + // 0.5 * erfc((x - mean)/(sqrt(2) * sigma)) + static constexpr double kR[] = { + 1.0, 0.196854, 0.115194, 0.000344, 0.019527, + }; + double r = EvaluatePolynomial(z, kR); + r *= r; + return 0.5 / (r * r); +} + +} // namespace + +// Calculates the critical chi-square value given degrees-of-freedom and a +// p-value, usually using bisection. Also known by the name CRITCHI. +double ChiSquareValue(int dof, double p) { + static constexpr double kChiEpsilon = + 0.000001; // Accuracy of the approximation. + static constexpr double kChiMax = + 99999.0; // Maximum chi-squared value. + + const double p_value = 1.0 - p; + if (dof < 1 || p_value > 1.0) { + return 0.0; + } + + if (dof > kLargeDOF) { + // For large degrees of freedom, use the normal approximation by + // Wilson, E. B. and Hilferty, M. M. (1931) + // chi^2 - mean + // Z = -------------- + // stddev + const double z = InverseNormalSurvival(p_value); + const double mean = 1 - 2.0 / (9 * dof); + const double variance = 2.0 / (9 * dof); + // Cannot use this method if the variance is 0. + if (variance != 0) { + return std::pow(z * std::sqrt(variance) + mean, 3.0) * dof; + } + } + + if (p_value <= 0.0) return kChiMax; + + // Otherwise search for the p value by bisection + double min_chisq = 0.0; + double max_chisq = kChiMax; + double current = dof / std::sqrt(p_value); + while ((max_chisq - min_chisq) > kChiEpsilon) { + if (ChiSquarePValue(current, dof) < p_value) { + max_chisq = current; + } else { + min_chisq = current; + } + current = (max_chisq + min_chisq) * 0.5; + } + return current; +} + +// Calculates the p-value (probability) of a given chi-square value +// and degrees of freedom. +// +// Adapted from the POCHISQ function from: +// Hill, I. D. and Pike, M. C. Algorithm 299 +// Collected Algorithms of the CACM 1963 p. 243 +// +double ChiSquarePValue(double chi_square, int dof) { + static constexpr double kLogSqrtPi = + 0.5723649429247000870717135; // Log[Sqrt[Pi]] + static constexpr double kInverseSqrtPi = + 0.5641895835477562869480795; // 1/(Sqrt[Pi]) + + // For large degrees of freedom, use the normal approximation by + // Wilson, E. B. and Hilferty, M. M. (1931) + // Via Wikipedia: + // By the Central Limit Theorem, because the chi-square distribution is the + // sum of k independent random variables with finite mean and variance, it + // converges to a normal distribution for large k. + if (dof > kLargeDOF) { + // Re-scale everything. + const double chi_square_scaled = std::pow(chi_square / dof, 1.0 / 3); + const double mean = 1 - 2.0 / (9 * dof); + const double variance = 2.0 / (9 * dof); + // If variance is 0, this method cannot be used. + if (variance != 0) { + const double z = (chi_square_scaled - mean) / std::sqrt(variance); + if (z > 0) { + return normal_survival(z); + } else if (z < 0) { + return 1.0 - normal_survival(-z); + } else { + return 0.5; + } + } + } + + // The chi square function is >= 0 for any degrees of freedom. + // In other words, probability that the chi square function >= 0 is 1. + if (chi_square <= 0.0) return 1.0; + + // If the degrees of freedom is zero, the chi square function is always 0 by + // definition. In other words, the probability that the chi square function + // is > 0 is zero (chi square values <= 0 have been filtered above). + if (dof < 1) return 0; + + auto capped_exp = [](double x) { return x < -20 ? 0.0 : std::exp(x); }; + static constexpr double kBigX = 20; + + double a = 0.5 * chi_square; + const bool even = !(dof & 1); // True if dof is an even number. + const double y = capped_exp(-a); + double s = even ? y : (2.0 * POZ(-std::sqrt(chi_square))); + + if (dof <= 2) { + return s; + } + + chi_square = 0.5 * (dof - 1.0); + double z = (even ? 1.0 : 0.5); + if (a > kBigX) { + double e = (even ? 0.0 : kLogSqrtPi); + double c = std::log(a); + while (z <= chi_square) { + e = std::log(z) + e; + s += capped_exp(c * z - a - e); + z += 1.0; + } + return s; + } + + double e = (even ? 1.0 : (kInverseSqrtPi / std::sqrt(a))); + double c = 0.0; + while (z <= chi_square) { + e = e * (a / z); + c = c + e; + z += 1.0; + } + return c * y + s; +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/internal/chi_square.h b/absl/random/internal/chi_square.h new file mode 100644 index 00000000..002b4c95 --- /dev/null +++ b/absl/random/internal/chi_square.h @@ -0,0 +1,87 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_ +#define ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_ + +// The chi-square statistic. +// +// Useful for evaluating if `D` independent random variables are behaving as +// expected, or if two distributions are similar. (`D` is the degrees of +// freedom). +// +// Each bucket should have an expected count of 10 or more for the chi square to +// be meaningful. + +#include <cassert> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +constexpr const char kChiSquared[] = "chi-squared"; + +// Returns the measured chi square value, using a single expected value. This +// assumes that the values in [begin, end) are uniformly distributed. +template <typename Iterator> +double ChiSquareWithExpected(Iterator begin, Iterator end, double expected) { + // Compute the sum and the number of buckets. + assert(expected >= 10); // require at least 10 samples per bucket. + double chi_square = 0; + for (auto it = begin; it != end; it++) { + double d = static_cast<double>(*it) - expected; + chi_square += d * d; + } + chi_square = chi_square / expected; + return chi_square; +} + +// Returns the measured chi square value, taking the actual value of each bucket +// from the first set of iterators, and the expected value of each bucket from +// the second set of iterators. +template <typename Iterator, typename Expected> +double ChiSquare(Iterator it, Iterator end, Expected eit, Expected eend) { + double chi_square = 0; + for (; it != end && eit != eend; ++it, ++eit) { + if (*it > 0) { + assert(*eit > 0); + } + double e = static_cast<double>(*eit); + double d = static_cast<double>(*it - *eit); + if (d != 0) { + assert(e > 0); + chi_square += (d * d) / e; + } + } + assert(it == end && eit == eend); + return chi_square; +} + +// ====================================================================== +// The following methods can be used for an arbitrary significance level. +// + +// Calculates critical chi-square values to produce the given p-value using a +// bisection search for a value within epsilon, relying on the monotonicity of +// ChiSquarePValue(). +double ChiSquareValue(int dof, double p); + +// Calculates the p-value (probability) of a given chi-square value. +double ChiSquarePValue(double chi_square, int dof); + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_ diff --git a/absl/random/internal/chi_square_test.cc b/absl/random/internal/chi_square_test.cc new file mode 100644 index 00000000..5025defa --- /dev/null +++ b/absl/random/internal/chi_square_test.cc @@ -0,0 +1,365 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/chi_square.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <numeric> +#include <vector> + +#include "gtest/gtest.h" +#include "absl/base/macros.h" + +using absl::random_internal::ChiSquare; +using absl::random_internal::ChiSquarePValue; +using absl::random_internal::ChiSquareValue; +using absl::random_internal::ChiSquareWithExpected; + +namespace { + +TEST(ChiSquare, Value) { + struct { + int line; + double chi_square; + int df; + double confidence; + } const specs[] = { + // Testing lookup at 1% confidence + {__LINE__, 0, 0, 0.01}, + {__LINE__, 0.00016, 1, 0.01}, + {__LINE__, 1.64650, 8, 0.01}, + {__LINE__, 5.81221, 16, 0.01}, + {__LINE__, 156.4319, 200, 0.01}, + {__LINE__, 1121.3784, 1234, 0.01}, + {__LINE__, 53557.1629, 54321, 0.01}, + {__LINE__, 651662.6647, 654321, 0.01}, + + // Testing lookup at 99% confidence + {__LINE__, 0, 0, 0.99}, + {__LINE__, 6.635, 1, 0.99}, + {__LINE__, 20.090, 8, 0.99}, + {__LINE__, 32.000, 16, 0.99}, + {__LINE__, 249.4456, 200, 0.99}, + {__LINE__, 1131.1573, 1023, 0.99}, + {__LINE__, 1352.5038, 1234, 0.99}, + {__LINE__, 55090.7356, 54321, 0.99}, + {__LINE__, 656985.1514, 654321, 0.99}, + + // Testing lookup at 99.9% confidence + {__LINE__, 16.2659, 3, 0.999}, + {__LINE__, 22.4580, 6, 0.999}, + {__LINE__, 267.5409, 200, 0.999}, + {__LINE__, 1168.5033, 1023, 0.999}, + {__LINE__, 55345.1741, 54321, 0.999}, + {__LINE__, 657861.7284, 654321, 0.999}, + {__LINE__, 51.1772, 24, 0.999}, + {__LINE__, 59.7003, 30, 0.999}, + {__LINE__, 37.6984, 15, 0.999}, + {__LINE__, 29.5898, 10, 0.999}, + {__LINE__, 27.8776, 9, 0.999}, + + // Testing lookup at random confidences + {__LINE__, 0.000157088, 1, 0.01}, + {__LINE__, 5.31852, 2, 0.93}, + {__LINE__, 1.92256, 4, 0.25}, + {__LINE__, 10.7709, 13, 0.37}, + {__LINE__, 26.2514, 17, 0.93}, + {__LINE__, 36.4799, 29, 0.84}, + {__LINE__, 25.818, 31, 0.27}, + {__LINE__, 63.3346, 64, 0.50}, + {__LINE__, 196.211, 128, 0.9999}, + {__LINE__, 215.21, 243, 0.10}, + {__LINE__, 285.393, 256, 0.90}, + {__LINE__, 984.504, 1024, 0.1923}, + {__LINE__, 2043.85, 2048, 0.4783}, + {__LINE__, 48004.6, 48273, 0.194}, + }; + for (const auto& spec : specs) { + SCOPED_TRACE(spec.line); + // Verify all values are have at most a 1% relative error. + const double val = ChiSquareValue(spec.df, spec.confidence); + const double err = std::max(5e-6, spec.chi_square / 5e3); // 1 part in 5000 + EXPECT_NEAR(spec.chi_square, val, err) << spec.line; + } + + // Relaxed test for extreme values, from + // http://www.ciphersbyritter.com/JAVASCRP/NORMCHIK.HTM#ChiSquare + EXPECT_NEAR(49.2680, ChiSquareValue(100, 1e-6), 5); // 0.000'005 mark + EXPECT_NEAR(123.499, ChiSquareValue(200, 1e-6), 5); // 0.000'005 mark + + EXPECT_NEAR(149.449, ChiSquareValue(100, 0.999), 0.01); + EXPECT_NEAR(161.318, ChiSquareValue(100, 0.9999), 0.01); + EXPECT_NEAR(172.098, ChiSquareValue(100, 0.99999), 0.01); + + EXPECT_NEAR(381.426, ChiSquareValue(300, 0.999), 0.05); + EXPECT_NEAR(399.756, ChiSquareValue(300, 0.9999), 0.1); + EXPECT_NEAR(416.126, ChiSquareValue(300, 0.99999), 0.2); +} + +TEST(ChiSquareTest, PValue) { + struct { + int line; + double pval; + double chi_square; + int df; + } static const specs[] = { + {__LINE__, 1, 0, 0}, + {__LINE__, 0, 0.001, 0}, + {__LINE__, 1.000, 0, 453}, + {__LINE__, 0.134471, 7972.52, 7834}, + {__LINE__, 0.203922, 28.32, 23}, + {__LINE__, 0.737171, 48274, 48472}, + {__LINE__, 0.444146, 583.1234, 579}, + {__LINE__, 0.294814, 138.2, 130}, + {__LINE__, 0.0816532, 12.63, 7}, + {__LINE__, 0, 682.32, 67}, + {__LINE__, 0.49405, 999, 999}, + {__LINE__, 1.000, 0, 9999}, + {__LINE__, 0.997477, 0.00001, 1}, + {__LINE__, 0, 5823.21, 5040}, + }; + for (const auto& spec : specs) { + SCOPED_TRACE(spec.line); + const double pval = ChiSquarePValue(spec.chi_square, spec.df); + EXPECT_NEAR(spec.pval, pval, 1e-3); + } +} + +TEST(ChiSquareTest, CalcChiSquare) { + struct { + int line; + std::vector<int> expected; + std::vector<int> actual; + } const specs[] = { + {__LINE__, + {56, 234, 76, 1, 546, 1, 87, 345, 1, 234}, + {2, 132, 4, 43, 234, 8, 345, 8, 236, 56}}, + {__LINE__, + {123, 36, 234, 367, 345, 2, 456, 567, 234, 567}, + {123, 56, 2345, 8, 345, 8, 2345, 23, 48, 267}}, + {__LINE__, + {123, 234, 345, 456, 567, 678, 789, 890, 98, 76}, + {123, 234, 345, 456, 567, 678, 789, 890, 98, 76}}, + {__LINE__, {3, 675, 23, 86, 2, 8, 2}, {456, 675, 23, 86, 23, 65, 2}}, + {__LINE__, {1}, {23}}, + }; + for (const auto& spec : specs) { + SCOPED_TRACE(spec.line); + double chi_square = 0; + for (int i = 0; i < spec.expected.size(); ++i) { + const double diff = spec.actual[i] - spec.expected[i]; + chi_square += (diff * diff) / spec.expected[i]; + } + EXPECT_NEAR(chi_square, + ChiSquare(std::begin(spec.actual), std::end(spec.actual), + std::begin(spec.expected), std::end(spec.expected)), + 1e-5); + } +} + +TEST(ChiSquareTest, CalcChiSquareInt64) { + const int64_t data[3] = {910293487, 910292491, 910216780}; + // $ python -c "import scipy.stats + // > print scipy.stats.chisquare([910293487, 910292491, 910216780])[0]" + // 4.25410123524 + double sum = std::accumulate(std::begin(data), std::end(data), double{0}); + size_t n = std::distance(std::begin(data), std::end(data)); + double a = ChiSquareWithExpected(std::begin(data), std::end(data), sum / n); + EXPECT_NEAR(4.254101, a, 1e-6); + + // ... Or with known values. + double b = + ChiSquareWithExpected(std::begin(data), std::end(data), 910267586.0); + EXPECT_NEAR(4.254101, b, 1e-6); +} + +TEST(ChiSquareTest, TableData) { + // Test data from + // http://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm + // 0.90 0.95 0.975 0.99 0.999 + const double data[100][5] = { + /* 1*/ {2.706, 3.841, 5.024, 6.635, 10.828}, + /* 2*/ {4.605, 5.991, 7.378, 9.210, 13.816}, + /* 3*/ {6.251, 7.815, 9.348, 11.345, 16.266}, + /* 4*/ {7.779, 9.488, 11.143, 13.277, 18.467}, + /* 5*/ {9.236, 11.070, 12.833, 15.086, 20.515}, + /* 6*/ {10.645, 12.592, 14.449, 16.812, 22.458}, + /* 7*/ {12.017, 14.067, 16.013, 18.475, 24.322}, + /* 8*/ {13.362, 15.507, 17.535, 20.090, 26.125}, + /* 9*/ {14.684, 16.919, 19.023, 21.666, 27.877}, + /*10*/ {15.987, 18.307, 20.483, 23.209, 29.588}, + /*11*/ {17.275, 19.675, 21.920, 24.725, 31.264}, + /*12*/ {18.549, 21.026, 23.337, 26.217, 32.910}, + /*13*/ {19.812, 22.362, 24.736, 27.688, 34.528}, + /*14*/ {21.064, 23.685, 26.119, 29.141, 36.123}, + /*15*/ {22.307, 24.996, 27.488, 30.578, 37.697}, + /*16*/ {23.542, 26.296, 28.845, 32.000, 39.252}, + /*17*/ {24.769, 27.587, 30.191, 33.409, 40.790}, + /*18*/ {25.989, 28.869, 31.526, 34.805, 42.312}, + /*19*/ {27.204, 30.144, 32.852, 36.191, 43.820}, + /*20*/ {28.412, 31.410, 34.170, 37.566, 45.315}, + /*21*/ {29.615, 32.671, 35.479, 38.932, 46.797}, + /*22*/ {30.813, 33.924, 36.781, 40.289, 48.268}, + /*23*/ {32.007, 35.172, 38.076, 41.638, 49.728}, + /*24*/ {33.196, 36.415, 39.364, 42.980, 51.179}, + /*25*/ {34.382, 37.652, 40.646, 44.314, 52.620}, + /*26*/ {35.563, 38.885, 41.923, 45.642, 54.052}, + /*27*/ {36.741, 40.113, 43.195, 46.963, 55.476}, + /*28*/ {37.916, 41.337, 44.461, 48.278, 56.892}, + /*29*/ {39.087, 42.557, 45.722, 49.588, 58.301}, + /*30*/ {40.256, 43.773, 46.979, 50.892, 59.703}, + /*31*/ {41.422, 44.985, 48.232, 52.191, 61.098}, + /*32*/ {42.585, 46.194, 49.480, 53.486, 62.487}, + /*33*/ {43.745, 47.400, 50.725, 54.776, 63.870}, + /*34*/ {44.903, 48.602, 51.966, 56.061, 65.247}, + /*35*/ {46.059, 49.802, 53.203, 57.342, 66.619}, + /*36*/ {47.212, 50.998, 54.437, 58.619, 67.985}, + /*37*/ {48.363, 52.192, 55.668, 59.893, 69.347}, + /*38*/ {49.513, 53.384, 56.896, 61.162, 70.703}, + /*39*/ {50.660, 54.572, 58.120, 62.428, 72.055}, + /*40*/ {51.805, 55.758, 59.342, 63.691, 73.402}, + /*41*/ {52.949, 56.942, 60.561, 64.950, 74.745}, + /*42*/ {54.090, 58.124, 61.777, 66.206, 76.084}, + /*43*/ {55.230, 59.304, 62.990, 67.459, 77.419}, + /*44*/ {56.369, 60.481, 64.201, 68.710, 78.750}, + /*45*/ {57.505, 61.656, 65.410, 69.957, 80.077}, + /*46*/ {58.641, 62.830, 66.617, 71.201, 81.400}, + /*47*/ {59.774, 64.001, 67.821, 72.443, 82.720}, + /*48*/ {60.907, 65.171, 69.023, 73.683, 84.037}, + /*49*/ {62.038, 66.339, 70.222, 74.919, 85.351}, + /*50*/ {63.167, 67.505, 71.420, 76.154, 86.661}, + /*51*/ {64.295, 68.669, 72.616, 77.386, 87.968}, + /*52*/ {65.422, 69.832, 73.810, 78.616, 89.272}, + /*53*/ {66.548, 70.993, 75.002, 79.843, 90.573}, + /*54*/ {67.673, 72.153, 76.192, 81.069, 91.872}, + /*55*/ {68.796, 73.311, 77.380, 82.292, 93.168}, + /*56*/ {69.919, 74.468, 78.567, 83.513, 94.461}, + /*57*/ {71.040, 75.624, 79.752, 84.733, 95.751}, + /*58*/ {72.160, 76.778, 80.936, 85.950, 97.039}, + /*59*/ {73.279, 77.931, 82.117, 87.166, 98.324}, + /*60*/ {74.397, 79.082, 83.298, 88.379, 99.607}, + /*61*/ {75.514, 80.232, 84.476, 89.591, 100.888}, + /*62*/ {76.630, 81.381, 85.654, 90.802, 102.166}, + /*63*/ {77.745, 82.529, 86.830, 92.010, 103.442}, + /*64*/ {78.860, 83.675, 88.004, 93.217, 104.716}, + /*65*/ {79.973, 84.821, 89.177, 94.422, 105.988}, + /*66*/ {81.085, 85.965, 90.349, 95.626, 107.258}, + /*67*/ {82.197, 87.108, 91.519, 96.828, 108.526}, + /*68*/ {83.308, 88.250, 92.689, 98.028, 109.791}, + /*69*/ {84.418, 89.391, 93.856, 99.228, 111.055}, + /*70*/ {85.527, 90.531, 95.023, 100.425, 112.317}, + /*71*/ {86.635, 91.670, 96.189, 101.621, 113.577}, + /*72*/ {87.743, 92.808, 97.353, 102.816, 114.835}, + /*73*/ {88.850, 93.945, 98.516, 104.010, 116.092}, + /*74*/ {89.956, 95.081, 99.678, 105.202, 117.346}, + /*75*/ {91.061, 96.217, 100.839, 106.393, 118.599}, + /*76*/ {92.166, 97.351, 101.999, 107.583, 119.850}, + /*77*/ {93.270, 98.484, 103.158, 108.771, 121.100}, + /*78*/ {94.374, 99.617, 104.316, 109.958, 122.348}, + /*79*/ {95.476, 100.749, 105.473, 111.144, 123.594}, + /*80*/ {96.578, 101.879, 106.629, 112.329, 124.839}, + /*81*/ {97.680, 103.010, 107.783, 113.512, 126.083}, + /*82*/ {98.780, 104.139, 108.937, 114.695, 127.324}, + /*83*/ {99.880, 105.267, 110.090, 115.876, 128.565}, + /*84*/ {100.980, 106.395, 111.242, 117.057, 129.804}, + /*85*/ {102.079, 107.522, 112.393, 118.236, 131.041}, + /*86*/ {103.177, 108.648, 113.544, 119.414, 132.277}, + /*87*/ {104.275, 109.773, 114.693, 120.591, 133.512}, + /*88*/ {105.372, 110.898, 115.841, 121.767, 134.746}, + /*89*/ {106.469, 112.022, 116.989, 122.942, 135.978}, + /*90*/ {107.565, 113.145, 118.136, 124.116, 137.208}, + /*91*/ {108.661, 114.268, 119.282, 125.289, 138.438}, + /*92*/ {109.756, 115.390, 120.427, 126.462, 139.666}, + /*93*/ {110.850, 116.511, 121.571, 127.633, 140.893}, + /*94*/ {111.944, 117.632, 122.715, 128.803, 142.119}, + /*95*/ {113.038, 118.752, 123.858, 129.973, 143.344}, + /*96*/ {114.131, 119.871, 125.000, 131.141, 144.567}, + /*97*/ {115.223, 120.990, 126.141, 132.309, 145.789}, + /*98*/ {116.315, 122.108, 127.282, 133.476, 147.010}, + /*99*/ {117.407, 123.225, 128.422, 134.642, 148.230}, + /*100*/ {118.498, 124.342, 129.561, 135.807, 149.449} + /**/}; + + // 0.90 0.95 0.975 0.99 0.999 + for (int i = 0; i < ABSL_ARRAYSIZE(data); i++) { + const double E = 0.0001; + EXPECT_NEAR(ChiSquarePValue(data[i][0], i + 1), 0.10, E) + << i << " " << data[i][0]; + EXPECT_NEAR(ChiSquarePValue(data[i][1], i + 1), 0.05, E) + << i << " " << data[i][1]; + EXPECT_NEAR(ChiSquarePValue(data[i][2], i + 1), 0.025, E) + << i << " " << data[i][2]; + EXPECT_NEAR(ChiSquarePValue(data[i][3], i + 1), 0.01, E) + << i << " " << data[i][3]; + EXPECT_NEAR(ChiSquarePValue(data[i][4], i + 1), 0.001, E) + << i << " " << data[i][4]; + + const double F = 0.1; + EXPECT_NEAR(ChiSquareValue(i + 1, 0.90), data[i][0], F) << i; + EXPECT_NEAR(ChiSquareValue(i + 1, 0.95), data[i][1], F) << i; + EXPECT_NEAR(ChiSquareValue(i + 1, 0.975), data[i][2], F) << i; + EXPECT_NEAR(ChiSquareValue(i + 1, 0.99), data[i][3], F) << i; + EXPECT_NEAR(ChiSquareValue(i + 1, 0.999), data[i][4], F) << i; + } +} + +TEST(ChiSquareTest, ChiSquareTwoIterator) { + // Test data from http://www.stat.yale.edu/Courses/1997-98/101/chigf.htm + // Null-hypothesis: This data is normally distributed. + const int counts[10] = {6, 6, 18, 33, 38, 38, 28, 21, 9, 3}; + const double expected[10] = {4.6, 8.8, 18.4, 30.0, 38.2, + 38.2, 30.0, 18.4, 8.8, 4.6}; + double chi_square = ChiSquare(std::begin(counts), std::end(counts), + std::begin(expected), std::end(expected)); + EXPECT_NEAR(chi_square, 2.69, 0.001); + + // Degrees of freedom: 10 bins. two estimated parameters. = 10 - 2 - 1. + const int dof = 7; + // The critical value of 7, 95% => 14.067 (see above test) + double p_value_05 = ChiSquarePValue(14.067, dof); + EXPECT_NEAR(p_value_05, 0.05, 0.001); // 95%-ile p-value + + double p_actual = ChiSquarePValue(chi_square, dof); + EXPECT_GT(p_actual, 0.05); // Accept the null hypothesis. +} + +TEST(ChiSquareTest, DiceRolls) { + // Assume we are testing 102 fair dice rolls. + // Null-hypothesis: This data is fairly distributed. + // + // The dof value of 4, @95% = 9.488 (see above test) + // The dof value of 5, @95% = 11.070 + const int rolls[6] = {22, 11, 17, 14, 20, 18}; + double sum = std::accumulate(std::begin(rolls), std::end(rolls), double{0}); + size_t n = std::distance(std::begin(rolls), std::end(rolls)); + + double a = ChiSquareWithExpected(std::begin(rolls), std::end(rolls), sum / n); + EXPECT_NEAR(a, 4.70588, 1e-5); + EXPECT_LT(a, ChiSquareValue(4, 0.95)); + + double p_a = ChiSquarePValue(a, 4); + EXPECT_NEAR(p_a, 0.318828, 1e-5); // Accept the null hypothesis. + + double b = ChiSquareWithExpected(std::begin(rolls), std::end(rolls), 17.0); + EXPECT_NEAR(b, 4.70588, 1e-5); + EXPECT_LT(b, ChiSquareValue(5, 0.95)); + + double p_b = ChiSquarePValue(b, 5); + EXPECT_NEAR(p_b, 0.4528180, 1e-5); // Accept the null hypothesis. +} + +} // namespace diff --git a/absl/random/internal/distribution_caller.h b/absl/random/internal/distribution_caller.h new file mode 100644 index 00000000..dd06c985 --- /dev/null +++ b/absl/random/internal/distribution_caller.h @@ -0,0 +1,58 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_ +#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_ + +#include <utility> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// DistributionCaller provides an opportunity to overload the general +// mechanism for calling a distribution, allowing for mock-RNG classes +// to intercept such calls. +template <typename URBG> +struct DistributionCaller { + // Call the provided distribution type. The parameters are expected + // to be explicitly specified. + // DistrT is the distribution type. + // FormatT is the formatter type: + // + // struct FormatT { + // using result_type = distribution_t::result_type; + // static std::string FormatCall( + // const distribution_t& distr, + // absl::Span<const result_type>); + // + // static std::string FormatExpectation( + // absl::string_view match_args, + // absl::Span<const result_t> results); + // } + // + template <typename DistrT, typename FormatT, typename... Args> + static typename DistrT::result_type Call(URBG* urbg, Args&&... args) { + DistrT dist(std::forward<Args>(args)...); + return dist(*urbg); + } +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_ diff --git a/absl/random/internal/distribution_impl.h b/absl/random/internal/distribution_impl.h new file mode 100644 index 00000000..a8e5d61e --- /dev/null +++ b/absl/random/internal/distribution_impl.h @@ -0,0 +1,262 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ +#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ + +// This file contains some implementation details which are used by one or more +// of the absl random number distributions. + +#include <cfloat> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <limits> +#include <type_traits> + +#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64) +#include <intrin.h> // NOLINT(build/include_order) +#pragma intrinsic(_umul128) +#define ABSL_INTERNAL_USE_UMUL128 1 +#endif + +#include "absl/base/config.h" +#include "absl/base/internal/bits.h" +#include "absl/numeric/int128.h" +#include "absl/random/internal/fastmath.h" +#include "absl/random/internal/traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// Creates a double from `bits`, with the template fields controlling the +// output. +// +// RandU64To is both more efficient and generates more unique values in the +// result interval than known implementations of std::generate_canonical(). +// +// The `Signed` parameter controls whether positive, negative, or both are +// returned (thus affecting the output interval). +// When Signed == SignedValueT, range is U(-1, 1) +// When Signed == NegativeValueT, range is U(-1, 0) +// When Signed == PositiveValueT, range is U(0, 1) +// +// When the `IncludeZero` parameter is true, the function may return 0 for some +// inputs, otherwise it never returns 0. +// +// The `ExponentBias` parameter determines the scale of the output range by +// adjusting the exponent. +// +// When a value in U(0,1) is required, use: +// RandU64ToDouble<PositiveValueT, true, 0>(); +// +// When a value in U(-1,1) is required, use: +// RandU64ToDouble<SignedValueT, false, 0>() => U(-1, 1) +// This generates more distinct values than the mathematically equivalent +// expression `U(0, 1) * 2.0 - 1.0`, and is preferable. +// +// Scaling the result by powers of 2 (and avoiding a multiply) is also possible: +// RandU64ToDouble<PositiveValueT, false, 1>(); => U(0, 2) +// RandU64ToDouble<PositiveValueT, false, -1>(); => U(0, 0.5) +// + +// Tristate types controlling the output. +struct PositiveValueT {}; +struct NegativeValueT {}; +struct SignedValueT {}; + +// RandU64ToDouble is the double-result variant of RandU64To, described above. +template <typename Signed, bool IncludeZero, int ExponentBias = 0> +inline double RandU64ToDouble(uint64_t bits) { + static_assert(std::is_same<Signed, PositiveValueT>::value || + std::is_same<Signed, NegativeValueT>::value || + std::is_same<Signed, SignedValueT>::value, + ""); + + // Maybe use the left-most bit for a sign bit. + uint64_t sign = std::is_same<Signed, NegativeValueT>::value + ? 0x8000000000000000ull + : 0; // Sign bits. + + if (std::is_same<Signed, SignedValueT>::value) { + sign = bits & 0x8000000000000000ull; + bits = bits & 0x7FFFFFFFFFFFFFFFull; + } + if (IncludeZero) { + if (bits == 0u) return 0; + } + + // Number of leading zeros is mapped to the exponent: 2^-clz + int clz = base_internal::CountLeadingZeros64(bits); + // Shift number left to erase leading zeros. + bits <<= IncludeZero ? clz : (clz & 63); + + // Shift number right to remove bits that overflow double mantissa. The + // direction of the shift depends on `clz`. + bits >>= (64 - DBL_MANT_DIG); + + // Compute IEEE 754 double exponent. + // In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the + // exponent to account for that. + const uint64_t exp = + (std::is_same<Signed, SignedValueT>::value ? 1023U : 1022U) + + static_cast<uint64_t>(ExponentBias - clz); + constexpr int kExp = DBL_MANT_DIG - 1; + // Construct IEEE 754 double from exponent and mantissa. + const uint64_t val = sign | (exp << kExp) | (bits & ((1ULL << kExp) - 1U)); + + double res; + static_assert(sizeof(res) == sizeof(val), "double is not 64 bit"); + // Memcpy value from "val" to "res" to avoid aliasing problems. Assumes that + // endian-ness is same for double and uint64_t. + std::memcpy(&res, &val, sizeof(res)); + + return res; +} + +// RandU64ToFloat is the float-result variant of RandU64To, described above. +template <typename Signed, bool IncludeZero, int ExponentBias = 0> +inline float RandU64ToFloat(uint64_t bits) { + static_assert(std::is_same<Signed, PositiveValueT>::value || + std::is_same<Signed, NegativeValueT>::value || + std::is_same<Signed, SignedValueT>::value, + ""); + + // Maybe use the left-most bit for a sign bit. + uint64_t sign = std::is_same<Signed, NegativeValueT>::value + ? 0x80000000ul + : 0; // Sign bits. + + if (std::is_same<Signed, SignedValueT>::value) { + uint64_t a = bits & 0x8000000000000000ull; + sign = static_cast<uint32_t>(a >> 32); + bits = bits & 0x7FFFFFFFFFFFFFFFull; + } + if (IncludeZero) { + if (bits == 0u) return 0; + } + + // Number of leading zeros is mapped to the exponent: 2^-clz + int clz = base_internal::CountLeadingZeros64(bits); + // Shift number left to erase leading zeros. + bits <<= IncludeZero ? clz : (clz & 63); + // Shift number right to remove bits that overflow double mantissa. The + // direction of the shift depends on `clz`. + bits >>= (64 - FLT_MANT_DIG); + + // Construct IEEE 754 float exponent. + // In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the + // exponent to account for that. + const uint32_t exp = + (std::is_same<Signed, SignedValueT>::value ? 127U : 126U) + + static_cast<uint32_t>(ExponentBias - clz); + constexpr int kExp = FLT_MANT_DIG - 1; + const uint32_t val = sign | (exp << kExp) | (bits & ((1U << kExp) - 1U)); + + float res; + static_assert(sizeof(res) == sizeof(val), "float is not 32 bit"); + // Assumes that endian-ness is same for float and uint32_t. + std::memcpy(&res, &val, sizeof(res)); + + return res; +} + +template <typename Result> +struct RandU64ToReal { + template <typename Signed, bool IncludeZero, int ExponentBias = 0> + static inline Result Value(uint64_t bits) { + return RandU64ToDouble<Signed, IncludeZero, ExponentBias>(bits); + } +}; + +template <> +struct RandU64ToReal<float> { + template <typename Signed, bool IncludeZero, int ExponentBias = 0> + static inline float Value(uint64_t bits) { + return RandU64ToFloat<Signed, IncludeZero, ExponentBias>(bits); + } +}; + +inline uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return uint128(static_cast<__uint128_t>(a) * b); +#elif defined(ABSL_INTERNAL_USE_UMUL128) + // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC. + uint64_t high = 0; + const uint64_t low = _umul128(a, b, &high); + return absl::MakeUint128(high, low); +#else + // uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit + // multiply. However there are many cases where that is not necessary, and it + // is only necessary to support a 64-bit x 64-bit = 128-bit multiply. This is + // for those cases. + const uint64_t a00 = static_cast<uint32_t>(a); + const uint64_t a32 = a >> 32; + const uint64_t b00 = static_cast<uint32_t>(b); + const uint64_t b32 = b >> 32; + + const uint64_t c00 = a00 * b00; + const uint64_t c32a = a00 * b32; + const uint64_t c32b = a32 * b00; + const uint64_t c64 = a32 * b32; + + const uint32_t carry = + static_cast<uint32_t>(((c00 >> 32) + static_cast<uint32_t>(c32a) + + static_cast<uint32_t>(c32b)) >> + 32); + + return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry, + c00 + (c32a << 32) + (c32b << 32)); +#endif +} + +// wide_multiply<T> multiplies two N-bit values to a 2N-bit result. +template <typename UIntType> +struct wide_multiply { + static constexpr size_t kN = std::numeric_limits<UIntType>::digits; + using input_type = UIntType; + using result_type = typename random_internal::unsigned_bits<kN * 2>::type; + + static result_type multiply(input_type a, input_type b) { + return static_cast<result_type>(a) * b; + } + + static input_type hi(result_type r) { return r >> kN; } + static input_type lo(result_type r) { return r; } + + static_assert(std::is_unsigned<UIntType>::value, + "Class-template wide_multiply<> argument must be unsigned."); +}; + +#ifndef ABSL_HAVE_INTRINSIC_INT128 +template <> +struct wide_multiply<uint64_t> { + using input_type = uint64_t; + using result_type = uint128; + + static result_type multiply(uint64_t a, uint64_t b) { + return MultiplyU64ToU128(a, b); + } + + static uint64_t hi(result_type r) { return Uint128High64(r); } + static uint64_t lo(result_type r) { return Uint128Low64(r); } +}; +#endif + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ diff --git a/absl/random/internal/distribution_impl_test.cc b/absl/random/internal/distribution_impl_test.cc new file mode 100644 index 00000000..09e7a318 --- /dev/null +++ b/absl/random/internal/distribution_impl_test.cc @@ -0,0 +1,506 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/distribution_impl.h" + +#include "gtest/gtest.h" +#include "absl/base/internal/bits.h" +#include "absl/flags/flag.h" +#include "absl/numeric/int128.h" + +ABSL_FLAG(int64_t, absl_random_test_trials, 50000, + "Number of trials for the probability tests."); + +using absl::random_internal::NegativeValueT; +using absl::random_internal::PositiveValueT; +using absl::random_internal::RandU64ToDouble; +using absl::random_internal::RandU64ToFloat; +using absl::random_internal::SignedValueT; + +namespace { + +TEST(DistributionImplTest, U64ToFloat_Positive_NoZero_Test) { + auto ToFloat = [](uint64_t a) { + return RandU64ToFloat<PositiveValueT, false>(a); + }; + EXPECT_EQ(ToFloat(0x0000000000000000), 2.710505431e-20f); + EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f); + EXPECT_EQ(ToFloat(0x8000000000000000), 0.5); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f); +} + +TEST(DistributionImplTest, U64ToFloat_Positive_Zero_Test) { + auto ToFloat = [](uint64_t a) { + return RandU64ToFloat<PositiveValueT, true>(a); + }; + EXPECT_EQ(ToFloat(0x0000000000000000), 0.0); + EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f); + EXPECT_EQ(ToFloat(0x8000000000000000), 0.5); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f); +} + +TEST(DistributionImplTest, U64ToFloat_Negative_NoZero_Test) { + auto ToFloat = [](uint64_t a) { + return RandU64ToFloat<NegativeValueT, false>(a); + }; + EXPECT_EQ(ToFloat(0x0000000000000000), -2.710505431e-20f); + EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f); + EXPECT_EQ(ToFloat(0x8000000000000000), -0.5); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); +} + +TEST(DistributionImplTest, U64ToFloat_Signed_NoZero_Test) { + auto ToFloat = [](uint64_t a) { + return RandU64ToFloat<SignedValueT, false>(a); + }; + EXPECT_EQ(ToFloat(0x0000000000000000), 5.421010862e-20f); + EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f); + EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), 0.9999999404f); + EXPECT_EQ(ToFloat(0x8000000000000000), -5.421010862e-20f); + EXPECT_EQ(ToFloat(0x8000000000000001), -1.084202172e-19f); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); +} + +TEST(DistributionImplTest, U64ToFloat_Signed_Zero_Test) { + auto ToFloat = [](uint64_t a) { + return RandU64ToFloat<SignedValueT, true>(a); + }; + EXPECT_EQ(ToFloat(0x0000000000000000), 0); + EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f); + EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), 0.9999999404f); + EXPECT_EQ(ToFloat(0x8000000000000000), 0); + EXPECT_EQ(ToFloat(0x8000000000000001), -1.084202172e-19f); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); +} + +TEST(DistributionImplTest, U64ToFloat_Signed_Bias_Test) { + auto ToFloat = [](uint64_t a) { + return RandU64ToFloat<SignedValueT, true, 1>(a); + }; + EXPECT_EQ(ToFloat(0x0000000000000000), 0); + EXPECT_EQ(ToFloat(0x0000000000000001), 2 * 1.084202172e-19f); + EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), 2 * 0.9999999404f); + EXPECT_EQ(ToFloat(0x8000000000000000), 0); + EXPECT_EQ(ToFloat(0x8000000000000001), 2 * -1.084202172e-19f); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 2 * -0.9999999404f); +} + +TEST(DistributionImplTest, U64ToFloatTest) { + auto ToFloat = [](uint64_t a) -> float { + return RandU64ToFloat<PositiveValueT, true>(a); + }; + + EXPECT_EQ(ToFloat(0x0000000000000000), 0.0f); + + EXPECT_EQ(ToFloat(0x8000000000000000), 0.5f); + EXPECT_EQ(ToFloat(0x8000000000000001), 0.5f); + EXPECT_EQ(ToFloat(0x800000FFFFFFFFFF), 0.5f); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f); + + EXPECT_GT(ToFloat(0x0000000000000001), 0.0f); + + EXPECT_NE(ToFloat(0x7FFFFF0000000000), ToFloat(0x7FFFFEFFFFFFFFFF)); + + EXPECT_LT(ToFloat(0xFFFFFFFFFFFFFFFF), 1.0f); + int32_t two_to_24 = 1 << 24; + EXPECT_EQ(static_cast<int32_t>(ToFloat(0xFFFFFFFFFFFFFFFF) * two_to_24), + two_to_24 - 1); + EXPECT_NE(static_cast<int32_t>(ToFloat(0xFFFFFFFFFFFFFFFF) * two_to_24 * 2), + two_to_24 * 2 - 1); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), ToFloat(0xFFFFFF0000000000)); + EXPECT_NE(ToFloat(0xFFFFFFFFFFFFFFFF), ToFloat(0xFFFFFEFFFFFFFFFF)); + EXPECT_EQ(ToFloat(0x7FFFFFFFFFFFFFFF), ToFloat(0x7FFFFF8000000000)); + EXPECT_NE(ToFloat(0x7FFFFFFFFFFFFFFF), ToFloat(0x7FFFFF7FFFFFFFFF)); + EXPECT_EQ(ToFloat(0x3FFFFFFFFFFFFFFF), ToFloat(0x3FFFFFC000000000)); + EXPECT_NE(ToFloat(0x3FFFFFFFFFFFFFFF), ToFloat(0x3FFFFFBFFFFFFFFF)); + + // For values where every bit counts, the values scale as multiples of the + // input. + for (int i = 0; i < 100; ++i) { + EXPECT_EQ(i * ToFloat(0x0000000000000001), ToFloat(i)); + } + + // For each i: value generated from (1 << i). + float exp_values[64]; + exp_values[63] = 0.5f; + for (int i = 62; i >= 0; --i) exp_values[i] = 0.5f * exp_values[i + 1]; + constexpr uint64_t one = 1; + for (int i = 0; i < 64; ++i) { + EXPECT_EQ(ToFloat(one << i), exp_values[i]); + for (int j = 1; j < FLT_MANT_DIG && i - j >= 0; ++j) { + EXPECT_NE(exp_values[i] + exp_values[i - j], exp_values[i]); + EXPECT_EQ(ToFloat((one << i) + (one << (i - j))), + exp_values[i] + exp_values[i - j]); + } + for (int j = FLT_MANT_DIG; i - j >= 0; ++j) { + EXPECT_EQ(exp_values[i] + exp_values[i - j], exp_values[i]); + EXPECT_EQ(ToFloat((one << i) + (one << (i - j))), exp_values[i]); + } + } +} + +TEST(DistributionImplTest, U64ToDouble_Positive_NoZero_Test) { + auto ToDouble = [](uint64_t a) { + return RandU64ToDouble<PositiveValueT, false>(a); + }; + + EXPECT_EQ(ToDouble(0x0000000000000000), 2.710505431213761085e-20); + EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x0000000000000002), 1.084202172485504434e-19); + EXPECT_EQ(ToDouble(0x8000000000000000), 0.5); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978); +} + +TEST(DistributionImplTest, U64ToDouble_Positive_Zero_Test) { + auto ToDouble = [](uint64_t a) { + return RandU64ToDouble<PositiveValueT, true>(a); + }; + + EXPECT_EQ(ToDouble(0x0000000000000000), 0.0); + EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x8000000000000000), 0.5); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978); +} + +TEST(DistributionImplTest, U64ToDouble_Negative_NoZero_Test) { + auto ToDouble = [](uint64_t a) { + return RandU64ToDouble<NegativeValueT, false>(a); + }; + + EXPECT_EQ(ToDouble(0x0000000000000000), -2.710505431213761085e-20); + EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19); + EXPECT_EQ(ToDouble(0x8000000000000000), -0.5); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); +} + +TEST(DistributionImplTest, U64ToDouble_Signed_NoZero_Test) { + auto ToDouble = [](uint64_t a) { + return RandU64ToDouble<SignedValueT, false>(a); + }; + + EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19); + EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), 0.999999999999999888978); + EXPECT_EQ(ToDouble(0x8000000000000000), -5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); +} + +TEST(DistributionImplTest, U64ToDouble_Signed_Zero_Test) { + auto ToDouble = [](uint64_t a) { + return RandU64ToDouble<SignedValueT, true>(a); + }; + EXPECT_EQ(ToDouble(0x0000000000000000), 0); + EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19); + EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), 0.999999999999999888978); + EXPECT_EQ(ToDouble(0x8000000000000000), 0); + EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); +} + +TEST(DistributionImplTest, U64ToDouble_Signed_Bias_Test) { + auto ToDouble = [](uint64_t a) { + return RandU64ToDouble<SignedValueT, true, -1>(a); + }; + EXPECT_EQ(ToDouble(0x0000000000000000), 0); + EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19 / 2); + EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), 0.999999999999999888978 / 2); + EXPECT_EQ(ToDouble(0x8000000000000000), 0); + EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19 / 2); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978 / 2); +} + +TEST(DistributionImplTest, U64ToDoubleTest) { + auto ToDouble = [](uint64_t a) { + return RandU64ToDouble<PositiveValueT, true>(a); + }; + + EXPECT_EQ(ToDouble(0x0000000000000000), 0.0); + EXPECT_EQ(ToDouble(0x0000000000000000), 0.0); + + EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x7fffffffffffffef), 0.499999999999999944489); + EXPECT_EQ(ToDouble(0x8000000000000000), 0.5); + + // For values > 0.5, RandU64ToDouble discards up to 11 bits. (64-53). + EXPECT_EQ(ToDouble(0x8000000000000001), 0.5); + EXPECT_EQ(ToDouble(0x80000000000007FF), 0.5); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978); + EXPECT_NE(ToDouble(0x7FFFFFFFFFFFF800), ToDouble(0x7FFFFFFFFFFFF7FF)); + + EXPECT_LT(ToDouble(0xFFFFFFFFFFFFFFFF), 1.0); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), ToDouble(0xFFFFFFFFFFFFF800)); + EXPECT_NE(ToDouble(0xFFFFFFFFFFFFFFFF), ToDouble(0xFFFFFFFFFFFFF7FF)); + EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFFC00)); + EXPECT_NE(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFFBFF)); + EXPECT_EQ(ToDouble(0x3FFFFFFFFFFFFFFF), ToDouble(0x3FFFFFFFFFFFFE00)); + EXPECT_NE(ToDouble(0x3FFFFFFFFFFFFFFF), ToDouble(0x3FFFFFFFFFFFFDFF)); + + EXPECT_EQ(ToDouble(0x1000000000000001), 0.0625); + EXPECT_EQ(ToDouble(0x2000000000000001), 0.125); + EXPECT_EQ(ToDouble(0x3000000000000001), 0.1875); + EXPECT_EQ(ToDouble(0x4000000000000001), 0.25); + EXPECT_EQ(ToDouble(0x5000000000000001), 0.3125); + EXPECT_EQ(ToDouble(0x6000000000000001), 0.375); + EXPECT_EQ(ToDouble(0x7000000000000001), 0.4375); + EXPECT_EQ(ToDouble(0x8000000000000001), 0.5); + EXPECT_EQ(ToDouble(0x9000000000000001), 0.5625); + EXPECT_EQ(ToDouble(0xa000000000000001), 0.625); + EXPECT_EQ(ToDouble(0xb000000000000001), 0.6875); + EXPECT_EQ(ToDouble(0xc000000000000001), 0.75); + EXPECT_EQ(ToDouble(0xd000000000000001), 0.8125); + EXPECT_EQ(ToDouble(0xe000000000000001), 0.875); + EXPECT_EQ(ToDouble(0xf000000000000001), 0.9375); + + // Large powers of 2. + int64_t two_to_53 = int64_t{1} << 53; + EXPECT_EQ(static_cast<int64_t>(ToDouble(0xFFFFFFFFFFFFFFFF) * two_to_53), + two_to_53 - 1); + EXPECT_NE(static_cast<int64_t>(ToDouble(0xFFFFFFFFFFFFFFFF) * two_to_53 * 2), + two_to_53 * 2 - 1); + + // For values where every bit counts, the values scale as multiples of the + // input. + for (int i = 0; i < 100; ++i) { + EXPECT_EQ(i * ToDouble(0x0000000000000001), ToDouble(i)); + } + + // For each i: value generated from (1 << i). + double exp_values[64]; + exp_values[63] = 0.5; + for (int i = 62; i >= 0; --i) exp_values[i] = 0.5 * exp_values[i + 1]; + constexpr uint64_t one = 1; + for (int i = 0; i < 64; ++i) { + EXPECT_EQ(ToDouble(one << i), exp_values[i]); + for (int j = 1; j < DBL_MANT_DIG && i - j >= 0; ++j) { + EXPECT_NE(exp_values[i] + exp_values[i - j], exp_values[i]); + EXPECT_EQ(ToDouble((one << i) + (one << (i - j))), + exp_values[i] + exp_values[i - j]); + } + for (int j = DBL_MANT_DIG; i - j >= 0; ++j) { + EXPECT_EQ(exp_values[i] + exp_values[i - j], exp_values[i]); + EXPECT_EQ(ToDouble((one << i) + (one << (i - j))), exp_values[i]); + } + } +} + +TEST(DistributionImplTest, U64ToDoubleSignedTest) { + auto ToDouble = [](uint64_t a) { + return RandU64ToDouble<SignedValueT, false>(a); + }; + + EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19); + + EXPECT_EQ(ToDouble(0x8000000000000000), -5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x8000000000000001), -1.084202172485504434e-19); + + const double e_plus = ToDouble(0x0000000000000001); + const double e_minus = ToDouble(0x8000000000000001); + EXPECT_EQ(e_plus, 1.084202172485504434e-19); + EXPECT_EQ(e_minus, -1.084202172485504434e-19); + + EXPECT_EQ(ToDouble(0x3fffffffffffffef), 0.499999999999999944489); + EXPECT_EQ(ToDouble(0xbfffffffffffffef), -0.499999999999999944489); + + // For values > 0.5, RandU64ToDouble discards up to 10 bits. (63-53). + EXPECT_EQ(ToDouble(0x4000000000000000), 0.5); + EXPECT_EQ(ToDouble(0x4000000000000001), 0.5); + EXPECT_EQ(ToDouble(0x40000000000003FF), 0.5); + + EXPECT_EQ(ToDouble(0xC000000000000000), -0.5); + EXPECT_EQ(ToDouble(0xC000000000000001), -0.5); + EXPECT_EQ(ToDouble(0xC0000000000003FF), -0.5); + + EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFe), 0.999999999999999888978); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFe), -0.999999999999999888978); + + EXPECT_NE(ToDouble(0x7FFFFFFFFFFFF800), ToDouble(0x7FFFFFFFFFFFF7FF)); + + EXPECT_LT(ToDouble(0x7FFFFFFFFFFFFFFF), 1.0); + EXPECT_GT(ToDouble(0x7FFFFFFFFFFFFFFF), 0.9999999999); + + EXPECT_GT(ToDouble(0xFFFFFFFFFFFFFFFe), -1.0); + EXPECT_LT(ToDouble(0xFFFFFFFFFFFFFFFe), -0.999999999); + + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFe), ToDouble(0xFFFFFFFFFFFFFC00)); + EXPECT_EQ(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFFC00)); + EXPECT_NE(ToDouble(0xFFFFFFFFFFFFFFFe), ToDouble(0xFFFFFFFFFFFFF3FF)); + EXPECT_NE(ToDouble(0x7FFFFFFFFFFFFFFF), ToDouble(0x7FFFFFFFFFFFF3FF)); + + EXPECT_EQ(ToDouble(0x1000000000000001), 0.125); + EXPECT_EQ(ToDouble(0x2000000000000001), 0.25); + EXPECT_EQ(ToDouble(0x3000000000000001), 0.375); + EXPECT_EQ(ToDouble(0x4000000000000001), 0.5); + EXPECT_EQ(ToDouble(0x5000000000000001), 0.625); + EXPECT_EQ(ToDouble(0x6000000000000001), 0.75); + EXPECT_EQ(ToDouble(0x7000000000000001), 0.875); + EXPECT_EQ(ToDouble(0x7800000000000001), 0.9375); + EXPECT_EQ(ToDouble(0x7c00000000000001), 0.96875); + EXPECT_EQ(ToDouble(0x7e00000000000001), 0.984375); + EXPECT_EQ(ToDouble(0x7f00000000000001), 0.9921875); + + // 0x8000000000000000 ~= 0 + EXPECT_EQ(ToDouble(0x9000000000000001), -0.125); + EXPECT_EQ(ToDouble(0xa000000000000001), -0.25); + EXPECT_EQ(ToDouble(0xb000000000000001), -0.375); + EXPECT_EQ(ToDouble(0xc000000000000001), -0.5); + EXPECT_EQ(ToDouble(0xd000000000000001), -0.625); + EXPECT_EQ(ToDouble(0xe000000000000001), -0.75); + EXPECT_EQ(ToDouble(0xf000000000000001), -0.875); + + // Large powers of 2. + int64_t two_to_53 = int64_t{1} << 53; + EXPECT_EQ(static_cast<int64_t>(ToDouble(0x7FFFFFFFFFFFFFFF) * two_to_53), + two_to_53 - 1); + EXPECT_EQ(static_cast<int64_t>(ToDouble(0xFFFFFFFFFFFFFFFF) * two_to_53), + -(two_to_53 - 1)); + + EXPECT_NE(static_cast<int64_t>(ToDouble(0x7FFFFFFFFFFFFFFF) * two_to_53 * 2), + two_to_53 * 2 - 1); + + // For values where every bit counts, the values scale as multiples of the + // input. + for (int i = 1; i < 100; ++i) { + EXPECT_EQ(i * e_plus, ToDouble(i)) << i; + EXPECT_EQ(i * e_minus, ToDouble(0x8000000000000000 | i)) << i; + } +} + +TEST(DistributionImplTest, ExhaustiveFloat) { + using absl::base_internal::CountLeadingZeros64; + auto ToFloat = [](uint64_t a) { + return RandU64ToFloat<PositiveValueT, true>(a); + }; + + // Rely on RandU64ToFloat generating values from greatest to least when + // supplied with uint64_t values from greatest (0xfff...) to least (0x0). Thus, + // this algorithm stores the previous value, and if the new value is at + // greater than or equal to the previous value, then there is a collision in + // the generation algorithm. + // + // Use the computation below to convert the random value into a result: + // double res = a() * (1.0f - sample) + b() * sample; + float last_f = 1.0, last_g = 2.0; + uint64_t f_collisions = 0, g_collisions = 0; + uint64_t f_unique = 0, g_unique = 0; + uint64_t total = 0; + auto count = [&](const float r) { + total++; + // `f` is mapped to the range [0, 1) (default) + const float f = 0.0f * (1.0f - r) + 1.0f * r; + if (f >= last_f) { + f_collisions++; + } else { + f_unique++; + last_f = f; + } + // `g` is mapped to the range [1, 2) + const float g = 1.0f * (1.0f - r) + 2.0f * r; + if (g >= last_g) { + g_collisions++; + } else { + g_unique++; + last_g = g; + } + }; + + size_t limit = absl::GetFlag(FLAGS_absl_random_test_trials); + + // Generate all uint64_t which have unique floating point values. + // Counting down from 0xFFFFFFFFFFFFFFFFu ... 0x0u + uint64_t x = ~uint64_t(0); + for (; x != 0 && limit > 0;) { + constexpr int kDig = (64 - FLT_MANT_DIG); + // Set a decrement value & the next point at which to change + // the decrement value. By default these are 1, 0. + uint64_t dec = 1; + uint64_t chk = 0; + + // Adjust decrement and check value based on how many leading 0 + // bits are set in the current value. + const int clz = CountLeadingZeros64(x); + if (clz < kDig) { + dec <<= (kDig - clz); + chk = (~uint64_t(0)) >> (clz + 1); + } + for (; x > chk && limit > 0; x -= dec) { + count(ToFloat(x)); + --limit; + } + } + + static_assert(FLT_MANT_DIG == 24, + "The float type is expected to have a 24 bit mantissa."); + + if (limit != 0) { + // There are between 2^28 and 2^29 unique values in the range [0, 1). For + // the low values of x, there are 2^24 -1 unique values. Once x > 2^24, + // there are 40 * 2^24 unique values. Thus: + // (2 + 4 + 8 ... + 2^23) + 40 * 2^23 + EXPECT_LT(1 << 28, f_unique); + EXPECT_EQ((1 << 24) + 40 * (1 << 23) - 1, f_unique); + EXPECT_EQ(total, f_unique); + EXPECT_EQ(0, f_collisions); + + // Expect at least 2^23 unique values for the range [1, 2) + EXPECT_LE(1 << 23, g_unique); + EXPECT_EQ(total - g_unique, g_collisions); + } +} + +TEST(DistributionImplTest, MultiplyU64ToU128Test) { + using absl::random_internal::MultiplyU64ToU128; + constexpr uint64_t k1 = 1; + constexpr uint64_t kMax = ~static_cast<uint64_t>(0); + + EXPECT_EQ(absl::uint128(0), MultiplyU64ToU128(0, 0)); + + // Max uint64 + EXPECT_EQ(MultiplyU64ToU128(kMax, kMax), + absl::MakeUint128(0xfffffffffffffffe, 0x0000000000000001)); + EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(kMax, 1)); + EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(1, kMax)); + for (int i = 0; i < 64; ++i) { + EXPECT_EQ(absl::MakeUint128(0, kMax) << i, + MultiplyU64ToU128(kMax, k1 << i)); + EXPECT_EQ(absl::MakeUint128(0, kMax) << i, + MultiplyU64ToU128(k1 << i, kMax)); + } + + // 1-bit x 1-bit. + for (int i = 0; i < 64; ++i) { + for (int j = 0; j < 64; ++j) { + EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j), + MultiplyU64ToU128(k1 << i, k1 << j)); + EXPECT_EQ(absl::MakeUint128(0, 1) << (i + j), + MultiplyU64ToU128(k1 << i, k1 << j)); + } + } + + // Verified multiplies + EXPECT_EQ(MultiplyU64ToU128(0xffffeeeeddddcccc, 0xbbbbaaaa99998888), + absl::MakeUint128(0xbbbb9e2692c5dddc, 0xc28f7531048d2c60)); + EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfedcba9876543210), + absl::MakeUint128(0x0121fa00ad77d742, 0x2236d88fe5618cf0)); + EXPECT_EQ(MultiplyU64ToU128(0x0123456789abcdef, 0xfdb97531eca86420), + absl::MakeUint128(0x0120ae99d26725fc, 0xce197f0ecac319e0)); + EXPECT_EQ(MultiplyU64ToU128(0x97a87f4f261ba3f2, 0xfedcba9876543210), + absl::MakeUint128(0x96fbf1a8ae78d0ba, 0x5a6dd4b71f278320)); + EXPECT_EQ(MultiplyU64ToU128(0xfedcba9876543210, 0xfdb97531eca86420), + absl::MakeUint128(0xfc98c6981a413e22, 0x342d0bbf48948200)); +} + +} // namespace diff --git a/absl/random/internal/distribution_test_util.cc b/absl/random/internal/distribution_test_util.cc new file mode 100644 index 00000000..4fb4149d --- /dev/null +++ b/absl/random/internal/distribution_test_util.cc @@ -0,0 +1,418 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/distribution_test_util.h" + +#include <cassert> +#include <cmath> +#include <string> +#include <vector> + +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { +namespace { + +#if defined(__EMSCRIPTEN__) +// Workaround __EMSCRIPTEN__ error: llvm_fma_f64 not found. +inline double fma(double x, double y, double z) { return (x * y) + z; } +#endif + +} // namespace + +DistributionMoments ComputeDistributionMoments( + absl::Span<const double> data_points) { + DistributionMoments result; + + // Compute m1 + for (double x : data_points) { + result.n++; + result.mean += x; + } + result.mean /= static_cast<double>(result.n); + + // Compute m2, m3, m4 + for (double x : data_points) { + double v = x - result.mean; + result.variance += v * v; + result.skewness += v * v * v; + result.kurtosis += v * v * v * v; + } + result.variance /= static_cast<double>(result.n - 1); + + result.skewness /= static_cast<double>(result.n); + result.skewness /= std::pow(result.variance, 1.5); + + result.kurtosis /= static_cast<double>(result.n); + result.kurtosis /= std::pow(result.variance, 2.0); + return result; + + // When validating the min/max count, the following confidence intervals may + // be of use: + // 3.291 * stddev = 99.9% CI + // 2.576 * stddev = 99% CI + // 1.96 * stddev = 95% CI + // 1.65 * stddev = 90% CI +} + +std::ostream& operator<<(std::ostream& os, const DistributionMoments& moments) { + return os << absl::StrFormat("mean=%f, stddev=%f, skewness=%f, kurtosis=%f", + moments.mean, std::sqrt(moments.variance), + moments.skewness, moments.kurtosis); +} + +double InverseNormalSurvival(double x) { + // inv_sf(u) = -sqrt(2) * erfinv(2u-1) + static constexpr double kSqrt2 = 1.4142135623730950488; + return -kSqrt2 * absl::random_internal::erfinv(2 * x - 1.0); +} + +bool Near(absl::string_view msg, double actual, double expected, double bound) { + assert(bound > 0.0); + double delta = fabs(expected - actual); + if (delta < bound) { + return true; + } + + std::string formatted = absl::StrCat( + msg, " actual=", actual, " expected=", expected, " err=", delta / bound); + ABSL_RAW_LOG(INFO, "%s", formatted.c_str()); + return false; +} + +// TODO(absl-team): Replace with an "ABSL_HAVE_SPECIAL_MATH" and try +// to use std::beta(). As of this writing P0226R1 is not implemented +// in libc++: http://libcxx.llvm.org/cxx1z_status.html +double beta(double p, double q) { + // Beta(x, y) = Gamma(x) * Gamma(y) / Gamma(x+y) + double lbeta = std::lgamma(p) + std::lgamma(q) - std::lgamma(p + q); + return std::exp(lbeta); +} + +// Approximation to inverse of the Error Function in double precision. +// (http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf) +double erfinv(double x) { +#if !defined(__EMSCRIPTEN__) + using std::fma; +#endif + + double w = 0.0; + double p = 0.0; + w = -std::log((1.0 - x) * (1.0 + x)); + if (w < 6.250000) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = fma(p, w, -1.685059138182016589e-19); + p = fma(p, w, 1.2858480715256400167e-18); + p = fma(p, w, 1.115787767802518096e-17); + p = fma(p, w, -1.333171662854620906e-16); + p = fma(p, w, 2.0972767875968561637e-17); + p = fma(p, w, 6.6376381343583238325e-15); + p = fma(p, w, -4.0545662729752068639e-14); + p = fma(p, w, -8.1519341976054721522e-14); + p = fma(p, w, 2.6335093153082322977e-12); + p = fma(p, w, -1.2975133253453532498e-11); + p = fma(p, w, -5.4154120542946279317e-11); + p = fma(p, w, 1.051212273321532285e-09); + p = fma(p, w, -4.1126339803469836976e-09); + p = fma(p, w, -2.9070369957882005086e-08); + p = fma(p, w, 4.2347877827932403518e-07); + p = fma(p, w, -1.3654692000834678645e-06); + p = fma(p, w, -1.3882523362786468719e-05); + p = fma(p, w, 0.0001867342080340571352); + p = fma(p, w, -0.00074070253416626697512); + p = fma(p, w, -0.0060336708714301490533); + p = fma(p, w, 0.24015818242558961693); + p = fma(p, w, 1.6536545626831027356); + } else if (w < 16.000000) { + w = std::sqrt(w) - 3.250000; + p = 2.2137376921775787049e-09; + p = fma(p, w, 9.0756561938885390979e-08); + p = fma(p, w, -2.7517406297064545428e-07); + p = fma(p, w, 1.8239629214389227755e-08); + p = fma(p, w, 1.5027403968909827627e-06); + p = fma(p, w, -4.013867526981545969e-06); + p = fma(p, w, 2.9234449089955446044e-06); + p = fma(p, w, 1.2475304481671778723e-05); + p = fma(p, w, -4.7318229009055733981e-05); + p = fma(p, w, 6.8284851459573175448e-05); + p = fma(p, w, 2.4031110387097893999e-05); + p = fma(p, w, -0.0003550375203628474796); + p = fma(p, w, 0.00095328937973738049703); + p = fma(p, w, -0.0016882755560235047313); + p = fma(p, w, 0.0024914420961078508066); + p = fma(p, w, -0.0037512085075692412107); + p = fma(p, w, 0.005370914553590063617); + p = fma(p, w, 1.0052589676941592334); + p = fma(p, w, 3.0838856104922207635); + } else { + w = std::sqrt(w) - 5.000000; + p = -2.7109920616438573243e-11; + p = fma(p, w, -2.5556418169965252055e-10); + p = fma(p, w, 1.5076572693500548083e-09); + p = fma(p, w, -3.7894654401267369937e-09); + p = fma(p, w, 7.6157012080783393804e-09); + p = fma(p, w, -1.4960026627149240478e-08); + p = fma(p, w, 2.9147953450901080826e-08); + p = fma(p, w, -6.7711997758452339498e-08); + p = fma(p, w, 2.2900482228026654717e-07); + p = fma(p, w, -9.9298272942317002539e-07); + p = fma(p, w, 4.5260625972231537039e-06); + p = fma(p, w, -1.9681778105531670567e-05); + p = fma(p, w, 7.5995277030017761139e-05); + p = fma(p, w, -0.00021503011930044477347); + p = fma(p, w, -0.00013871931833623122026); + p = fma(p, w, 1.0103004648645343977); + p = fma(p, w, 4.8499064014085844221); + } + return p * x; +} + +namespace { + +// Direct implementation of AS63, BETAIN() +// https://www.jstor.org/stable/2346797?seq=3#page_scan_tab_contents. +// +// BETAIN(x, p, q, beta) +// x: the value of the upper limit x. +// p: the value of the parameter p. +// q: the value of the parameter q. +// beta: the value of ln B(p, q) +// +double BetaIncompleteImpl(const double x, const double p, const double q, + const double beta) { + if (p < (p + q) * x) { + // Incomplete beta function is symmetrical, so return the complement. + return 1. - BetaIncompleteImpl(1.0 - x, q, p, beta); + } + + double psq = p + q; + const double kErr = 1e-14; + const double xc = 1. - x; + const double pre = + std::exp(p * std::log(x) + (q - 1.) * std::log(xc) - beta) / p; + + double term = 1.; + double ai = 1.; + double result = 1.; + int ns = static_cast<int>(q + xc * psq); + + // Use the soper reduction forumla. + double rx = (ns == 0) ? x : x / xc; + double temp = q - ai; + for (;;) { + term = term * temp * rx / (p + ai); + result = result + term; + temp = std::fabs(term); + if (temp < kErr && temp < kErr * result) { + return result * pre; + } + ai = ai + 1.; + --ns; + if (ns >= 0) { + temp = q - ai; + if (ns == 0) { + rx = x; + } + } else { + temp = psq; + psq = psq + 1.; + } + } + + // NOTE: See also TOMS Alogrithm 708. + // http://www.netlib.org/toms/index.html + // + // NOTE: The NWSC library also includes BRATIO / ISUBX (p87) + // https://archive.org/details/DTIC_ADA261511/page/n75 +} + +// Direct implementation of AS109, XINBTA(p, q, beta, alpha) +// https://www.jstor.org/stable/2346798?read-now=1&seq=4#page_scan_tab_contents +// https://www.jstor.org/stable/2346887?seq=1#page_scan_tab_contents +// +// XINBTA(p, q, beta, alhpa) +// p: the value of the parameter p. +// q: the value of the parameter q. +// beta: the value of ln B(p, q) +// alpha: the value of the lower tail area. +// +double BetaIncompleteInvImpl(const double p, const double q, const double beta, + const double alpha) { + if (alpha < 0.5) { + // Inverse Incomplete beta function is symmetrical, return the complement. + return 1. - BetaIncompleteInvImpl(q, p, beta, 1. - alpha); + } + const double kErr = 1e-14; + double value = kErr; + + // Compute the initial estimate. + { + double r = std::sqrt(-std::log(alpha * alpha)); + double y = + r - fma(r, 0.27061, 2.30753) / fma(r, fma(r, 0.04481, 0.99229), 1.0); + if (p > 1. && q > 1.) { + r = (y * y - 3.) / 6.; + double s = 1. / (p + p - 1.); + double t = 1. / (q + q - 1.); + double h = 2. / s + t; + double w = + y * std::sqrt(h + r) / h - (t - s) * (r + 5. / 6. - t / (3. * h)); + value = p / (p + q * std::exp(w + w)); + } else { + r = q + q; + double t = 1.0 / (9. * q); + double u = 1.0 - t + y * std::sqrt(t); + t = r * (u * u * u); + if (t <= 0) { + value = 1.0 - std::exp((std::log((1.0 - alpha) * q) + beta) / q); + } else { + t = (4.0 * p + r - 2.0) / t; + if (t <= 1) { + value = std::exp((std::log(alpha * p) + beta) / p); + } else { + value = 1.0 - 2.0 / (t + 1.0); + } + } + } + } + + // Solve for x using a modified newton-raphson method using the function + // BetaIncomplete. + { + value = std::max(value, kErr); + value = std::min(value, 1.0 - kErr); + + const double r = 1.0 - p; + const double t = 1.0 - q; + double y; + double yprev = 0; + double sq = 1; + double prev = 1; + for (;;) { + if (value < 0 || value > 1.0) { + // Error case; value went infinite. + return std::numeric_limits<double>::infinity(); + } else if (value == 0 || value == 1) { + y = value; + } else { + y = BetaIncompleteImpl(value, p, q, beta); + if (!std::isfinite(y)) { + return y; + } + } + y = (y - alpha) * + std::exp(beta + r * std::log(value) + t * std::log(1.0 - value)); + if (y * yprev <= 0) { + prev = std::max(sq, std::numeric_limits<double>::min()); + } + double g = 1.0; + for (;;) { + const double adj = g * y; + const double adj_sq = adj * adj; + if (adj_sq >= prev) { + g = g / 3.0; + continue; + } + const double tx = value - adj; + if (tx < 0 || tx > 1) { + g = g / 3.0; + continue; + } + if (prev < kErr) { + return value; + } + if (y * y < kErr) { + return value; + } + if (tx == value) { + return value; + } + if (tx == 0 || tx == 1) { + g = g / 3.0; + continue; + } + value = tx; + yprev = y; + break; + } + } + } + + // NOTES: See also: Asymptotic inversion of the incomplete beta function. + // https://core.ac.uk/download/pdf/82140723.pdf + // + // NOTE: See the Boost library documentation as well: + // https://www.boost.org/doc/libs/1_52_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_beta/ibeta_function.html +} + +} // namespace + +double BetaIncomplete(const double x, const double p, const double q) { + // Error cases. + if (p < 0 || q < 0 || x < 0 || x > 1.0) { + return std::numeric_limits<double>::infinity(); + } + if (x == 0 || x == 1) { + return x; + } + // ln(Beta(p, q)) + double beta = std::lgamma(p) + std::lgamma(q) - std::lgamma(p + q); + return BetaIncompleteImpl(x, p, q, beta); +} + +double BetaIncompleteInv(const double p, const double q, const double alpha) { + // Error cases. + if (p < 0 || q < 0 || alpha < 0 || alpha > 1.0) { + return std::numeric_limits<double>::infinity(); + } + if (alpha == 0 || alpha == 1) { + return alpha; + } + // ln(Beta(p, q)) + double beta = std::lgamma(p) + std::lgamma(q) - std::lgamma(p + q); + return BetaIncompleteInvImpl(p, q, beta, alpha); +} + +// Given `num_trials` trials each with probability `p` of success, the +// probability of no failures is `p^k`. To ensure the probability of a failure +// is no more than `p_fail`, it must be that `p^k == 1 - p_fail`. This function +// computes `p` from that equation. +double RequiredSuccessProbability(const double p_fail, const int num_trials) { + double p = std::exp(std::log(1.0 - p_fail) / static_cast<double>(num_trials)); + ABSL_ASSERT(p > 0); + return p; +} + +double ZScore(double expected_mean, const DistributionMoments& moments) { + return (moments.mean - expected_mean) / + (std::sqrt(moments.variance) / + std::sqrt(static_cast<double>(moments.n))); +} + +double MaxErrorTolerance(double acceptance_probability) { + double one_sided_pvalue = 0.5 * (1.0 - acceptance_probability); + const double max_err = InverseNormalSurvival(one_sided_pvalue); + ABSL_ASSERT(max_err > 0); + return max_err; +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/internal/distribution_test_util.h b/absl/random/internal/distribution_test_util.h new file mode 100644 index 00000000..56dc86ac --- /dev/null +++ b/absl/random/internal/distribution_test_util.h @@ -0,0 +1,113 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_ +#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_ + +#include <cstddef> +#include <iostream> +#include <vector> + +#include "absl/strings/string_view.h" +#include "absl/types/span.h" + +// NOTE: The functions in this file are test only, and are should not be used in +// non-test code. + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// http://webspace.ship.edu/pgmarr/Geo441/Lectures/Lec%205%20-%20Normality%20Testing.pdf + +// Compute the 1st to 4th standard moments: +// mean, variance, skewness, and kurtosis. +// http://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm +struct DistributionMoments { + size_t n = 0; + double mean = 0.0; + double variance = 0.0; + double skewness = 0.0; + double kurtosis = 0.0; +}; +DistributionMoments ComputeDistributionMoments( + absl::Span<const double> data_points); + +std::ostream& operator<<(std::ostream& os, const DistributionMoments& moments); + +// Computes the Z-score for a set of data with the given distribution moments +// compared against `expected_mean`. +double ZScore(double expected_mean, const DistributionMoments& moments); + +// Returns the probability of success required for a single trial to ensure that +// after `num_trials` trials, the probability of at least one failure is no more +// than `p_fail`. +double RequiredSuccessProbability(double p_fail, int num_trials); + +// Computes the maximum distance from the mean tolerable, for Z-Tests that are +// expected to pass with `acceptance_probability`. Will terminate if the +// resulting tolerance is zero (due to passing in 0.0 for +// `acceptance_probability` or rounding errors). +// +// For example, +// MaxErrorTolerance(0.001) = 0.0 +// MaxErrorTolerance(0.5) = ~0.47 +// MaxErrorTolerance(1.0) = inf +double MaxErrorTolerance(double acceptance_probability); + +// Approximation to inverse of the Error Function in double precision. +// (http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf) +double erfinv(double x); + +// Beta(p, q) = Gamma(p) * Gamma(q) / Gamma(p+q) +double beta(double p, double q); + +// The inverse of the normal survival function. +double InverseNormalSurvival(double x); + +// Returns whether actual is "near" expected, based on the bound. +bool Near(absl::string_view msg, double actual, double expected, double bound); + +// Implements the incomplete regularized beta function, AS63, BETAIN. +// https://www.jstor.org/stable/2346797 +// +// BetaIncomplete(x, p, q), where +// `x` is the value of the upper limit +// `p` is beta parameter p, `q` is beta parameter q. +// +// NOTE: This is a test-only function which is only accurate to within, at most, +// 1e-13 of the actual value. +// +double BetaIncomplete(double x, double p, double q); + +// Implements the inverse of the incomplete regularized beta function, AS109, +// XINBTA. +// https://www.jstor.org/stable/2346798 +// https://www.jstor.org/stable/2346887 +// +// BetaIncompleteInv(p, q, beta, alhpa) +// `p` is beta parameter p, `q` is beta parameter q. +// `alpha` is the value of the lower tail area. +// +// NOTE: This is a test-only function and, when successful, is only accurate to +// within ~1e-6 of the actual value; there are some cases where it diverges from +// the actual value by much more than that. The function uses Newton's method, +// and thus the runtime is highly variable. +double BetaIncompleteInv(double p, double q, double alpha); + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_ diff --git a/absl/random/internal/distribution_test_util_test.cc b/absl/random/internal/distribution_test_util_test.cc new file mode 100644 index 00000000..c49d44fb --- /dev/null +++ b/absl/random/internal/distribution_test_util_test.cc @@ -0,0 +1,193 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/distribution_test_util.h" + +#include "gtest/gtest.h" + +namespace { + +TEST(TestUtil, InverseErf) { + const struct { + const double z; + const double value; + } kErfInvTable[] = { + {0.0000001, 8.86227e-8}, + {0.00001, 8.86227e-6}, + {0.5, 0.4769362762044}, + {0.6, 0.5951160814499}, + {0.99999, 3.1234132743}, + {0.9999999, 3.7665625816}, + {0.999999944, 3.8403850690566985}, // = log((1-x) * (1+x)) =~ 16.004 + {0.999999999, 4.3200053849134452}, + }; + + for (const auto& data : kErfInvTable) { + auto value = absl::random_internal::erfinv(data.z); + + // Log using the Wolfram-alpha function name & parameters. + EXPECT_NEAR(value, data.value, 1e-8) + << " InverseErf[" << data.z << "] (expected=" << data.value << ") -> " + << value; + } +} + +const struct { + const double p; + const double q; + const double x; + const double alpha; +} kBetaTable[] = { + {0.5, 0.5, 0.01, 0.06376856085851985}, + {0.5, 0.5, 0.1, 0.2048327646991335}, + {0.5, 0.5, 1, 1}, + {1, 0.5, 0, 0}, + {1, 0.5, 0.01, 0.005012562893380045}, + {1, 0.5, 0.1, 0.0513167019494862}, + {1, 0.5, 0.5, 0.2928932188134525}, + {1, 1, 0.5, 0.5}, + {2, 2, 0.1, 0.028}, + {2, 2, 0.2, 0.104}, + {2, 2, 0.3, 0.216}, + {2, 2, 0.4, 0.352}, + {2, 2, 0.5, 0.5}, + {2, 2, 0.6, 0.648}, + {2, 2, 0.7, 0.784}, + {2, 2, 0.8, 0.896}, + {2, 2, 0.9, 0.972}, + {5.5, 5, 0.5, 0.4361908850559777}, + {10, 0.5, 0.9, 0.1516409096346979}, + {10, 5, 0.5, 0.08978271484375}, + {10, 5, 1, 1}, + {10, 10, 0.5, 0.5}, + {20, 5, 0.8, 0.4598773297575791}, + {20, 10, 0.6, 0.2146816102371739}, + {20, 10, 0.8, 0.9507364826957875}, + {20, 20, 0.5, 0.5}, + {20, 20, 0.6, 0.8979413687105918}, + {30, 10, 0.7, 0.2241297491808366}, + {30, 10, 0.8, 0.7586405487192086}, + {40, 20, 0.7, 0.7001783247477069}, + {1, 0.5, 0.1, 0.0513167019494862}, + {1, 0.5, 0.2, 0.1055728090000841}, + {1, 0.5, 0.3, 0.1633399734659245}, + {1, 0.5, 0.4, 0.2254033307585166}, + {1, 2, 0.2, 0.36}, + {1, 3, 0.2, 0.488}, + {1, 4, 0.2, 0.5904}, + {1, 5, 0.2, 0.67232}, + {2, 2, 0.3, 0.216}, + {3, 2, 0.3, 0.0837}, + {4, 2, 0.3, 0.03078}, + {5, 2, 0.3, 0.010935}, + + // These values test small & large points along the range of the Beta + // function. + // + // When selecting test points, remember that if BetaIncomplete(x, p, q) + // returns the same value to within the limits of precision over a large + // domain of the input, x, then BetaIncompleteInv(alpha, p, q) may return an + // essentially arbitrary value where BetaIncomplete(x, p, q) =~ alpha. + + // BetaRegularized[x, 0.00001, 0.00001], + // For x in {~0.001 ... ~0.999}, => ~0.5 + {1e-5, 1e-5, 1e-5, 0.4999424388184638311}, + {1e-5, 1e-5, (1.0 - 1e-8), 0.5000920948389232964}, + + // BetaRegularized[x, 0.00001, 10000]. + // For x in {~epsilon ... 1.0}, => ~1 + {1e-5, 1e5, 1e-6, 0.9999817708130066936}, + {1e-5, 1e5, (1.0 - 1e-7), 1.0}, + + // BetaRegularized[x, 10000, 0.00001]. + // For x in {0 .. 1-epsilon}, => ~0 + {1e5, 1e-5, 1e-6, 0}, + {1e5, 1e-5, (1.0 - 1e-6), 1.8229186993306369e-5}, +}; + +TEST(BetaTest, BetaIncomplete) { + for (const auto& data : kBetaTable) { + auto value = absl::random_internal::BetaIncomplete(data.x, data.p, data.q); + + // Log using the Wolfram-alpha function name & parameters. + EXPECT_NEAR(value, data.alpha, 1e-12) + << " BetaRegularized[" << data.x << ", " << data.p << ", " << data.q + << "] (expected=" << data.alpha << ") -> " << value; + } +} + +TEST(BetaTest, BetaIncompleteInv) { + for (const auto& data : kBetaTable) { + auto value = + absl::random_internal::BetaIncompleteInv(data.p, data.q, data.alpha); + + // Log using the Wolfram-alpha function name & parameters. + EXPECT_NEAR(value, data.x, 1e-6) + << " InverseBetaRegularized[" << data.alpha << ", " << data.p << ", " + << data.q << "] (expected=" << data.x << ") -> " << value; + } +} + +TEST(MaxErrorTolerance, MaxErrorTolerance) { + std::vector<std::pair<double, double>> cases = { + {0.0000001, 8.86227e-8 * 1.41421356237}, + {0.00001, 8.86227e-6 * 1.41421356237}, + {0.5, 0.4769362762044 * 1.41421356237}, + {0.6, 0.5951160814499 * 1.41421356237}, + {0.99999, 3.1234132743 * 1.41421356237}, + {0.9999999, 3.7665625816 * 1.41421356237}, + {0.999999944, 3.8403850690566985 * 1.41421356237}, + {0.999999999, 4.3200053849134452 * 1.41421356237}}; + for (auto entry : cases) { + EXPECT_NEAR(absl::random_internal::MaxErrorTolerance(entry.first), + entry.second, 1e-8); + } +} + +TEST(ZScore, WithSameMean) { + absl::random_internal::DistributionMoments m; + m.n = 100; + m.mean = 5; + m.variance = 1; + EXPECT_NEAR(absl::random_internal::ZScore(5, m), 0, 1e-12); + + m.n = 1; + m.mean = 0; + m.variance = 1; + EXPECT_NEAR(absl::random_internal::ZScore(0, m), 0, 1e-12); + + m.n = 10000; + m.mean = -5; + m.variance = 100; + EXPECT_NEAR(absl::random_internal::ZScore(-5, m), 0, 1e-12); +} + +TEST(ZScore, DifferentMean) { + absl::random_internal::DistributionMoments m; + m.n = 100; + m.mean = 5; + m.variance = 1; + EXPECT_NEAR(absl::random_internal::ZScore(4, m), 10, 1e-12); + + m.n = 1; + m.mean = 0; + m.variance = 1; + EXPECT_NEAR(absl::random_internal::ZScore(-1, m), 1, 1e-12); + + m.n = 10000; + m.mean = -5; + m.variance = 100; + EXPECT_NEAR(absl::random_internal::ZScore(-4, m), -10, 1e-12); +} +} // namespace diff --git a/absl/random/internal/distributions.h b/absl/random/internal/distributions.h new file mode 100644 index 00000000..98d4f313 --- /dev/null +++ b/absl/random/internal/distributions.h @@ -0,0 +1,84 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_ +#define ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_ + +#include <type_traits> + +#include "absl/meta/type_traits.h" +#include "absl/random/internal/distribution_caller.h" +#include "absl/random/internal/traits.h" +#include "absl/random/internal/uniform_helper.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { +template <typename D> +struct DistributionFormatTraits; + +// UniformImpl implements the core logic of the Uniform<T> call, which is to +// select the correct distribution type, compute the bounds based on the +// interval tag, and then generate a value. +template <typename NumType, typename TagType, typename URBG> +NumType UniformImpl(TagType tag, + URBG& urbg, // NOLINT(runtime/references) + NumType lo, NumType hi) { + static_assert( + std::is_arithmetic<NumType>::value, + "absl::Uniform<T>() must use an integer or real parameter type."); + + using distribution_t = + typename std::conditional<std::is_integral<NumType>::value, + absl::uniform_int_distribution<NumType>, + absl::uniform_real_distribution<NumType>>::type; + using format_t = random_internal::DistributionFormatTraits<distribution_t>; + + auto a = random_internal::uniform_lower_bound<NumType>(tag, lo, hi); + auto b = random_internal::uniform_upper_bound<NumType>(tag, lo, hi); + // TODO(lar): it doesn't make a lot of sense to ask for a random number in an + // empty range. Right now we just return a boundary--even though that + // boundary is not an acceptable value! Is there something better we can do + // here? + + using gen_t = absl::decay_t<URBG>; + if (a > b) return a; + return DistributionCaller<gen_t>::template Call<distribution_t, format_t>( + &urbg, a, b); +} + +// In the absence of an explicitly provided return-type, the template +// "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on +// the data-types of the endpoint-arguments {A lo, B hi}. +// +// Given endpoints {A lo, B hi}, one of {A, B} will be chosen as the +// return-type, if one type can be implicitly converted into the other, in a +// lossless way. The template "is_widening_convertible" implements the +// compile-time logic for deciding if such a conversion is possible. +// +// If no such conversion between {A, B} exists, then the overload for +// absl::Uniform() will be discarded, and the call will be ill-formed. +// Return-type for absl::Uniform() when the return-type is inferred. +template <typename A, typename B> +using uniform_inferred_return_t = + absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>, + is_widening_convertible<B, A>>::value, + typename std::conditional< + is_widening_convertible<A, B>::value, B, A>::type>; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_ diff --git a/absl/random/internal/explicit_seed_seq.h b/absl/random/internal/explicit_seed_seq.h new file mode 100644 index 00000000..66c35f2e --- /dev/null +++ b/absl/random/internal/explicit_seed_seq.h @@ -0,0 +1,89 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_ +#define ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_ + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <initializer_list> +#include <iterator> +#include <vector> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// This class conforms to the C++ Standard "Seed Sequence" concept +// [rand.req.seedseq]. +// +// An "ExplicitSeedSeq" is meant to provide a conformant interface for +// forwarding pre-computed seed material to the constructor of a class +// conforming to the "Uniform Random Bit Generator" concept. This class makes no +// attempt to mutate the state provided by its constructor, and returns it +// directly via ExplicitSeedSeq::generate(). +// +// If this class is asked to generate more seed material than was provided to +// the constructor, then the remaining bytes will be filled with deterministic, +// nonrandom data. +class ExplicitSeedSeq { + public: + using result_type = uint32_t; + + ExplicitSeedSeq() : state_() {} + + // Copy and move both allowed. + ExplicitSeedSeq(const ExplicitSeedSeq& other) = default; + ExplicitSeedSeq& operator=(const ExplicitSeedSeq& other) = default; + ExplicitSeedSeq(ExplicitSeedSeq&& other) = default; + ExplicitSeedSeq& operator=(ExplicitSeedSeq&& other) = default; + + template <typename Iterator> + ExplicitSeedSeq(Iterator begin, Iterator end) { + for (auto it = begin; it != end; it++) { + state_.push_back(*it & 0xffffffff); + } + } + + template <typename T> + ExplicitSeedSeq(std::initializer_list<T> il) + : ExplicitSeedSeq(il.begin(), il.end()) {} + + size_t size() const { return state_.size(); } + + template <typename OutIterator> + void param(OutIterator out) const { + std::copy(std::begin(state_), std::end(state_), out); + } + + template <typename OutIterator> + void generate(OutIterator begin, OutIterator end) { + for (size_t index = 0; begin != end; begin++) { + *begin = state_.empty() ? 0 : state_[index++]; + if (index >= state_.size()) { + index = 0; + } + } + } + + protected: + std::vector<uint32_t> state_; +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_ diff --git a/absl/random/internal/explicit_seed_seq_test.cc b/absl/random/internal/explicit_seed_seq_test.cc new file mode 100644 index 00000000..a55ad739 --- /dev/null +++ b/absl/random/internal/explicit_seed_seq_test.cc @@ -0,0 +1,204 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/explicit_seed_seq.h" + +#include <iterator> +#include <random> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/seed_sequences.h" + +namespace { + +template <typename Sseq> +bool ConformsToInterface() { + // Check that the SeedSequence can be default-constructed. + { Sseq default_constructed_seq; } + // Check that the SeedSequence can be constructed with two iterators. + { + uint32_t init_array[] = {1, 3, 5, 7, 9}; + Sseq iterator_constructed_seq(init_array, &init_array[5]); + } + // Check that the SeedSequence can be std::initializer_list-constructed. + { Sseq list_constructed_seq = {1, 3, 5, 7, 9, 11, 13}; } + // Check that param() and size() return state provided to constructor. + { + uint32_t init_array[] = {1, 2, 3, 4, 5}; + Sseq seq(init_array, &init_array[ABSL_ARRAYSIZE(init_array)]); + EXPECT_EQ(seq.size(), ABSL_ARRAYSIZE(init_array)); + + uint32_t state_array[ABSL_ARRAYSIZE(init_array)]; + seq.param(state_array); + + for (int i = 0; i < ABSL_ARRAYSIZE(state_array); i++) { + EXPECT_EQ(state_array[i], i + 1); + } + } + // Check for presence of generate() method. + { + Sseq seq; + uint32_t seeds[5]; + + seq.generate(seeds, &seeds[ABSL_ARRAYSIZE(seeds)]); + } + return true; +} +} // namespace + +TEST(SeedSequences, CheckInterfaces) { + // Control case + EXPECT_TRUE(ConformsToInterface<std::seed_seq>()); + + // Abseil classes + EXPECT_TRUE(ConformsToInterface<absl::random_internal::ExplicitSeedSeq>()); +} + +TEST(ExplicitSeedSeq, DefaultConstructorGeneratesZeros) { + const size_t kNumBlocks = 128; + + uint32_t outputs[kNumBlocks]; + absl::random_internal::ExplicitSeedSeq seq; + seq.generate(outputs, &outputs[kNumBlocks]); + + for (uint32_t& seed : outputs) { + EXPECT_EQ(seed, 0); + } +} + +TEST(ExplicitSeeqSeq, SeedMaterialIsForwardedIdentically) { + const size_t kNumBlocks = 128; + + uint32_t seed_material[kNumBlocks]; + std::random_device urandom{"/dev/urandom"}; + for (uint32_t& seed : seed_material) { + seed = urandom(); + } + absl::random_internal::ExplicitSeedSeq seq(seed_material, + &seed_material[kNumBlocks]); + + // Check that output is same as seed-material provided to constructor. + { + const size_t kNumGenerated = kNumBlocks / 2; + uint32_t outputs[kNumGenerated]; + seq.generate(outputs, &outputs[kNumGenerated]); + for (size_t i = 0; i < kNumGenerated; i++) { + EXPECT_EQ(outputs[i], seed_material[i]); + } + } + // Check that SeedSequence is stateless between invocations: Despite the last + // invocation of generate() only consuming half of the input-entropy, the same + // entropy will be recycled for the next invocation. + { + const size_t kNumGenerated = kNumBlocks; + uint32_t outputs[kNumGenerated]; + seq.generate(outputs, &outputs[kNumGenerated]); + for (size_t i = 0; i < kNumGenerated; i++) { + EXPECT_EQ(outputs[i], seed_material[i]); + } + } + // Check that when more seed-material is asked for than is provided, nonzero + // values are still written. + { + const size_t kNumGenerated = kNumBlocks * 2; + uint32_t outputs[kNumGenerated]; + seq.generate(outputs, &outputs[kNumGenerated]); + for (size_t i = 0; i < kNumGenerated; i++) { + EXPECT_EQ(outputs[i], seed_material[i % kNumBlocks]); + } + } +} + +TEST(ExplicitSeedSeq, CopyAndMoveConstructors) { + using testing::Each; + using testing::Eq; + using testing::Not; + using testing::Pointwise; + + uint32_t entropy[4]; + std::random_device urandom("/dev/urandom"); + for (uint32_t& entry : entropy) { + entry = urandom(); + } + absl::random_internal::ExplicitSeedSeq seq_from_entropy(std::begin(entropy), + std::end(entropy)); + // Copy constructor. + { + absl::random_internal::ExplicitSeedSeq seq_copy(seq_from_entropy); + EXPECT_EQ(seq_copy.size(), seq_from_entropy.size()); + + std::vector<uint32_t> seeds_1; + seeds_1.resize(1000, 0); + std::vector<uint32_t> seeds_2; + seeds_2.resize(1000, 1); + + seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); + seq_copy.generate(seeds_2.begin(), seeds_2.end()); + + EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2)); + } + // Assignment operator. + { + for (uint32_t& entry : entropy) { + entry = urandom(); + } + absl::random_internal::ExplicitSeedSeq another_seq(std::begin(entropy), + std::end(entropy)); + + std::vector<uint32_t> seeds_1; + seeds_1.resize(1000, 0); + std::vector<uint32_t> seeds_2; + seeds_2.resize(1000, 0); + + seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); + another_seq.generate(seeds_2.begin(), seeds_2.end()); + + // Assert precondition: Sequences generated by seed-sequences are not equal. + EXPECT_THAT(seeds_1, Not(Pointwise(Eq(), seeds_2))); + + // Apply the assignment-operator. + another_seq = seq_from_entropy; + + // Re-generate seeds. + seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); + another_seq.generate(seeds_2.begin(), seeds_2.end()); + + // Seeds generated by seed-sequences should now be equal. + EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2)); + } + // Move constructor. + { + // Get seeds from seed-sequence constructed from entropy. + std::vector<uint32_t> seeds_1; + seeds_1.resize(1000, 0); + seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); + + // Apply move-constructor move the sequence to another instance. + absl::random_internal::ExplicitSeedSeq moved_seq( + std::move(seq_from_entropy)); + std::vector<uint32_t> seeds_2; + seeds_2.resize(1000, 1); + moved_seq.generate(seeds_2.begin(), seeds_2.end()); + // Verify that seeds produced by moved-instance are the same as original. + EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2)); + + // Verify that the moved-from instance now behaves like a + // default-constructed instance. + EXPECT_EQ(seq_from_entropy.size(), 0); + seq_from_entropy.generate(seeds_1.begin(), seeds_1.end()); + EXPECT_THAT(seeds_1, Each(Eq(0))); + } +} diff --git a/absl/random/internal/fast_uniform_bits.h b/absl/random/internal/fast_uniform_bits.h new file mode 100644 index 00000000..5aa9de01 --- /dev/null +++ b/absl/random/internal/fast_uniform_bits.h @@ -0,0 +1,262 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_ +#define ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_ + +#include <cstddef> +#include <cstdint> +#include <limits> +#include <type_traits> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { +// Returns true if the input value is zero or a power of two. Useful for +// determining if the range of output values in a URBG +template <typename UIntType> +constexpr bool IsPowerOfTwoOrZero(UIntType n) { + return (n == 0) || ((n & (n - 1)) == 0); +} + +// Computes the length of the range of values producible by the URBG, or returns +// zero if that would encompass the entire range of representable values in +// URBG::result_type. +template <typename URBG> +constexpr typename URBG::result_type RangeSize() { + using result_type = typename URBG::result_type; + return ((URBG::max)() == (std::numeric_limits<result_type>::max)() && + (URBG::min)() == std::numeric_limits<result_type>::lowest()) + ? result_type{0} + : (URBG::max)() - (URBG::min)() + result_type{1}; +} + +template <typename UIntType> +constexpr UIntType LargestPowerOfTwoLessThanOrEqualTo(UIntType n) { + return n < 2 ? n : 2 * LargestPowerOfTwoLessThanOrEqualTo(n / 2); +} + +// Given a URBG generating values in the closed interval [Lo, Hi], returns the +// largest power of two less than or equal to `Hi - Lo + 1`. +template <typename URBG> +constexpr typename URBG::result_type PowerOfTwoSubRangeSize() { + return LargestPowerOfTwoLessThanOrEqualTo(RangeSize<URBG>()); +} + +// Computes the floor of the log. (i.e., std::floor(std::log2(N)); +template <typename UIntType> +constexpr UIntType IntegerLog2(UIntType n) { + return (n <= 1) ? 0 : 1 + IntegerLog2(n / 2); +} + +// Returns the number of bits of randomness returned through +// `PowerOfTwoVariate(urbg)`. +template <typename URBG> +constexpr size_t NumBits() { + return RangeSize<URBG>() == 0 + ? std::numeric_limits<typename URBG::result_type>::digits + : IntegerLog2(PowerOfTwoSubRangeSize<URBG>()); +} + +// Given a shift value `n`, constructs a mask with exactly the low `n` bits set. +// If `n == 0`, all bits are set. +template <typename UIntType> +constexpr UIntType MaskFromShift(UIntType n) { + return ((n % std::numeric_limits<UIntType>::digits) == 0) + ? ~UIntType{0} + : (UIntType{1} << n) - UIntType{1}; +} + +// FastUniformBits implements a fast path to acquire uniform independent bits +// from a type which conforms to the [rand.req.urbg] concept. +// Parameterized by: +// `UIntType`: the result (output) type +// +// The std::independent_bits_engine [rand.adapt.ibits] adaptor can be +// instantiated from an existing generator through a copy or a move. It does +// not, however, facilitate the production of pseudorandom bits from an un-owned +// generator that will outlive the std::independent_bits_engine instance. +template <typename UIntType = uint64_t> +class FastUniformBits { + public: + using result_type = UIntType; + + static constexpr result_type(min)() { return 0; } + static constexpr result_type(max)() { + return (std::numeric_limits<result_type>::max)(); + } + + template <typename URBG> + result_type operator()(URBG& g); // NOLINT(runtime/references) + + private: + static_assert(std::is_unsigned<UIntType>::value, + "Class-template FastUniformBits<> must be parameterized using " + "an unsigned type."); + + // PowerOfTwoVariate() generates a single random variate, always returning a + // value in the half-open interval `[0, PowerOfTwoSubRangeSize<URBG>())`. If + // the URBG already generates values in a power-of-two range, the generator + // itself is used. Otherwise, we use rejection sampling on the largest + // possible power-of-two-sized subrange. + struct PowerOfTwoTag {}; + struct RejectionSamplingTag {}; + template <typename URBG> + static typename URBG::result_type PowerOfTwoVariate( + URBG& g) { // NOLINT(runtime/references) + using tag = + typename std::conditional<IsPowerOfTwoOrZero(RangeSize<URBG>()), + PowerOfTwoTag, RejectionSamplingTag>::type; + return PowerOfTwoVariate(g, tag{}); + } + + template <typename URBG> + static typename URBG::result_type PowerOfTwoVariate( + URBG& g, // NOLINT(runtime/references) + PowerOfTwoTag) { + return g() - (URBG::min)(); + } + + template <typename URBG> + static typename URBG::result_type PowerOfTwoVariate( + URBG& g, // NOLINT(runtime/references) + RejectionSamplingTag) { + // Use rejection sampling to ensure uniformity across the range. + typename URBG::result_type u; + do { + u = g() - (URBG::min)(); + } while (u >= PowerOfTwoSubRangeSize<URBG>()); + return u; + } + + // Generate() generates a random value, dispatched on whether + // the underlying URBG must loop over multiple calls or not. + template <typename URBG> + result_type Generate(URBG& g, // NOLINT(runtime/references) + std::true_type /* avoid_looping */); + + template <typename URBG> + result_type Generate(URBG& g, // NOLINT(runtime/references) + std::false_type /* avoid_looping */); +}; + +template <typename UIntType> +template <typename URBG> +typename FastUniformBits<UIntType>::result_type +FastUniformBits<UIntType>::operator()(URBG& g) { // NOLINT(runtime/references) + // kRangeMask is the mask used when sampling variates from the URBG when the + // width of the URBG range is not a power of 2. + // Y = (2 ^ kRange) - 1 + static_assert((URBG::max)() > (URBG::min)(), + "URBG::max and URBG::min may not be equal."); + using urbg_result_type = typename URBG::result_type; + constexpr urbg_result_type kRangeMask = + RangeSize<URBG>() == 0 + ? (std::numeric_limits<urbg_result_type>::max)() + : static_cast<urbg_result_type>(PowerOfTwoSubRangeSize<URBG>() - 1); + return Generate(g, std::integral_constant<bool, (kRangeMask >= (max)())>{}); +} + +template <typename UIntType> +template <typename URBG> +typename FastUniformBits<UIntType>::result_type +FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references) + std::true_type /* avoid_looping */) { + // The width of the result_type is less than than the width of the random bits + // provided by URBG. Thus, generate a single value and then simply mask off + // the required bits. + + return PowerOfTwoVariate(g) & (max)(); +} + +template <typename UIntType> +template <typename URBG> +typename FastUniformBits<UIntType>::result_type +FastUniformBits<UIntType>::Generate(URBG& g, // NOLINT(runtime/references) + std::false_type /* avoid_looping */) { + // See [rand.adapt.ibits] for more details on the constants calculated below. + // + // It is preferable to use roughly the same number of bits from each generator + // call, however this is only possible when the number of bits provided by the + // URBG is a divisor of the number of bits in `result_type`. In all other + // cases, the number of bits used cannot always be the same, but it can be + // guaranteed to be off by at most 1. Thus we run two loops, one with a + // smaller bit-width size (`kSmallWidth`) and one with a larger width size + // (satisfying `kLargeWidth == kSmallWidth + 1`). The loops are run + // `kSmallIters` and `kLargeIters` times respectively such + // that + // + // `kTotalWidth == kSmallIters * kSmallWidth + // + kLargeIters * kLargeWidth` + // + // where `kTotalWidth` is the total number of bits in `result_type`. + // + constexpr size_t kTotalWidth = std::numeric_limits<result_type>::digits; + constexpr size_t kUrbgWidth = NumBits<URBG>(); + constexpr size_t kTotalIters = + kTotalWidth / kUrbgWidth + (kTotalWidth % kUrbgWidth != 0); + constexpr size_t kSmallWidth = kTotalWidth / kTotalIters; + constexpr size_t kLargeWidth = kSmallWidth + 1; + // + // Because `kLargeWidth == kSmallWidth + 1`, it follows that + // + // `kTotalWidth == kTotalIters * kSmallWidth + kLargeIters` + // + // and therefore + // + // `kLargeIters == kTotalWidth % kSmallWidth` + // + // Intuitively, each iteration with the large width accounts for one unit + // of the remainder when `kTotalWidth` is divided by `kSmallWidth`. As + // mentioned above, if the URBG width is a divisor of `kTotalWidth`, then + // there would be no need for any large iterations (i.e., one loop would + // suffice), and indeed, in this case, `kLargeIters` would be zero. + constexpr size_t kLargeIters = kTotalWidth % kSmallWidth; + constexpr size_t kSmallIters = + (kTotalWidth - (kLargeWidth * kLargeIters)) / kSmallWidth; + + static_assert( + kTotalWidth == kSmallIters * kSmallWidth + kLargeIters * kLargeWidth, + "Error in looping constant calculations."); + + result_type s = 0; + + constexpr size_t kSmallShift = kSmallWidth % kTotalWidth; + constexpr result_type kSmallMask = MaskFromShift(result_type{kSmallShift}); + for (size_t n = 0; n < kSmallIters; ++n) { + s = (s << kSmallShift) + + (static_cast<result_type>(PowerOfTwoVariate(g)) & kSmallMask); + } + + constexpr size_t kLargeShift = kLargeWidth % kTotalWidth; + constexpr result_type kLargeMask = MaskFromShift(result_type{kLargeShift}); + for (size_t n = 0; n < kLargeIters; ++n) { + s = (s << kLargeShift) + + (static_cast<result_type>(PowerOfTwoVariate(g)) & kLargeMask); + } + + static_assert( + kLargeShift == kSmallShift + 1 || + (kLargeShift == 0 && + kSmallShift == std::numeric_limits<result_type>::digits - 1), + "Error in looping constant calculations"); + + return s; +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_FAST_UNIFORM_BITS_H_ diff --git a/absl/random/internal/fast_uniform_bits_test.cc b/absl/random/internal/fast_uniform_bits_test.cc new file mode 100644 index 00000000..35c96226 --- /dev/null +++ b/absl/random/internal/fast_uniform_bits_test.cc @@ -0,0 +1,274 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/fast_uniform_bits.h" + +#include <random> + +#include "gtest/gtest.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { +namespace { + +template <typename IntType> +class FastUniformBitsTypedTest : public ::testing::Test {}; + +using IntTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t>; + +TYPED_TEST_SUITE(FastUniformBitsTypedTest, IntTypes); + +TYPED_TEST(FastUniformBitsTypedTest, BasicTest) { + using Limits = std::numeric_limits<TypeParam>; + using FastBits = FastUniformBits<TypeParam>; + + EXPECT_EQ(0, FastBits::min()); + EXPECT_EQ(Limits::max(), FastBits::max()); + + constexpr int kIters = 10000; + std::random_device rd; + std::mt19937 gen(rd()); + FastBits fast; + for (int i = 0; i < kIters; i++) { + const auto v = fast(gen); + EXPECT_LE(v, FastBits::max()); + EXPECT_GE(v, FastBits::min()); + } +} + +template <typename UIntType, UIntType Lo, UIntType Hi, UIntType Val = Lo> +struct FakeUrbg { + using result_type = UIntType; + + static constexpr result_type(max)() { return Hi; } + static constexpr result_type(min)() { return Lo; } + result_type operator()() { return Val; } +}; + +using UrngOddbits = FakeUrbg<uint8_t, 1, 0xfe, 0x73>; +using Urng4bits = FakeUrbg<uint8_t, 1, 0x10, 2>; +using Urng31bits = FakeUrbg<uint32_t, 1, 0xfffffffe, 0x60070f03>; +using Urng32bits = FakeUrbg<uint32_t, 0, 0xffffffff, 0x74010f01>; + +TEST(FastUniformBitsTest, IsPowerOfTwoOrZero) { + EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{0})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{1})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{2})); + EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{3})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint8_t{16})); + EXPECT_FALSE(IsPowerOfTwoOrZero(uint8_t{17})); + EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint8_t>::max)())); + + EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{0})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{1})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{2})); + EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{3})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint16_t{16})); + EXPECT_FALSE(IsPowerOfTwoOrZero(uint16_t{17})); + EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint16_t>::max)())); + + EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{0})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{1})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{2})); + EXPECT_FALSE(IsPowerOfTwoOrZero(uint32_t{3})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint32_t{32})); + EXPECT_FALSE(IsPowerOfTwoOrZero(uint32_t{17})); + EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint32_t>::max)())); + + EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{0})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{1})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{2})); + EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{3})); + EXPECT_TRUE(IsPowerOfTwoOrZero(uint64_t{64})); + EXPECT_FALSE(IsPowerOfTwoOrZero(uint64_t{17})); + EXPECT_FALSE(IsPowerOfTwoOrZero((std::numeric_limits<uint64_t>::max)())); +} + +TEST(FastUniformBitsTest, IntegerLog2) { + EXPECT_EQ(IntegerLog2(uint16_t{0}), 0); + EXPECT_EQ(IntegerLog2(uint16_t{1}), 0); + EXPECT_EQ(IntegerLog2(uint16_t{2}), 1); + EXPECT_EQ(IntegerLog2(uint16_t{3}), 1); + EXPECT_EQ(IntegerLog2(uint16_t{4}), 2); + EXPECT_EQ(IntegerLog2(uint16_t{5}), 2); + EXPECT_EQ(IntegerLog2(std::numeric_limits<uint64_t>::max()), 63); +} + +TEST(FastUniformBitsTest, RangeSize) { + EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 0, 3>>()), 4); + EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 2>>()), 1); + EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 5>>()), 4); + EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 6>>()), 5); + EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 10>>()), 9); + EXPECT_EQ( + (RangeSize<FakeUrbg<uint8_t, 0, std::numeric_limits<uint8_t>::max()>>()), + 0); + + EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 0, 3>>()), 4); + EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 2>>()), 1); + EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 5>>()), 4); + EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 6>>()), 5); + EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 1000, 1017>>()), 18); + EXPECT_EQ((RangeSize< + FakeUrbg<uint16_t, 0, std::numeric_limits<uint16_t>::max()>>()), + 0); + + EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 0, 3>>()), 4); + EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 2>>()), 1); + EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 5>>()), 4); + EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 6>>()), 5); + EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1000, 1017>>()), 18); + EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()), 0); + EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()), 0xffffffff); + EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()), 0xfffffffe); + EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 0xfffffffe>>()), 0xfffffffd); + EXPECT_EQ((RangeSize< + FakeUrbg<uint32_t, 0, std::numeric_limits<uint32_t>::max()>>()), + 0); + + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 3>>()), 4); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 2>>()), 1); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 5>>()), 4); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 6>>()), 5); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1000, 1017>>()), 18); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()), 0x100000000ull); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()), 0xffffffffull); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()), 0xfffffffeull); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffe>>()), 0xfffffffdull); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffffull>>()), 0ull); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffffull>>()), + 0xffffffffffffffffull); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffeull>>()), + 0xfffffffffffffffeull); + EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffffffffffeull>>()), + 0xfffffffffffffffdull); + EXPECT_EQ((RangeSize< + FakeUrbg<uint64_t, 0, std::numeric_limits<uint64_t>::max()>>()), + 0); +} + +TEST(FastUniformBitsTest, PowerOfTwoSubRangeSize) { + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 0, 3>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 2>>()), 1); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 5>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 6>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 10>>()), 8); + EXPECT_EQ((PowerOfTwoSubRangeSize< + FakeUrbg<uint8_t, 0, std::numeric_limits<uint8_t>::max()>>()), + 0); + + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 0, 3>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 2>>()), 1); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 5>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 6>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 1000, 1017>>()), 16); + EXPECT_EQ((PowerOfTwoSubRangeSize< + FakeUrbg<uint16_t, 0, std::numeric_limits<uint16_t>::max()>>()), + 0); + + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 0, 3>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 2>>()), 1); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 5>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 6>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1000, 1017>>()), 16); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()), 0); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()), + 0x80000000); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()), + 0x80000000); + EXPECT_EQ((PowerOfTwoSubRangeSize< + FakeUrbg<uint32_t, 0, std::numeric_limits<uint32_t>::max()>>()), + 0); + + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 3>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 2>>()), 1); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 5>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 6>>()), 4); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1000, 1017>>()), 16); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()), + 0x100000000ull); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()), + 0x80000000ull); + EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()), + 0x80000000ull); + EXPECT_EQ( + (PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffffull>>()), + 0); + EXPECT_EQ( + (PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffffull>>()), + 0x8000000000000000ull); + EXPECT_EQ( + (PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffeull>>()), + 0x8000000000000000ull); + EXPECT_EQ((PowerOfTwoSubRangeSize< + FakeUrbg<uint64_t, 0, std::numeric_limits<uint64_t>::max()>>()), + 0); +} + +TEST(FastUniformBitsTest, Urng4_VariousOutputs) { + // Tests that how values are composed; the single-bit deltas should be spread + // across each invocation. + Urng4bits urng4; + Urng31bits urng31; + Urng32bits urng32; + + // 8-bit types + { + FastUniformBits<uint8_t> fast8; + EXPECT_EQ(0x11, fast8(urng4)); + EXPECT_EQ(0x2, fast8(urng31)); + EXPECT_EQ(0x1, fast8(urng32)); + } + + // 16-bit types + { + FastUniformBits<uint16_t> fast16; + EXPECT_EQ(0x1111, fast16(urng4)); + EXPECT_EQ(0xf02, fast16(urng31)); + EXPECT_EQ(0xf01, fast16(urng32)); + } + + // 32-bit types + { + FastUniformBits<uint32_t> fast32; + EXPECT_EQ(0x11111111, fast32(urng4)); + EXPECT_EQ(0x0f020f02, fast32(urng31)); + EXPECT_EQ(0x74010f01, fast32(urng32)); + } + + // 64-bit types + { + FastUniformBits<uint64_t> fast64; + EXPECT_EQ(0x1111111111111111, fast64(urng4)); + EXPECT_EQ(0x387811c3c0870f02, fast64(urng31)); + EXPECT_EQ(0x74010f0174010f01, fast64(urng32)); + } +} + +TEST(FastUniformBitsTest, URBG32bitRegression) { + // Validate with deterministic 32-bit std::minstd_rand + // to ensure that operator() performs as expected. + std::minstd_rand gen(1); + FastUniformBits<uint64_t> fast64; + + EXPECT_EQ(0x05e47095f847c122ull, fast64(gen)); + EXPECT_EQ(0x8f82c1ba30b64d22ull, fast64(gen)); + EXPECT_EQ(0x3b971a3558155039ull, fast64(gen)); +} + +} // namespace +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/internal/fastmath.h b/absl/random/internal/fastmath.h new file mode 100644 index 00000000..1dc2cd7b --- /dev/null +++ b/absl/random/internal/fastmath.h @@ -0,0 +1,74 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_FASTMATH_H_ +#define ABSL_RANDOM_INTERNAL_FASTMATH_H_ + +// This file contains fast math functions (bitwise ops as well as some others) +// which are implementation details of various absl random number distributions. + +#include <cassert> +#include <cmath> +#include <cstdint> + +#include "absl/base/internal/bits.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// Returns the position of the first bit set. +inline int LeadingSetBit(uint64_t n) { + return 64 - base_internal::CountLeadingZeros64(n); +} + +// Compute log2(n) using integer operations. +// While std::log2 is more accurate than std::log(n) / std::log(2), for +// very large numbers--those close to std::numeric_limits<uint64_t>::max() - 2, +// for instance--std::log2 rounds up rather than down, which introduces +// definite skew in the results. +inline int IntLog2Floor(uint64_t n) { + return (n <= 1) ? 0 : (63 - base_internal::CountLeadingZeros64(n)); +} +inline int IntLog2Ceil(uint64_t n) { + return (n <= 1) ? 0 : (64 - base_internal::CountLeadingZeros64(n - 1)); +} + +inline double StirlingLogFactorial(double n) { + assert(n >= 1); + // Using Stirling's approximation. + constexpr double kLog2PI = 1.83787706640934548356; + const double logn = std::log(n); + const double ninv = 1.0 / static_cast<double>(n); + return n * logn - n + 0.5 * (kLog2PI + logn) + (1.0 / 12.0) * ninv - + (1.0 / 360.0) * ninv * ninv * ninv; +} + +// Rotate value right. +// +// We only implement the uint32_t / uint64_t versions because +// 1) those are the only ones we use, and +// 2) those are the only ones where clang detects the rotate idiom correctly. +inline constexpr uint32_t rotr(uint32_t value, uint8_t bits) { + return (value >> (bits & 31)) | (value << ((-bits) & 31)); +} +inline constexpr uint64_t rotr(uint64_t value, uint8_t bits) { + return (value >> (bits & 63)) | (value << ((-bits) & 63)); +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_FASTMATH_H_ diff --git a/absl/random/internal/fastmath_test.cc b/absl/random/internal/fastmath_test.cc new file mode 100644 index 00000000..65859c25 --- /dev/null +++ b/absl/random/internal/fastmath_test.cc @@ -0,0 +1,110 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/fastmath.h" + +#include "gtest/gtest.h" + +#if defined(__native_client__) || defined(__EMSCRIPTEN__) +// NACL has a less accurate implementation of std::log2 than most of +// the other platforms. For some values which should have integral results, +// sometimes NACL returns slightly larger values. +// +// The MUSL libc used by emscripten also has a similar bug. +#define ABSL_RANDOM_INACCURATE_LOG2 +#endif + +namespace { + +TEST(DistributionImplTest, LeadingSetBit) { + using absl::random_internal::LeadingSetBit; + constexpr uint64_t kZero = 0; + EXPECT_EQ(0, LeadingSetBit(kZero)); + EXPECT_EQ(64, LeadingSetBit(~kZero)); + + for (int index = 0; index < 64; index++) { + uint64_t x = static_cast<uint64_t>(1) << index; + EXPECT_EQ(index + 1, LeadingSetBit(x)) << index; + EXPECT_EQ(index + 1, LeadingSetBit(x + x - 1)) << index; + } +} + +TEST(FastMathTest, IntLog2FloorTest) { + using absl::random_internal::IntLog2Floor; + constexpr uint64_t kZero = 0; + EXPECT_EQ(0, IntLog2Floor(0)); // boundary. return 0. + EXPECT_EQ(0, IntLog2Floor(1)); + EXPECT_EQ(1, IntLog2Floor(2)); + EXPECT_EQ(63, IntLog2Floor(~kZero)); + + // A boundary case: Converting 0xffffffffffffffff requires > 53 + // bits of precision, so the conversion to double rounds up, + // and the result of std::log2(x) > IntLog2Floor(x). + EXPECT_LT(IntLog2Floor(~kZero), static_cast<int>(std::log2(~kZero))); + + for (int i = 0; i < 64; i++) { + const uint64_t i_pow_2 = static_cast<uint64_t>(1) << i; + EXPECT_EQ(i, IntLog2Floor(i_pow_2)); + EXPECT_EQ(i, static_cast<int>(std::log2(i_pow_2))); + + uint64_t y = i_pow_2; + for (int j = i - 1; j > 0; --j) { + y = y | (i_pow_2 >> j); + EXPECT_EQ(i, IntLog2Floor(y)); + } + } +} + +TEST(FastMathTest, IntLog2CeilTest) { + using absl::random_internal::IntLog2Ceil; + constexpr uint64_t kZero = 0; + EXPECT_EQ(0, IntLog2Ceil(0)); // boundary. return 0. + EXPECT_EQ(0, IntLog2Ceil(1)); + EXPECT_EQ(1, IntLog2Ceil(2)); + EXPECT_EQ(64, IntLog2Ceil(~kZero)); + + // A boundary case: Converting 0xffffffffffffffff requires > 53 + // bits of precision, so the conversion to double rounds up, + // and the result of std::log2(x) > IntLog2Floor(x). + EXPECT_LE(IntLog2Ceil(~kZero), static_cast<int>(std::log2(~kZero))); + + for (int i = 0; i < 64; i++) { + const uint64_t i_pow_2 = static_cast<uint64_t>(1) << i; + EXPECT_EQ(i, IntLog2Ceil(i_pow_2)); +#ifndef ABSL_RANDOM_INACCURATE_LOG2 + EXPECT_EQ(i, static_cast<int>(std::ceil(std::log2(i_pow_2)))); +#endif + + uint64_t y = i_pow_2; + for (int j = i - 1; j > 0; --j) { + y = y | (i_pow_2 >> j); + EXPECT_EQ(i + 1, IntLog2Ceil(y)); + } + } +} + +TEST(FastMathTest, StirlingLogFactorial) { + using absl::random_internal::StirlingLogFactorial; + + EXPECT_NEAR(StirlingLogFactorial(1.0), 0, 1e-3); + EXPECT_NEAR(StirlingLogFactorial(1.50), 0.284683, 1e-3); + EXPECT_NEAR(StirlingLogFactorial(2.0), 0.69314718056, 1e-4); + + for (int i = 2; i < 50; i++) { + double d = static_cast<double>(i); + EXPECT_NEAR(StirlingLogFactorial(d), std::lgamma(d + 1), 3e-5); + } +} + +} // namespace diff --git a/absl/random/internal/gaussian_distribution_gentables.cc b/absl/random/internal/gaussian_distribution_gentables.cc new file mode 100644 index 00000000..6f9a28be --- /dev/null +++ b/absl/random/internal/gaussian_distribution_gentables.cc @@ -0,0 +1,147 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generates gaussian_distribution.cc +// +// $ blaze run :gaussian_distribution_gentables > gaussian_distribution.cc +// +#include "absl/random/gaussian_distribution.h" + +#include <cmath> +#include <cstddef> +#include <iostream> +#include <limits> +#include <string> + +#include "absl/base/macros.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { +namespace { + +template <typename T, size_t N> +void FormatArrayContents(std::ostream* os, T (&data)[N]) { + if (!std::numeric_limits<T>::is_exact) { + // Note: T is either an integer or a float. + // float requires higher precision to ensure that values are + // reproduced exactly. + // Trivia: C99 has hexadecimal floating point literals, but C++11 does not. + // Using them would remove all concern of precision loss. + os->precision(std::numeric_limits<T>::max_digits10 + 2); + } + *os << " {"; + std::string separator = ""; + for (size_t i = 0; i < N; ++i) { + *os << separator << data[i]; + if ((i + 1) % 3 != 0) { + separator = ", "; + } else { + separator = ",\n "; + } + } + *os << "}"; +} + +} // namespace + +class TableGenerator : public gaussian_distribution_base { + public: + TableGenerator(); + void Print(std::ostream* os); + + using gaussian_distribution_base::kMask; + using gaussian_distribution_base::kR; + using gaussian_distribution_base::kV; + + private: + Tables tables_; +}; + +// Ziggurat gaussian initialization. For an explanation of the algorithm, see +// the Marsaglia paper, "The Ziggurat Method for Generating Random Variables". +// http://www.jstatsoft.org/v05/i08/ +// +// Further details are available in the Doornik paper +// https://www.doornik.com/research/ziggurat.pdf +// +TableGenerator::TableGenerator() { + // The constants here should match the values in gaussian_distribution.h + static constexpr int kC = kMask + 1; + + static_assert((ABSL_ARRAYSIZE(tables_.x) == kC + 1), + "xArray must be length kMask + 2"); + + static_assert((ABSL_ARRAYSIZE(tables_.x) == ABSL_ARRAYSIZE(tables_.f)), + "fx and x arrays must be identical length"); + + auto f = [](double x) { return std::exp(-0.5 * x * x); }; + auto f_inv = [](double x) { return std::sqrt(-2.0 * std::log(x)); }; + + tables_.x[0] = kV / f(kR); + tables_.f[0] = f(tables_.x[0]); + + tables_.x[1] = kR; + tables_.f[1] = f(tables_.x[1]); + + tables_.x[kC] = 0.0; + tables_.f[kC] = f(tables_.x[kC]); // 1.0 + + for (int i = 2; i < kC; i++) { + double v = (kV / tables_.x[i - 1]) + tables_.f[i - 1]; + tables_.x[i] = f_inv(v); + tables_.f[i] = v; + } +} + +void TableGenerator::Print(std::ostream* os) { + *os << "// BEGIN GENERATED CODE; DO NOT EDIT\n" + "// clang-format off\n" + "\n" + "#include \"absl/random/gaussian_distribution.h\"\n" + "\n" + // "namespace " and "absl" are broken apart so as not to conflict with + // script that adds the LTS inline namespace. + "namespace " + "absl {\n" + "namespace " + "random_internal {\n" + "\n" + "const gaussian_distribution_base::Tables\n" + " gaussian_distribution_base::zg_ = {\n"; + FormatArrayContents(os, tables_.x); + *os << ",\n"; + FormatArrayContents(os, tables_.f); + *os << "};\n" + "\n" + "} // namespace " + "random_internal\n" + "} // namespace " + "absl\n" + "\n" + "// clang-format on\n" + "// END GENERATED CODE"; + *os << std::endl; +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +int main(int, char**) { + std::cerr << "\nCopy the output to gaussian_distribution.cc" << std::endl; + absl::random_internal::TableGenerator generator; + generator.Print(&std::cout); + return 0; +} diff --git a/absl/random/internal/iostream_state_saver.h b/absl/random/internal/iostream_state_saver.h new file mode 100644 index 00000000..b9adca86 --- /dev/null +++ b/absl/random/internal/iostream_state_saver.h @@ -0,0 +1,245 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_ +#define ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_ + +#include <cmath> +#include <iostream> +#include <limits> +#include <type_traits> + +#include "absl/meta/type_traits.h" +#include "absl/numeric/int128.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// The null_state_saver does nothing. +template <typename T> +class null_state_saver { + public: + using stream_type = T; + using flags_type = std::ios_base::fmtflags; + + null_state_saver(T&, flags_type) {} + ~null_state_saver() {} +}; + +// ostream_state_saver is a RAII object to save and restore the common +// basic_ostream flags used when implementing `operator <<()` on any of +// the absl random distributions. +template <typename OStream> +class ostream_state_saver { + public: + using ostream_type = OStream; + using flags_type = std::ios_base::fmtflags; + using fill_type = typename ostream_type::char_type; + using precision_type = std::streamsize; + + ostream_state_saver(ostream_type& os, // NOLINT(runtime/references) + flags_type flags, fill_type fill) + : os_(os), + flags_(os.flags(flags)), + fill_(os.fill(fill)), + precision_(os.precision()) { + // Save state in initialized variables. + } + + ~ostream_state_saver() { + // Restore saved state. + os_.precision(precision_); + os_.fill(fill_); + os_.flags(flags_); + } + + private: + ostream_type& os_; + const flags_type flags_; + const fill_type fill_; + const precision_type precision_; +}; + +#if defined(__NDK_MAJOR__) && __NDK_MAJOR__ < 16 +#define ABSL_RANDOM_INTERNAL_IOSTREAM_HEXFLOAT 1 +#else +#define ABSL_RANDOM_INTERNAL_IOSTREAM_HEXFLOAT 0 +#endif + +template <typename CharT, typename Traits> +ostream_state_saver<std::basic_ostream<CharT, Traits>> make_ostream_state_saver( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + std::ios_base::fmtflags flags = std::ios_base::dec | std::ios_base::left | +#if ABSL_RANDOM_INTERNAL_IOSTREAM_HEXFLOAT + std::ios_base::fixed | +#endif + std::ios_base::scientific) { + using result_type = ostream_state_saver<std::basic_ostream<CharT, Traits>>; + return result_type(os, flags, os.widen(' ')); +} + +template <typename T> +typename absl::enable_if_t<!std::is_base_of<std::ios_base, T>::value, + null_state_saver<T>> +make_ostream_state_saver(T& is, // NOLINT(runtime/references) + std::ios_base::fmtflags flags = std::ios_base::dec) { + std::cerr << "null_state_saver"; + using result_type = null_state_saver<T>; + return result_type(is, flags); +} + +// stream_precision_helper<type>::kPrecision returns the base 10 precision +// required to stream and reconstruct a real type exact binary value through +// a binary->decimal->binary transition. +template <typename T> +struct stream_precision_helper { + // max_digits10 may be 0 on MSVC; if so, use digits10 + 3. + static constexpr int kPrecision = + (std::numeric_limits<T>::max_digits10 > std::numeric_limits<T>::digits10) + ? std::numeric_limits<T>::max_digits10 + : (std::numeric_limits<T>::digits10 + 3); +}; + +template <> +struct stream_precision_helper<float> { + static constexpr int kPrecision = 9; +}; +template <> +struct stream_precision_helper<double> { + static constexpr int kPrecision = 17; +}; +template <> +struct stream_precision_helper<long double> { + static constexpr int kPrecision = 36; // assuming fp128 +}; + +// istream_state_saver is a RAII object to save and restore the common +// std::basic_istream<> flags used when implementing `operator >>()` on any of +// the absl random distributions. +template <typename IStream> +class istream_state_saver { + public: + using istream_type = IStream; + using flags_type = std::ios_base::fmtflags; + + istream_state_saver(istream_type& is, // NOLINT(runtime/references) + flags_type flags) + : is_(is), flags_(is.flags(flags)) {} + + ~istream_state_saver() { is_.flags(flags_); } + + private: + istream_type& is_; + flags_type flags_; +}; + +template <typename CharT, typename Traits> +istream_state_saver<std::basic_istream<CharT, Traits>> make_istream_state_saver( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + std::ios_base::fmtflags flags = std::ios_base::dec | + std::ios_base::scientific | + std::ios_base::skipws) { + using result_type = istream_state_saver<std::basic_istream<CharT, Traits>>; + return result_type(is, flags); +} + +template <typename T> +typename absl::enable_if_t<!std::is_base_of<std::ios_base, T>::value, + null_state_saver<T>> +make_istream_state_saver(T& is, // NOLINT(runtime/references) + std::ios_base::fmtflags flags = std::ios_base::dec) { + using result_type = null_state_saver<T>; + return result_type(is, flags); +} + +// stream_format_type<T> is a helper struct to convert types which +// basic_iostream cannot output as decimal numbers into types which +// basic_iostream can output as decimal numbers. Specifically: +// * signed/unsigned char-width types are converted to int. +// * TODO(lar): __int128 => uint128, except there is no operator << yet. +// +template <typename T> +struct stream_format_type + : public std::conditional<(sizeof(T) == sizeof(char)), int, T> {}; + +// stream_u128_helper allows us to write out either absl::uint128 or +// __uint128_t types in the same way, which enables their use as internal +// state of PRNG engines. +template <typename T> +struct stream_u128_helper; + +template <> +struct stream_u128_helper<absl::uint128> { + template <typename IStream> + inline absl::uint128 read(IStream& in) { + uint64_t h = 0; + uint64_t l = 0; + in >> h >> l; + return absl::MakeUint128(h, l); + } + + template <typename OStream> + inline void write(absl::uint128 val, OStream& out) { + uint64_t h = Uint128High64(val); + uint64_t l = Uint128Low64(val); + out << h << out.fill() << l; + } +}; + +#ifdef ABSL_HAVE_INTRINSIC_INT128 +template <> +struct stream_u128_helper<__uint128_t> { + template <typename IStream> + inline __uint128_t read(IStream& in) { + uint64_t h = 0; + uint64_t l = 0; + in >> h >> l; + return (static_cast<__uint128_t>(h) << 64) | l; + } + + template <typename OStream> + inline void write(__uint128_t val, OStream& out) { + uint64_t h = static_cast<uint64_t>(val >> 64u); + uint64_t l = static_cast<uint64_t>(val); + out << h << out.fill() << l; + } +}; +#endif + +template <typename FloatType, typename IStream> +inline FloatType read_floating_point(IStream& is) { + static_assert(std::is_floating_point<FloatType>::value, ""); + FloatType dest; + is >> dest; + // Parsing a double value may report a subnormal value as an error + // despite being able to represent it. + // See https://stackoverflow.com/q/52410931/3286653 + // It may also report an underflow when parsing DOUBLE_MIN as an + // ERANGE error, as the parsed value may be smaller than DOUBLE_MIN + // and rounded up. + // See: https://stackoverflow.com/q/42005462 + if (is.fail() && + (std::fabs(dest) == (std::numeric_limits<FloatType>::min)() || + std::fpclassify(dest) == FP_SUBNORMAL)) { + is.clear(is.rdstate() & (~std::ios_base::failbit)); + } + return dest; +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_IOSTREAM_STATE_SAVER_H_ diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc new file mode 100644 index 00000000..2ecbaac1 --- /dev/null +++ b/absl/random/internal/iostream_state_saver_test.cc @@ -0,0 +1,369 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/iostream_state_saver.h" + +#include <sstream> +#include <string> + +#include "gtest/gtest.h" + +namespace { + +using absl::random_internal::make_istream_state_saver; +using absl::random_internal::make_ostream_state_saver; +using absl::random_internal::stream_precision_helper; + +template <typename T> +typename absl::enable_if_t<std::is_integral<T>::value, T> // +StreamRoundTrip(T t) { + std::stringstream ss; + { + auto saver = make_ostream_state_saver(ss); + ss.precision(stream_precision_helper<T>::kPrecision); + ss << t; + } + T result = 0; + { + auto saver = make_istream_state_saver(ss); + ss >> result; + } + EXPECT_FALSE(ss.fail()) // + << ss.str() << " " // + << (ss.good() ? "good " : "") // + << (ss.bad() ? "bad " : "") // + << (ss.eof() ? "eof " : "") // + << (ss.fail() ? "fail " : ""); + + return result; +} + +template <typename T> +typename absl::enable_if_t<std::is_floating_point<T>::value, T> // +StreamRoundTrip(T t) { + std::stringstream ss; + { + auto saver = make_ostream_state_saver(ss); + ss.precision(stream_precision_helper<T>::kPrecision); + ss << t; + } + T result = 0; + { + auto saver = make_istream_state_saver(ss); + result = absl::random_internal::read_floating_point<T>(ss); + } + EXPECT_FALSE(ss.fail()) // + << ss.str() << " " // + << (ss.good() ? "good " : "") // + << (ss.bad() ? "bad " : "") // + << (ss.eof() ? "eof " : "") // + << (ss.fail() ? "fail " : ""); + + return result; +} + +TEST(IOStreamStateSaver, BasicSaverState) { + std::stringstream ss; + ss.precision(2); + ss.fill('x'); + ss.flags(std::ios_base::dec | std::ios_base::right); + + { + auto saver = make_ostream_state_saver(ss); + ss.precision(10); + EXPECT_NE('x', ss.fill()); + EXPECT_EQ(10, ss.precision()); + EXPECT_NE(std::ios_base::dec | std::ios_base::right, ss.flags()); + + ss << 1.23; + } + + EXPECT_EQ('x', ss.fill()); + EXPECT_EQ(2, ss.precision()); + EXPECT_EQ(std::ios_base::dec | std::ios_base::right, ss.flags()); +} + +TEST(IOStreamStateSaver, RoundTripInts) { + const uint64_t kUintValues[] = { + 0, + 1, + static_cast<uint64_t>(-1), + 2, + static_cast<uint64_t>(-2), + + 1 << 7, + 1 << 8, + 1 << 16, + 1ull << 32, + 1ull << 50, + 1ull << 62, + 1ull << 63, + + (1 << 7) - 1, + (1 << 8) - 1, + (1 << 16) - 1, + (1ull << 32) - 1, + (1ull << 50) - 1, + (1ull << 62) - 1, + (1ull << 63) - 1, + + static_cast<uint64_t>(-(1 << 8)), + static_cast<uint64_t>(-(1 << 16)), + static_cast<uint64_t>(-(1ll << 32)), + static_cast<uint64_t>(-(1ll << 50)), + static_cast<uint64_t>(-(1ll << 62)), + + static_cast<uint64_t>(-(1 << 8) - 1), + static_cast<uint64_t>(-(1 << 16) - 1), + static_cast<uint64_t>(-(1ll << 32) - 1), + static_cast<uint64_t>(-(1ll << 50) - 1), + static_cast<uint64_t>(-(1ll << 62) - 1), + }; + + for (const uint64_t u : kUintValues) { + EXPECT_EQ(u, StreamRoundTrip<uint64_t>(u)); + + int64_t x = static_cast<int64_t>(u); + EXPECT_EQ(x, StreamRoundTrip<int64_t>(x)); + + double d = static_cast<double>(x); + EXPECT_EQ(d, StreamRoundTrip<double>(d)); + + float f = d; + EXPECT_EQ(f, StreamRoundTrip<float>(f)); + } +} + +TEST(IOStreamStateSaver, RoundTripFloats) { + static_assert( + stream_precision_helper<float>::kPrecision >= 9, + "stream_precision_helper<float>::kPrecision should be at least 9"); + + const float kValues[] = { + 1, + std::nextafter(1.0f, 0.0f), // 1 - epsilon + std::nextafter(1.0f, 2.0f), // 1 + epsilon + + 1.0e+1f, + 1.0e-1f, + 1.0e+2f, + 1.0e-2f, + 1.0e+10f, + 1.0e-10f, + + 0.00000051110000111311111111f, + -0.00000051110000111211111111f, + + 1.234678912345678912345e+6f, + 1.234678912345678912345e-6f, + 1.234678912345678912345e+30f, + 1.234678912345678912345e-30f, + 1.234678912345678912345e+38f, + 1.0234678912345678912345e-38f, + + // Boundary cases. + std::numeric_limits<float>::max(), + std::numeric_limits<float>::lowest(), + std::numeric_limits<float>::epsilon(), + std::nextafter(std::numeric_limits<float>::min(), + 1.0f), // min + epsilon + std::numeric_limits<float>::min(), // smallest normal + // There are some errors dealing with denorms on apple platforms. + std::numeric_limits<float>::denorm_min(), // smallest denorm + std::numeric_limits<float>::min() / 2, + std::nextafter(std::numeric_limits<float>::min(), + 0.0f), // denorm_max + std::nextafter(std::numeric_limits<float>::denorm_min(), 1.0f), + }; + + for (const float f : kValues) { + EXPECT_EQ(f, StreamRoundTrip<float>(f)); + EXPECT_EQ(-f, StreamRoundTrip<float>(-f)); + + double d = f; + EXPECT_EQ(d, StreamRoundTrip<double>(d)); + EXPECT_EQ(-d, StreamRoundTrip<double>(-d)); + + // Avoid undefined behavior (overflow/underflow). + if (d <= std::numeric_limits<int64_t>::max() && + d >= std::numeric_limits<int64_t>::lowest()) { + int64_t x = static_cast<int64_t>(f); + EXPECT_EQ(x, StreamRoundTrip<int64_t>(x)); + } + } +} + +TEST(IOStreamStateSaver, RoundTripDoubles) { + static_assert( + stream_precision_helper<double>::kPrecision >= 17, + "stream_precision_helper<double>::kPrecision should be at least 17"); + + const double kValues[] = { + 1, + std::nextafter(1.0, 0.0), // 1 - epsilon + std::nextafter(1.0, 2.0), // 1 + epsilon + + 1.0e+1, + 1.0e-1, + 1.0e+2, + 1.0e-2, + 1.0e+10, + 1.0e-10, + + 0.00000051110000111311111111, + -0.00000051110000111211111111, + + 1.234678912345678912345e+6, + 1.234678912345678912345e-6, + 1.234678912345678912345e+30, + 1.234678912345678912345e-30, + 1.234678912345678912345e+38, + 1.0234678912345678912345e-38, + + 1.0e+100, + 1.0e-100, + 1.234678912345678912345e+308, + 1.0234678912345678912345e-308, + 2.22507385850720138e-308, + + // Boundary cases. + std::numeric_limits<double>::max(), + std::numeric_limits<double>::lowest(), + std::numeric_limits<double>::epsilon(), + std::nextafter(std::numeric_limits<double>::min(), + 1.0), // min + epsilon + std::numeric_limits<double>::min(), // smallest normal + // There are some errors dealing with denorms on apple platforms. + std::numeric_limits<double>::denorm_min(), // smallest denorm + std::numeric_limits<double>::min() / 2, + std::nextafter(std::numeric_limits<double>::min(), + 0.0), // denorm_max + std::nextafter(std::numeric_limits<double>::denorm_min(), 1.0f), + }; + + for (const double d : kValues) { + EXPECT_EQ(d, StreamRoundTrip<double>(d)); + EXPECT_EQ(-d, StreamRoundTrip<double>(-d)); + + // Avoid undefined behavior (overflow/underflow). + if (d <= std::numeric_limits<float>::max() && + d >= std::numeric_limits<float>::lowest()) { + float f = static_cast<float>(d); + EXPECT_EQ(f, StreamRoundTrip<float>(f)); + } + + // Avoid undefined behavior (overflow/underflow). + if (d <= std::numeric_limits<int64_t>::max() && + d >= std::numeric_limits<int64_t>::lowest()) { + int64_t x = static_cast<int64_t>(d); + EXPECT_EQ(x, StreamRoundTrip<int64_t>(x)); + } + } +} + +TEST(IOStreamStateSaver, RoundTripLongDoubles) { + // Technically, C++ only guarantees that long double is at least as large as a + // double. Practically it varies from 64-bits to 128-bits. + // + // So it is best to consider long double a best-effort extended precision + // type. + + static_assert( + stream_precision_helper<long double>::kPrecision >= 36, + "stream_precision_helper<long double>::kPrecision should be at least 36"); + + using real_type = long double; + const real_type kValues[] = { + 1, + std::nextafter(1.0, 0.0), // 1 - epsilon + std::nextafter(1.0, 2.0), // 1 + epsilon + + 1.0e+1, + 1.0e-1, + 1.0e+2, + 1.0e-2, + 1.0e+10, + 1.0e-10, + + 0.00000051110000111311111111, + -0.00000051110000111211111111, + + 1.2346789123456789123456789123456789e+6, + 1.2346789123456789123456789123456789e-6, + 1.2346789123456789123456789123456789e+30, + 1.2346789123456789123456789123456789e-30, + 1.2346789123456789123456789123456789e+38, + 1.2346789123456789123456789123456789e-38, + 1.2346789123456789123456789123456789e+308, + 1.2346789123456789123456789123456789e-308, + + 1.0e+100, + 1.0e-100, + 1.234678912345678912345e+308, + 1.0234678912345678912345e-308, + + // Boundary cases. + std::numeric_limits<real_type>::max(), + std::numeric_limits<real_type>::lowest(), + std::numeric_limits<real_type>::epsilon(), + std::nextafter(std::numeric_limits<real_type>::min(), + real_type(1)), // min + epsilon + std::numeric_limits<real_type>::min(), // smallest normal + // There are some errors dealing with denorms on apple platforms. + std::numeric_limits<real_type>::denorm_min(), // smallest denorm + std::numeric_limits<real_type>::min() / 2, + std::nextafter(std::numeric_limits<real_type>::min(), + 0.0), // denorm_max + std::nextafter(std::numeric_limits<real_type>::denorm_min(), 1.0f), + }; + + int index = -1; + for (const long double dd : kValues) { + index++; + EXPECT_EQ(dd, StreamRoundTrip<real_type>(dd)) << index; + EXPECT_EQ(-dd, StreamRoundTrip<real_type>(-dd)) << index; + + // Avoid undefined behavior (overflow/underflow). + if (dd <= std::numeric_limits<double>::max() && + dd >= std::numeric_limits<double>::lowest()) { + double d = static_cast<double>(dd); + EXPECT_EQ(d, StreamRoundTrip<double>(d)); + } + + // Avoid undefined behavior (overflow/underflow). + if (dd <= std::numeric_limits<int64_t>::max() && + dd >= std::numeric_limits<int64_t>::lowest()) { + int64_t x = static_cast<int64_t>(dd); + EXPECT_EQ(x, StreamRoundTrip<int64_t>(x)); + } + } +} + +TEST(StrToDTest, DoubleMin) { + const char kV[] = "2.22507385850720138e-308"; + char* end; + double x = std::strtod(kV, &end); + EXPECT_EQ(std::numeric_limits<double>::min(), x); + // errno may equal ERANGE. +} + +TEST(StrToDTest, DoubleDenormMin) { + const char kV[] = "4.94065645841246544e-324"; + char* end; + double x = std::strtod(kV, &end); + EXPECT_EQ(std::numeric_limits<double>::denorm_min(), x); + // errno may equal ERANGE. +} + +} // namespace diff --git a/absl/random/internal/named_generator.cc b/absl/random/internal/named_generator.cc new file mode 100644 index 00000000..b168a25b --- /dev/null +++ b/absl/random/internal/named_generator.cc @@ -0,0 +1,30 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cstddef> +#include <iostream> + +#include "absl/random/random.h" + +// This program is used in integration tests. + +int main() { + auto seed_seq = absl::MakeTaggedSeedSeq("TEST_GENERATOR", std::cerr); + absl::BitGen rng(seed_seq); + constexpr size_t kSequenceLength = 8; + for (size_t i = 0; i < kSequenceLength; i++) { + std::cout << rng() << "\n"; + } + return 0; +} diff --git a/absl/random/internal/nanobenchmark.cc b/absl/random/internal/nanobenchmark.cc new file mode 100644 index 00000000..4d26469b --- /dev/null +++ b/absl/random/internal/nanobenchmark.cc @@ -0,0 +1,812 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/nanobenchmark.h" + +#include <sys/types.h> + +#include <algorithm> // sort +#include <atomic> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <cstring> // memcpy +#include <limits> +#include <string> +#include <utility> +#include <vector> + +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/platform.h" +#include "absl/random/internal/randen_engine.h" + +// OS +#if defined(_WIN32) || defined(_WIN64) +#define ABSL_OS_WIN +#include <windows.h> // NOLINT + +#elif defined(__ANDROID__) +#define ABSL_OS_ANDROID + +#elif defined(__linux__) +#define ABSL_OS_LINUX +#include <sched.h> // NOLINT +#include <sys/syscall.h> // NOLINT +#endif + +#if defined(ABSL_ARCH_X86_64) && !defined(ABSL_OS_WIN) +#include <cpuid.h> // NOLINT +#endif + +// __ppc_get_timebase_freq +#if defined(ABSL_ARCH_PPC) +#include <sys/platform/ppc.h> // NOLINT +#endif + +// clock_gettime +#if defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64) +#include <time.h> // NOLINT +#endif + +// ABSL_HAVE_ATTRIBUTE +#if !defined(ABSL_HAVE_ATTRIBUTE) +#ifdef __has_attribute +#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) +#else +#define ABSL_HAVE_ATTRIBUTE(x) 0 +#endif +#endif + +// ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE prevents inlining of the method. +#if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __attribute__((noinline)) +#elif defined(_MSC_VER) +#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE __declspec(noinline) +#else +#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE +#endif + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal_nanobenchmark { +namespace { + +// For code folding. +namespace platform { +#if defined(ABSL_ARCH_X86_64) + +// TODO(janwas): Merge with the one in randen_hwaes.cc? +void Cpuid(const uint32_t level, const uint32_t count, + uint32_t* ABSL_RANDOM_INTERNAL_RESTRICT abcd) { +#if defined(ABSL_OS_WIN) + int regs[4]; + __cpuidex(regs, level, count); + for (int i = 0; i < 4; ++i) { + abcd[i] = regs[i]; + } +#else + uint32_t a, b, c, d; + __cpuid_count(level, count, a, b, c, d); + abcd[0] = a; + abcd[1] = b; + abcd[2] = c; + abcd[3] = d; +#endif +} + +std::string BrandString() { + char brand_string[49]; + uint32_t abcd[4]; + + // Check if brand std::string is supported (it is on all reasonable Intel/AMD) + Cpuid(0x80000000U, 0, abcd); + if (abcd[0] < 0x80000004U) { + return std::string(); + } + + for (int i = 0; i < 3; ++i) { + Cpuid(0x80000002U + i, 0, abcd); + memcpy(brand_string + i * 16, &abcd, sizeof(abcd)); + } + brand_string[48] = 0; + return brand_string; +} + +// Returns the frequency quoted inside the brand string. This does not +// account for throttling nor Turbo Boost. +double NominalClockRate() { + const std::string& brand_string = BrandString(); + // Brand strings include the maximum configured frequency. These prefixes are + // defined by Intel CPUID documentation. + const char* prefixes[3] = {"MHz", "GHz", "THz"}; + const double multipliers[3] = {1E6, 1E9, 1E12}; + for (size_t i = 0; i < 3; ++i) { + const size_t pos_prefix = brand_string.find(prefixes[i]); + if (pos_prefix != std::string::npos) { + const size_t pos_space = brand_string.rfind(' ', pos_prefix - 1); + if (pos_space != std::string::npos) { + const std::string digits = + brand_string.substr(pos_space + 1, pos_prefix - pos_space - 1); + return std::stod(digits) * multipliers[i]; + } + } + } + + return 0.0; +} + +#endif // ABSL_ARCH_X86_64 +} // namespace platform + +// Prevents the compiler from eliding the computations that led to "output". +template <class T> +inline void PreventElision(T&& output) { +#ifndef ABSL_OS_WIN + // Works by indicating to the compiler that "output" is being read and + // modified. The +r constraint avoids unnecessary writes to memory, but only + // works for built-in types (typically FuncOutput). + asm volatile("" : "+r"(output) : : "memory"); +#else + // MSVC does not support inline assembly anymore (and never supported GCC's + // RTL constraints). Self-assignment with #pragma optimize("off") might be + // expected to prevent elision, but it does not with MSVC 2015. Type-punning + // with volatile pointers generates inefficient code on MSVC 2017. + static std::atomic<T> dummy(T{}); + dummy.store(output, std::memory_order_relaxed); +#endif +} + +namespace timer { + +// Start/Stop return absolute timestamps and must be placed immediately before +// and after the region to measure. We provide separate Start/Stop functions +// because they use different fences. +// +// Background: RDTSC is not 'serializing'; earlier instructions may complete +// after it, and/or later instructions may complete before it. 'Fences' ensure +// regions' elapsed times are independent of such reordering. The only +// documented unprivileged serializing instruction is CPUID, which acts as a +// full fence (no reordering across it in either direction). Unfortunately +// the latency of CPUID varies wildly (perhaps made worse by not initializing +// its EAX input). Because it cannot reliably be deducted from the region's +// elapsed time, it must not be included in the region to measure (i.e. +// between the two RDTSC). +// +// The newer RDTSCP is sometimes described as serializing, but it actually +// only serves as a half-fence with release semantics. Although all +// instructions in the region will complete before the final timestamp is +// captured, subsequent instructions may leak into the region and increase the +// elapsed time. Inserting another fence after the final RDTSCP would prevent +// such reordering without affecting the measured region. +// +// Fortunately, such a fence exists. The LFENCE instruction is only documented +// to delay later loads until earlier loads are visible. However, Intel's +// reference manual says it acts as a full fence (waiting until all earlier +// instructions have completed, and delaying later instructions until it +// completes). AMD assigns the same behavior to MFENCE. +// +// We need a fence before the initial RDTSC to prevent earlier instructions +// from leaking into the region, and arguably another after RDTSC to avoid +// region instructions from completing before the timestamp is recorded. +// When surrounded by fences, the additional RDTSCP half-fence provides no +// benefit, so the initial timestamp can be recorded via RDTSC, which has +// lower overhead than RDTSCP because it does not read TSC_AUX. In summary, +// we define Start = LFENCE/RDTSC/LFENCE; Stop = RDTSCP/LFENCE. +// +// Using Start+Start leads to higher variance and overhead than Stop+Stop. +// However, Stop+Stop includes an LFENCE in the region measurements, which +// adds a delay dependent on earlier loads. The combination of Start+Stop +// is faster than Start+Start and more consistent than Stop+Stop because +// the first LFENCE already delayed subsequent loads before the measured +// region. This combination seems not to have been considered in prior work: +// http://akaros.cs.berkeley.edu/lxr/akaros/kern/arch/x86/rdtsc_test.c +// +// Note: performance counters can measure 'exact' instructions-retired or +// (unhalted) cycle counts. The RDPMC instruction is not serializing and also +// requires fences. Unfortunately, it is not accessible on all OSes and we +// prefer to avoid kernel-mode drivers. Performance counters are also affected +// by several under/over-count errata, so we use the TSC instead. + +// Returns a 64-bit timestamp in unit of 'ticks'; to convert to seconds, +// divide by InvariantTicksPerSecond. +inline uint64_t Start64() { + uint64_t t; +#if defined(ABSL_ARCH_PPC) + asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268)); +#elif defined(ABSL_ARCH_X86_64) +#if defined(ABSL_OS_WIN) + _ReadWriteBarrier(); + _mm_lfence(); + _ReadWriteBarrier(); + t = __rdtsc(); + _ReadWriteBarrier(); + _mm_lfence(); + _ReadWriteBarrier(); +#else + asm volatile( + "lfence\n\t" + "rdtsc\n\t" + "shl $32, %%rdx\n\t" + "or %%rdx, %0\n\t" + "lfence" + : "=a"(t) + : + // "memory" avoids reordering. rdx = TSC >> 32. + // "cc" = flags modified by SHL. + : "rdx", "memory", "cc"); +#endif +#else + // Fall back to OS - unsure how to reliably query cntvct_el0 frequency. + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + t = ts.tv_sec * 1000000000LL + ts.tv_nsec; +#endif + return t; +} + +inline uint64_t Stop64() { + uint64_t t; +#if defined(ABSL_ARCH_X86_64) +#if defined(ABSL_OS_WIN) + _ReadWriteBarrier(); + unsigned aux; + t = __rdtscp(&aux); + _ReadWriteBarrier(); + _mm_lfence(); + _ReadWriteBarrier(); +#else + // Use inline asm because __rdtscp generates code to store TSC_AUX (ecx). + asm volatile( + "rdtscp\n\t" + "shl $32, %%rdx\n\t" + "or %%rdx, %0\n\t" + "lfence" + : "=a"(t) + : + // "memory" avoids reordering. rcx = TSC_AUX. rdx = TSC >> 32. + // "cc" = flags modified by SHL. + : "rcx", "rdx", "memory", "cc"); +#endif +#else + t = Start64(); +#endif + return t; +} + +// Returns a 32-bit timestamp with about 4 cycles less overhead than +// Start64. Only suitable for measuring very short regions because the +// timestamp overflows about once a second. +inline uint32_t Start32() { + uint32_t t; +#if defined(ABSL_ARCH_X86_64) +#if defined(ABSL_OS_WIN) + _ReadWriteBarrier(); + _mm_lfence(); + _ReadWriteBarrier(); + t = static_cast<uint32_t>(__rdtsc()); + _ReadWriteBarrier(); + _mm_lfence(); + _ReadWriteBarrier(); +#else + asm volatile( + "lfence\n\t" + "rdtsc\n\t" + "lfence" + : "=a"(t) + : + // "memory" avoids reordering. rdx = TSC >> 32. + : "rdx", "memory"); +#endif +#else + t = static_cast<uint32_t>(Start64()); +#endif + return t; +} + +inline uint32_t Stop32() { + uint32_t t; +#if defined(ABSL_ARCH_X86_64) +#if defined(ABSL_OS_WIN) + _ReadWriteBarrier(); + unsigned aux; + t = static_cast<uint32_t>(__rdtscp(&aux)); + _ReadWriteBarrier(); + _mm_lfence(); + _ReadWriteBarrier(); +#else + // Use inline asm because __rdtscp generates code to store TSC_AUX (ecx). + asm volatile( + "rdtscp\n\t" + "lfence" + : "=a"(t) + : + // "memory" avoids reordering. rcx = TSC_AUX. rdx = TSC >> 32. + : "rcx", "rdx", "memory"); +#endif +#else + t = static_cast<uint32_t>(Stop64()); +#endif + return t; +} + +} // namespace timer + +namespace robust_statistics { + +// Sorts integral values in ascending order (e.g. for Mode). About 3x faster +// than std::sort for input distributions with very few unique values. +template <class T> +void CountingSort(T* values, size_t num_values) { + // Unique values and their frequency (similar to flat_map). + using Unique = std::pair<T, int>; + std::vector<Unique> unique; + for (size_t i = 0; i < num_values; ++i) { + const T value = values[i]; + const auto pos = + std::find_if(unique.begin(), unique.end(), + [value](const Unique u) { return u.first == value; }); + if (pos == unique.end()) { + unique.push_back(std::make_pair(value, 1)); + } else { + ++pos->second; + } + } + + // Sort in ascending order of value (pair.first). + std::sort(unique.begin(), unique.end()); + + // Write that many copies of each unique value to the array. + T* ABSL_RANDOM_INTERNAL_RESTRICT p = values; + for (const auto& value_count : unique) { + std::fill(p, p + value_count.second, value_count.first); + p += value_count.second; + } + ABSL_RAW_CHECK(p == values + num_values, "Did not produce enough output"); +} + +// @return i in [idx_begin, idx_begin + half_count) that minimizes +// sorted[i + half_count] - sorted[i]. +template <typename T> +size_t MinRange(const T* const ABSL_RANDOM_INTERNAL_RESTRICT sorted, + const size_t idx_begin, const size_t half_count) { + T min_range = (std::numeric_limits<T>::max)(); + size_t min_idx = 0; + + for (size_t idx = idx_begin; idx < idx_begin + half_count; ++idx) { + ABSL_RAW_CHECK(sorted[idx] <= sorted[idx + half_count], "Not sorted"); + const T range = sorted[idx + half_count] - sorted[idx]; + if (range < min_range) { + min_range = range; + min_idx = idx; + } + } + + return min_idx; +} + +// Returns an estimate of the mode by calling MinRange on successively +// halved intervals. "sorted" must be in ascending order. This is the +// Half Sample Mode estimator proposed by Bickel in "On a fast, robust +// estimator of the mode", with complexity O(N log N). The mode is less +// affected by outliers in highly-skewed distributions than the median. +// The averaging operation below assumes "T" is an unsigned integer type. +template <typename T> +T ModeOfSorted(const T* const ABSL_RANDOM_INTERNAL_RESTRICT sorted, + const size_t num_values) { + size_t idx_begin = 0; + size_t half_count = num_values / 2; + while (half_count > 1) { + idx_begin = MinRange(sorted, idx_begin, half_count); + half_count >>= 1; + } + + const T x = sorted[idx_begin + 0]; + if (half_count == 0) { + return x; + } + ABSL_RAW_CHECK(half_count == 1, "Should stop at half_count=1"); + const T average = (x + sorted[idx_begin + 1] + 1) / 2; + return average; +} + +// Returns the mode. Side effect: sorts "values". +template <typename T> +T Mode(T* values, const size_t num_values) { + CountingSort(values, num_values); + return ModeOfSorted(values, num_values); +} + +template <typename T, size_t N> +T Mode(T (&values)[N]) { + return Mode(&values[0], N); +} + +// Returns the median value. Side effect: sorts "values". +template <typename T> +T Median(T* values, const size_t num_values) { + ABSL_RAW_CHECK(num_values != 0, "Empty input"); + std::sort(values, values + num_values); + const size_t half = num_values / 2; + // Odd count: return middle + if (num_values % 2) { + return values[half]; + } + // Even count: return average of middle two. + return (values[half] + values[half - 1] + 1) / 2; +} + +// Returns a robust measure of variability. +template <typename T> +T MedianAbsoluteDeviation(const T* values, const size_t num_values, + const T median) { + ABSL_RAW_CHECK(num_values != 0, "Empty input"); + std::vector<T> abs_deviations; + abs_deviations.reserve(num_values); + for (size_t i = 0; i < num_values; ++i) { + const int64_t abs = std::abs(int64_t(values[i]) - int64_t(median)); + abs_deviations.push_back(static_cast<T>(abs)); + } + return Median(abs_deviations.data(), num_values); +} + +} // namespace robust_statistics + +// Ticks := platform-specific timer values (CPU cycles on x86). Must be +// unsigned to guarantee wraparound on overflow. 32 bit timers are faster to +// read than 64 bit. +using Ticks = uint32_t; + +// Returns timer overhead / minimum measurable difference. +Ticks TimerResolution() { + // Nested loop avoids exceeding stack/L1 capacity. + Ticks repetitions[Params::kTimerSamples]; + for (size_t rep = 0; rep < Params::kTimerSamples; ++rep) { + Ticks samples[Params::kTimerSamples]; + for (size_t i = 0; i < Params::kTimerSamples; ++i) { + const Ticks t0 = timer::Start32(); + const Ticks t1 = timer::Stop32(); + samples[i] = t1 - t0; + } + repetitions[rep] = robust_statistics::Mode(samples); + } + return robust_statistics::Mode(repetitions); +} + +static const Ticks timer_resolution = TimerResolution(); + +// Estimates the expected value of "lambda" values with a variable number of +// samples until the variability "rel_mad" is less than "max_rel_mad". +template <class Lambda> +Ticks SampleUntilStable(const double max_rel_mad, double* rel_mad, + const Params& p, const Lambda& lambda) { + auto measure_duration = [&lambda]() -> Ticks { + const Ticks t0 = timer::Start32(); + lambda(); + const Ticks t1 = timer::Stop32(); + return t1 - t0; + }; + + // Choose initial samples_per_eval based on a single estimated duration. + Ticks est = measure_duration(); + static const double ticks_per_second = InvariantTicksPerSecond(); + const size_t ticks_per_eval = ticks_per_second * p.seconds_per_eval; + size_t samples_per_eval = ticks_per_eval / est; + samples_per_eval = (std::max)(samples_per_eval, p.min_samples_per_eval); + + std::vector<Ticks> samples; + samples.reserve(1 + samples_per_eval); + samples.push_back(est); + + // Percentage is too strict for tiny differences, so also allow a small + // absolute "median absolute deviation". + const Ticks max_abs_mad = (timer_resolution + 99) / 100; + *rel_mad = 0.0; // ensure initialized + + for (size_t eval = 0; eval < p.max_evals; ++eval, samples_per_eval *= 2) { + samples.reserve(samples.size() + samples_per_eval); + for (size_t i = 0; i < samples_per_eval; ++i) { + const Ticks r = measure_duration(); + samples.push_back(r); + } + + if (samples.size() >= p.min_mode_samples) { + est = robust_statistics::Mode(samples.data(), samples.size()); + } else { + // For "few" (depends also on the variance) samples, Median is safer. + est = robust_statistics::Median(samples.data(), samples.size()); + } + ABSL_RAW_CHECK(est != 0, "Estimator returned zero duration"); + + // Median absolute deviation (mad) is a robust measure of 'variability'. + const Ticks abs_mad = robust_statistics::MedianAbsoluteDeviation( + samples.data(), samples.size(), est); + *rel_mad = static_cast<double>(static_cast<int>(abs_mad)) / est; + + if (*rel_mad <= max_rel_mad || abs_mad <= max_abs_mad) { + if (p.verbose) { + ABSL_RAW_LOG(INFO, + "%6zu samples => %5u (abs_mad=%4u, rel_mad=%4.2f%%)\n", + samples.size(), est, abs_mad, *rel_mad * 100.0); + } + return est; + } + } + + if (p.verbose) { + ABSL_RAW_LOG(WARNING, + "rel_mad=%4.2f%% still exceeds %4.2f%% after %6zu samples.\n", + *rel_mad * 100.0, max_rel_mad * 100.0, samples.size()); + } + return est; +} + +using InputVec = std::vector<FuncInput>; + +// Returns vector of unique input values. +InputVec UniqueInputs(const FuncInput* inputs, const size_t num_inputs) { + InputVec unique(inputs, inputs + num_inputs); + std::sort(unique.begin(), unique.end()); + unique.erase(std::unique(unique.begin(), unique.end()), unique.end()); + return unique; +} + +// Returns how often we need to call func for sufficient precision, or zero +// on failure (e.g. the elapsed time is too long for a 32-bit tick count). +size_t NumSkip(const Func func, const void* arg, const InputVec& unique, + const Params& p) { + // Min elapsed ticks for any input. + Ticks min_duration = ~0u; + + for (const FuncInput input : unique) { + // Make sure a 32-bit timer is sufficient. + const uint64_t t0 = timer::Start64(); + PreventElision(func(arg, input)); + const uint64_t t1 = timer::Stop64(); + const uint64_t elapsed = t1 - t0; + if (elapsed >= (1ULL << 30)) { + ABSL_RAW_LOG(WARNING, + "Measurement failed: need 64-bit timer for input=%zu\n", + static_cast<size_t>(input)); + return 0; + } + + double rel_mad; + const Ticks total = SampleUntilStable( + p.target_rel_mad, &rel_mad, p, + [func, arg, input]() { PreventElision(func(arg, input)); }); + min_duration = (std::min)(min_duration, total - timer_resolution); + } + + // Number of repetitions required to reach the target resolution. + const size_t max_skip = p.precision_divisor; + // Number of repetitions given the estimated duration. + const size_t num_skip = + min_duration == 0 ? 0 : (max_skip + min_duration - 1) / min_duration; + if (p.verbose) { + ABSL_RAW_LOG(INFO, "res=%u max_skip=%zu min_dur=%u num_skip=%zu\n", + timer_resolution, max_skip, min_duration, num_skip); + } + return num_skip; +} + +// Replicates inputs until we can omit "num_skip" occurrences of an input. +InputVec ReplicateInputs(const FuncInput* inputs, const size_t num_inputs, + const size_t num_unique, const size_t num_skip, + const Params& p) { + InputVec full; + if (num_unique == 1) { + full.assign(p.subset_ratio * num_skip, inputs[0]); + return full; + } + + full.reserve(p.subset_ratio * num_skip * num_inputs); + for (size_t i = 0; i < p.subset_ratio * num_skip; ++i) { + full.insert(full.end(), inputs, inputs + num_inputs); + } + absl::random_internal::randen_engine<uint32_t> rng; + std::shuffle(full.begin(), full.end(), rng); + return full; +} + +// Copies the "full" to "subset" in the same order, but with "num_skip" +// randomly selected occurrences of "input_to_skip" removed. +void FillSubset(const InputVec& full, const FuncInput input_to_skip, + const size_t num_skip, InputVec* subset) { + const size_t count = std::count(full.begin(), full.end(), input_to_skip); + // Generate num_skip random indices: which occurrence to skip. + std::vector<uint32_t> omit; + // Replacement for std::iota, not yet available in MSVC builds. + omit.reserve(count); + for (size_t i = 0; i < count; ++i) { + omit.push_back(i); + } + // omit[] is the same on every call, but that's OK because they identify the + // Nth instance of input_to_skip, so the position within full[] differs. + absl::random_internal::randen_engine<uint32_t> rng; + std::shuffle(omit.begin(), omit.end(), rng); + omit.resize(num_skip); + std::sort(omit.begin(), omit.end()); + + uint32_t occurrence = ~0u; // 0 after preincrement + size_t idx_omit = 0; // cursor within omit[] + size_t idx_subset = 0; // cursor within *subset + for (const FuncInput next : full) { + if (next == input_to_skip) { + ++occurrence; + // Haven't removed enough already + if (idx_omit < num_skip) { + // This one is up for removal + if (occurrence == omit[idx_omit]) { + ++idx_omit; + continue; + } + } + } + if (idx_subset < subset->size()) { + (*subset)[idx_subset++] = next; + } + } + ABSL_RAW_CHECK(idx_subset == subset->size(), "idx_subset not at end"); + ABSL_RAW_CHECK(idx_omit == omit.size(), "idx_omit not at end"); + ABSL_RAW_CHECK(occurrence == count - 1, "occurrence not at end"); +} + +// Returns total ticks elapsed for all inputs. +Ticks TotalDuration(const Func func, const void* arg, const InputVec* inputs, + const Params& p, double* max_rel_mad) { + double rel_mad; + const Ticks duration = + SampleUntilStable(p.target_rel_mad, &rel_mad, p, [func, arg, inputs]() { + for (const FuncInput input : *inputs) { + PreventElision(func(arg, input)); + } + }); + *max_rel_mad = (std::max)(*max_rel_mad, rel_mad); + return duration; +} + +// (Nearly) empty Func for measuring timer overhead/resolution. +ABSL_RANDOM_INTERNAL_ATTRIBUTE_NEVER_INLINE FuncOutput +EmptyFunc(const void* arg, const FuncInput input) { + return input; +} + +// Returns overhead of accessing inputs[] and calling a function; this will +// be deducted from future TotalDuration return values. +Ticks Overhead(const void* arg, const InputVec* inputs, const Params& p) { + double rel_mad; + // Zero tolerance because repeatability is crucial and EmptyFunc is fast. + return SampleUntilStable(0.0, &rel_mad, p, [arg, inputs]() { + for (const FuncInput input : *inputs) { + PreventElision(EmptyFunc(arg, input)); + } + }); +} + +} // namespace + +void PinThreadToCPU(int cpu) { + // We might migrate to another CPU before pinning below, but at least cpu + // will be one of the CPUs on which this thread ran. +#if defined(ABSL_OS_WIN) + if (cpu < 0) { + cpu = static_cast<int>(GetCurrentProcessorNumber()); + ABSL_RAW_CHECK(cpu >= 0, "PinThreadToCPU detect failed"); + if (cpu >= 64) { + // NOTE: On wine, at least, GetCurrentProcessorNumber() sometimes returns + // a value > 64, which is out of range. When this happens, log a message + // and don't set a cpu affinity. + ABSL_RAW_LOG(ERROR, "Invalid CPU number: %d", cpu); + return; + } + } else if (cpu >= 64) { + // User specified an explicit CPU affinity > the valid range. + ABSL_RAW_LOG(FATAL, "Invalid CPU number: %d", cpu); + } + const DWORD_PTR prev = SetThreadAffinityMask(GetCurrentThread(), 1ULL << cpu); + ABSL_RAW_CHECK(prev != 0, "SetAffinity failed"); +#elif defined(ABSL_OS_LINUX) && !defined(ABSL_OS_ANDROID) + if (cpu < 0) { + cpu = sched_getcpu(); + ABSL_RAW_CHECK(cpu >= 0, "PinThreadToCPU detect failed"); + } + const pid_t pid = 0; // current thread + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(cpu, &set); + const int err = sched_setaffinity(pid, sizeof(set), &set); + ABSL_RAW_CHECK(err == 0, "SetAffinity failed"); +#endif +} + +// Returns tick rate. Invariant means the tick counter frequency is independent +// of CPU throttling or sleep. May be expensive, caller should cache the result. +double InvariantTicksPerSecond() { +#if defined(ABSL_ARCH_PPC) + return __ppc_get_timebase_freq(); +#elif defined(ABSL_ARCH_X86_64) + // We assume the TSC is invariant; it is on all recent Intel/AMD CPUs. + return platform::NominalClockRate(); +#else + // Fall back to clock_gettime nanoseconds. + return 1E9; +#endif +} + +size_t MeasureImpl(const Func func, const void* arg, const size_t num_skip, + const InputVec& unique, const InputVec& full, + const Params& p, Result* results) { + const float mul = 1.0f / static_cast<int>(num_skip); + + InputVec subset(full.size() - num_skip); + const Ticks overhead = Overhead(arg, &full, p); + const Ticks overhead_skip = Overhead(arg, &subset, p); + if (overhead < overhead_skip) { + ABSL_RAW_LOG(WARNING, "Measurement failed: overhead %u < %u\n", overhead, + overhead_skip); + return 0; + } + + if (p.verbose) { + ABSL_RAW_LOG(INFO, "#inputs=%5zu,%5zu overhead=%5u,%5u\n", full.size(), + subset.size(), overhead, overhead_skip); + } + + double max_rel_mad = 0.0; + const Ticks total = TotalDuration(func, arg, &full, p, &max_rel_mad); + + for (size_t i = 0; i < unique.size(); ++i) { + FillSubset(full, unique[i], num_skip, &subset); + const Ticks total_skip = TotalDuration(func, arg, &subset, p, &max_rel_mad); + + if (total < total_skip) { + ABSL_RAW_LOG(WARNING, "Measurement failed: total %u < %u\n", total, + total_skip); + return 0; + } + + const Ticks duration = (total - overhead) - (total_skip - overhead_skip); + results[i].input = unique[i]; + results[i].ticks = duration * mul; + results[i].variability = max_rel_mad; + } + + return unique.size(); +} + +size_t Measure(const Func func, const void* arg, const FuncInput* inputs, + const size_t num_inputs, Result* results, const Params& p) { + ABSL_RAW_CHECK(num_inputs != 0, "No inputs"); + + const InputVec unique = UniqueInputs(inputs, num_inputs); + const size_t num_skip = NumSkip(func, arg, unique, p); // never 0 + if (num_skip == 0) return 0; // NumSkip already printed error message + + const InputVec full = + ReplicateInputs(inputs, num_inputs, unique.size(), num_skip, p); + + // MeasureImpl may fail up to p.max_measure_retries times. + for (size_t i = 0; i < p.max_measure_retries; i++) { + auto result = MeasureImpl(func, arg, num_skip, unique, full, p, results); + if (result != 0) { + return result; + } + } + // All retries failed. (Unusual) + return 0; +} + +} // namespace random_internal_nanobenchmark +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/internal/nanobenchmark.h b/absl/random/internal/nanobenchmark.h new file mode 100644 index 00000000..424d2ba2 --- /dev/null +++ b/absl/random/internal/nanobenchmark.h @@ -0,0 +1,170 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_ +#define ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_ + +// Benchmarks functions of a single integer argument with realistic branch +// prediction hit rates. Uses a robust estimator to summarize the measurements. +// The precision is about 0.2%. +// +// Examples: see nanobenchmark_test.cc. +// +// Background: Microbenchmarks such as http://github.com/google/benchmark +// can measure elapsed times on the order of a microsecond. Shorter functions +// are typically measured by repeating them thousands of times and dividing +// the total elapsed time by this count. Unfortunately, repetition (especially +// with the same input parameter!) influences the runtime. In time-critical +// code, it is reasonable to expect warm instruction/data caches and TLBs, +// but a perfect record of which branches will be taken is unrealistic. +// Unless the application also repeatedly invokes the measured function with +// the same parameter, the benchmark is measuring something very different - +// a best-case result, almost as if the parameter were made a compile-time +// constant. This may lead to erroneous conclusions about branch-heavy +// algorithms outperforming branch-free alternatives. +// +// Our approach differs in three ways. Adding fences to the timer functions +// reduces variability due to instruction reordering, improving the timer +// resolution to about 40 CPU cycles. However, shorter functions must still +// be invoked repeatedly. For more realistic branch prediction performance, +// we vary the input parameter according to a user-specified distribution. +// Thus, instead of VaryInputs(Measure(Repeat(func))), we change the +// loop nesting to Measure(Repeat(VaryInputs(func))). We also estimate the +// central tendency of the measurement samples with the "half sample mode", +// which is more robust to outliers and skewed data than the mean or median. + +// NOTE: for compatibility with multiple translation units compiled with +// distinct flags, avoid #including headers that define functions. + +#include <stddef.h> +#include <stdint.h> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal_nanobenchmark { + +// Input influencing the function being measured (e.g. number of bytes to copy). +using FuncInput = size_t; + +// "Proof of work" returned by Func to ensure the compiler does not elide it. +using FuncOutput = uint64_t; + +// Function to measure: either 1) a captureless lambda or function with two +// arguments or 2) a lambda with capture, in which case the first argument +// is reserved for use by MeasureClosure. +using Func = FuncOutput (*)(const void*, FuncInput); + +// Internal parameters that determine precision/resolution/measuring time. +struct Params { + // For measuring timer overhead/resolution. Used in a nested loop => + // quadratic time, acceptable because we know timer overhead is "low". + // constexpr because this is used to define array bounds. + static constexpr size_t kTimerSamples = 256; + + // Best-case precision, expressed as a divisor of the timer resolution. + // Larger => more calls to Func and higher precision. + size_t precision_divisor = 1024; + + // Ratio between full and subset input distribution sizes. Cannot be less + // than 2; larger values increase measurement time but more faithfully + // model the given input distribution. + size_t subset_ratio = 2; + + // Together with the estimated Func duration, determines how many times to + // call Func before checking the sample variability. Larger values increase + // measurement time, memory/cache use and precision. + double seconds_per_eval = 4E-3; + + // The minimum number of samples before estimating the central tendency. + size_t min_samples_per_eval = 7; + + // The mode is better than median for estimating the central tendency of + // skewed/fat-tailed distributions, but it requires sufficient samples + // relative to the width of half-ranges. + size_t min_mode_samples = 64; + + // Maximum permissible variability (= median absolute deviation / center). + double target_rel_mad = 0.002; + + // Abort after this many evals without reaching target_rel_mad. This + // prevents infinite loops. + size_t max_evals = 9; + + // Retry the measure loop up to this many times. + size_t max_measure_retries = 2; + + // Whether to print additional statistics to stdout. + bool verbose = true; +}; + +// Measurement result for each unique input. +struct Result { + FuncInput input; + + // Robust estimate (mode or median) of duration. + float ticks; + + // Measure of variability (median absolute deviation relative to "ticks"). + float variability; +}; + +// Ensures the thread is running on the specified cpu, and no others. +// Reduces noise due to desynchronized socket RDTSC and context switches. +// If "cpu" is negative, pin to the currently running core. +void PinThreadToCPU(const int cpu = -1); + +// Returns tick rate, useful for converting measurements to seconds. Invariant +// means the tick counter frequency is independent of CPU throttling or sleep. +// This call may be expensive, callers should cache the result. +double InvariantTicksPerSecond(); + +// Precisely measures the number of ticks elapsed when calling "func" with the +// given inputs, shuffled to ensure realistic branch prediction hit rates. +// +// "func" returns a 'proof of work' to ensure its computations are not elided. +// "arg" is passed to Func, or reserved for internal use by MeasureClosure. +// "inputs" is an array of "num_inputs" (not necessarily unique) arguments to +// "func". The values should be chosen to maximize coverage of "func". This +// represents a distribution, so a value's frequency should reflect its +// probability in the real application. Order does not matter; for example, a +// uniform distribution over [0, 4) could be represented as {3,0,2,1}. +// Returns how many Result were written to "results": one per unique input, or +// zero if the measurement failed (an error message goes to stderr). +size_t Measure(const Func func, const void* arg, const FuncInput* inputs, + const size_t num_inputs, Result* results, + const Params& p = Params()); + +// Calls operator() of the given closure (lambda function). +template <class Closure> +static FuncOutput CallClosure(const void* f, const FuncInput input) { + return (*reinterpret_cast<const Closure*>(f))(input); +} + +// Same as Measure, except "closure" is typically a lambda function of +// FuncInput -> FuncOutput with a capture list. +template <class Closure> +static inline size_t MeasureClosure(const Closure& closure, + const FuncInput* inputs, + const size_t num_inputs, Result* results, + const Params& p = Params()) { + return Measure(reinterpret_cast<Func>(&CallClosure<Closure>), + reinterpret_cast<const void*>(&closure), inputs, num_inputs, + results, p); +} + +} // namespace random_internal_nanobenchmark +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_NANOBENCHMARK_H_ diff --git a/absl/random/internal/nanobenchmark_test.cc b/absl/random/internal/nanobenchmark_test.cc new file mode 100644 index 00000000..f96e0f5d --- /dev/null +++ b/absl/random/internal/nanobenchmark_test.cc @@ -0,0 +1,77 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/nanobenchmark.h" + +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/numbers.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal_nanobenchmark { +namespace { + +uint64_t Div(const void*, FuncInput in) { + // Here we're measuring the throughput because benchmark invocations are + // independent. + const int64_t d1 = 0xFFFFFFFFFFll / int64_t(in); // IDIV + return d1; +} + +template <size_t N> +void MeasureDiv(const FuncInput (&inputs)[N]) { + Result results[N]; + Params params; + params.max_evals = 6; // avoid test timeout + const size_t num_results = Measure(&Div, nullptr, inputs, N, results, params); + if (num_results == 0) { + ABSL_RAW_LOG( + WARNING, + "WARNING: Measurement failed, should not happen when using " + "PinThreadToCPU unless the region to measure takes > 1 second.\n"); + return; + } + for (size_t i = 0; i < num_results; ++i) { + ABSL_RAW_LOG(INFO, "%5zu: %6.2f ticks; MAD=%4.2f%%\n", results[i].input, + results[i].ticks, results[i].variability * 100.0); + ABSL_RAW_CHECK(results[i].ticks != 0.0f, "Zero duration"); + } +} + +void RunAll(const int argc, char* argv[]) { + // Avoid migrating between cores - important on multi-socket systems. + int cpu = -1; + if (argc == 2) { + if (!SimpleAtoi(argv[1], &cpu)) { + ABSL_RAW_LOG(FATAL, "The optional argument must be a CPU number >= 0.\n"); + } + } + PinThreadToCPU(cpu); + + // unpredictable == 1 but the compiler doesn't know that. + const FuncInput unpredictable = argc != 999; + static const FuncInput inputs[] = {unpredictable * 10, unpredictable * 100}; + + MeasureDiv(inputs); +} + +} // namespace +} // namespace random_internal_nanobenchmark +} // inline namespace lts_2019_08_08 +} // namespace absl + +int main(int argc, char* argv[]) { + absl::random_internal_nanobenchmark::RunAll(argc, argv); + return 0; +} diff --git a/absl/random/internal/nonsecure_base.h b/absl/random/internal/nonsecure_base.h new file mode 100644 index 00000000..c8af51cf --- /dev/null +++ b/absl/random/internal/nonsecure_base.h @@ -0,0 +1,150 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_ +#define ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_ + +#include <algorithm> +#include <cstdint> +#include <iostream> +#include <iterator> +#include <random> +#include <string> +#include <type_traits> +#include <vector> + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" +#include "absl/random/internal/pool_urbg.h" +#include "absl/random/internal/salted_seed_seq.h" +#include "absl/random/internal/seed_material.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// Each instance of NonsecureURBGBase<URBG> will be seeded by variates produced +// by a thread-unique URBG-instance. +template <typename URBG> +class NonsecureURBGBase { + public: + using result_type = typename URBG::result_type; + + // Default constructor + NonsecureURBGBase() : urbg_(ConstructURBG()) {} + + // Copy disallowed, move allowed. + NonsecureURBGBase(const NonsecureURBGBase&) = delete; + NonsecureURBGBase& operator=(const NonsecureURBGBase&) = delete; + NonsecureURBGBase(NonsecureURBGBase&&) = default; + NonsecureURBGBase& operator=(NonsecureURBGBase&&) = default; + + // Constructor using a seed + template <class SSeq, typename = typename absl::enable_if_t< + !std::is_same<SSeq, NonsecureURBGBase>::value>> + explicit NonsecureURBGBase(SSeq&& seq) + : urbg_(ConstructURBG(std::forward<SSeq>(seq))) {} + + // Note: on MSVC, min() or max() can be interpreted as MIN() or MAX(), so we + // enclose min() or max() in parens as (min)() and (max)(). + // Additionally, clang-format requires no space before this construction. + + // NonsecureURBGBase::min() + static constexpr result_type(min)() { return (URBG::min)(); } + + // NonsecureURBGBase::max() + static constexpr result_type(max)() { return (URBG::max)(); } + + // NonsecureURBGBase::operator()() + result_type operator()() { return urbg_(); } + + // NonsecureURBGBase::discard() + void discard(unsigned long long values) { // NOLINT(runtime/int) + urbg_.discard(values); + } + + bool operator==(const NonsecureURBGBase& other) const { + return urbg_ == other.urbg_; + } + + bool operator!=(const NonsecureURBGBase& other) const { + return !(urbg_ == other.urbg_); + } + + private: + // Seeder is a custom seed sequence type where generate() fills the provided + // buffer via the RandenPool entropy source. + struct Seeder { + using result_type = uint32_t; + + size_t size() { return 0; } + + template <typename OutIterator> + void param(OutIterator) const {} + + template <typename RandomAccessIterator> + void generate(RandomAccessIterator begin, RandomAccessIterator end) { + if (begin != end) { + // begin, end must be random access iterators assignable from uint32_t. + generate_impl( + std::integral_constant<bool, sizeof(*begin) == sizeof(uint32_t)>{}, + begin, end); + } + } + + // Commonly, generate is invoked with a pointer to a buffer which + // can be cast to a uint32_t. + template <typename RandomAccessIterator> + void generate_impl(std::integral_constant<bool, true>, + RandomAccessIterator begin, RandomAccessIterator end) { + auto buffer = absl::MakeSpan(begin, end); + auto target = absl::MakeSpan(reinterpret_cast<uint32_t*>(buffer.data()), + buffer.size()); + RandenPool<uint32_t>::Fill(target); + } + + // The non-uint32_t case should be uncommon, and involves an extra copy, + // filling the uint32_t buffer and then mixing into the output. + template <typename RandomAccessIterator> + void generate_impl(std::integral_constant<bool, false>, + RandomAccessIterator begin, RandomAccessIterator end) { + const size_t n = std::distance(begin, end); + absl::InlinedVector<uint32_t, 8> data(n, 0); + RandenPool<uint32_t>::Fill(absl::MakeSpan(data.begin(), data.end())); + std::copy(std::begin(data), std::end(data), begin); + } + }; + + static URBG ConstructURBG() { + Seeder seeder; + return URBG(seeder); + } + + template <typename SSeq> + static URBG ConstructURBG(SSeq&& seq) { // NOLINT(runtime/references) + auto salted_seq = + random_internal::MakeSaltedSeedSeq(std::forward<SSeq>(seq)); + return URBG(salted_seq); + } + + URBG urbg_; +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_NONSECURE_BASE_H_ diff --git a/absl/random/internal/nonsecure_base_test.cc b/absl/random/internal/nonsecure_base_test.cc new file mode 100644 index 00000000..d9de9901 --- /dev/null +++ b/absl/random/internal/nonsecure_base_test.cc @@ -0,0 +1,244 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/nonsecure_base.h" + +#include <algorithm> +#include <iostream> +#include <memory> +#include <random> +#include <sstream> + +#include "gtest/gtest.h" +#include "absl/random/distributions.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" + +namespace { + +using ExampleNonsecureURBG = + absl::random_internal::NonsecureURBGBase<std::mt19937>; + +template <typename T> +void Use(const T&) {} + +} // namespace + +TEST(NonsecureURBGBase, DefaultConstructorIsValid) { + ExampleNonsecureURBG urbg; +} + +// Ensure that the recommended template-instantiations are valid. +TEST(RecommendedTemplates, CanBeConstructed) { + absl::BitGen default_generator; + absl::InsecureBitGen insecure_generator; +} + +TEST(RecommendedTemplates, CanDiscardValues) { + absl::BitGen default_generator; + absl::InsecureBitGen insecure_generator; + + default_generator.discard(5); + insecure_generator.discard(5); +} + +TEST(NonsecureURBGBase, StandardInterface) { + // Names after definition of [rand.req.urbg] in C++ standard. + // e us a value of E + // v is a lvalue of E + // x, y are possibly const values of E + // s is a value of T + // q is a value satisfying requirements of seed_sequence + // z is a value of type unsigned long long + // os is a some specialization of basic_ostream + // is is a some specialization of basic_istream + + using E = absl::random_internal::NonsecureURBGBase<std::minstd_rand>; + + using T = typename E::result_type; + + static_assert(!std::is_copy_constructible<E>::value, + "NonsecureURBGBase should not be copy constructible"); + + static_assert(!absl::is_copy_assignable<E>::value, + "NonsecureURBGBase should not be copy assignable"); + + static_assert(std::is_move_constructible<E>::value, + "NonsecureURBGBase should be move constructible"); + + static_assert(absl::is_move_assignable<E>::value, + "NonsecureURBGBase should be move assignable"); + + static_assert(std::is_same<decltype(std::declval<E>()()), T>::value, + "return type of operator() must be result_type"); + + { + const E x, y; + Use(x); + Use(y); + + static_assert(std::is_same<decltype(x == y), bool>::value, + "return type of operator== must be bool"); + + static_assert(std::is_same<decltype(x != y), bool>::value, + "return type of operator== must be bool"); + } + + E e; + std::seed_seq q{1, 2, 3}; + + E{}; + E{q}; + + // Copy constructor not supported. + // E{x}; + + // result_type seed constructor not supported. + // E{T{1}}; + + // Move constructors are supported. + { + E tmp(q); + E m = std::move(tmp); + E n(std::move(m)); + EXPECT_TRUE(e != n); + } + + // Comparisons work. + { + // MSVC emits error 2718 when using EXPECT_EQ(e, x) + // * actual parameter with __declspec(align('#')) won't be aligned + E a(q); + E b(q); + + EXPECT_TRUE(a != e); + EXPECT_TRUE(a == b); + + a(); + EXPECT_TRUE(a != b); + } + + // e.seed(s) not supported. + + // [rand.req.eng] specifies the parameter as 'unsigned long long' + // e.discard(unsigned long long) is supported. + unsigned long long z = 1; // NOLINT(runtime/int) + e.discard(z); +} + +TEST(NonsecureURBGBase, SeedSeqConstructorIsValid) { + std::seed_seq seq; + ExampleNonsecureURBG rbg(seq); +} + +TEST(NonsecureURBGBase, CompatibleWithDistributionUtils) { + ExampleNonsecureURBG rbg; + + absl::Uniform(rbg, 0, 100); + absl::Uniform(rbg, 0.5, 0.7); + absl::Poisson<uint32_t>(rbg); + absl::Exponential<float>(rbg); +} + +TEST(NonsecureURBGBase, CompatibleWithStdDistributions) { + ExampleNonsecureURBG rbg; + + std::uniform_int_distribution<uint32_t>(0, 100)(rbg); + std::uniform_real_distribution<float>()(rbg); + std::bernoulli_distribution(0.2)(rbg); +} + +TEST(NonsecureURBGBase, ConsecutiveDefaultInstancesYieldUniqueVariates) { + const size_t kNumSamples = 128; + + ExampleNonsecureURBG rbg1; + ExampleNonsecureURBG rbg2; + + for (size_t i = 0; i < kNumSamples; i++) { + EXPECT_NE(rbg1(), rbg2()); + } +} + +TEST(NonsecureURBGBase, EqualSeedSequencesYieldEqualVariates) { + std::seed_seq seq; + + ExampleNonsecureURBG rbg1(seq); + ExampleNonsecureURBG rbg2(seq); + + // ExampleNonsecureURBG rbg3({1, 2, 3}); // Should not compile. + + for (uint32_t i = 0; i < 1000; i++) { + EXPECT_EQ(rbg1(), rbg2()); + } + + rbg1.discard(100); + rbg2.discard(100); + + // The sequences should continue after discarding + for (uint32_t i = 0; i < 1000; i++) { + EXPECT_EQ(rbg1(), rbg2()); + } +} + +// This is a PRNG-compatible type specifically designed to test +// that NonsecureURBGBase::Seeder can correctly handle iterators +// to arbitrary non-uint32_t size types. +template <typename T> +struct SeederTestEngine { + using result_type = T; + + static constexpr result_type(min)() { + return (std::numeric_limits<result_type>::min)(); + } + static constexpr result_type(max)() { + return (std::numeric_limits<result_type>::max)(); + } + + template <class SeedSequence, + typename = typename absl::enable_if_t< + !std::is_same<SeedSequence, SeederTestEngine>::value>> + explicit SeederTestEngine(SeedSequence&& seq) { + seed(seq); + } + + SeederTestEngine(const SeederTestEngine&) = default; + SeederTestEngine& operator=(const SeederTestEngine&) = default; + SeederTestEngine(SeederTestEngine&&) = default; + SeederTestEngine& operator=(SeederTestEngine&&) = default; + + result_type operator()() { return state[0]; } + + template <class SeedSequence> + void seed(SeedSequence&& seq) { + std::fill(std::begin(state), std::end(state), T(0)); + seq.generate(std::begin(state), std::end(state)); + } + + T state[2]; +}; + +TEST(NonsecureURBGBase, SeederWorksForU32) { + using U32 = + absl::random_internal::NonsecureURBGBase<SeederTestEngine<uint32_t>>; + U32 x; + EXPECT_NE(0, x()); +} + +TEST(NonsecureURBGBase, SeederWorksForU64) { + using U64 = + absl::random_internal::NonsecureURBGBase<SeederTestEngine<uint64_t>>; + + U64 x; + EXPECT_NE(0, x()); +} diff --git a/absl/random/internal/pcg_engine.h b/absl/random/internal/pcg_engine.h new file mode 100644 index 00000000..607ac34b --- /dev/null +++ b/absl/random/internal/pcg_engine.h @@ -0,0 +1,307 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_ +#define ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_ + +#include <type_traits> + +#include "absl/base/config.h" +#include "absl/meta/type_traits.h" +#include "absl/numeric/int128.h" +#include "absl/random/internal/fastmath.h" +#include "absl/random/internal/iostream_state_saver.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// pcg_engine is a simplified implementation of Melissa O'Neil's PCG engine in +// C++. PCG combines a linear congruential generator (LCG) with output state +// mixing functions to generate each random variate. pcg_engine supports only a +// single sequence (oneseq), and does not support streams. +// +// pcg_engine is parameterized by two types: +// Params, which provides the multiplier and increment values; +// Mix, which mixes the state into the result. +// +template <typename Params, typename Mix> +class pcg_engine { + static_assert(std::is_same<typename Params::state_type, + typename Mix::state_type>::value, + "Class-template absl::pcg_engine must be parameterized by " + "Params and Mix with identical state_type"); + + static_assert(std::is_unsigned<typename Mix::result_type>::value, + "Class-template absl::pcg_engine must be parameterized by " + "an unsigned Mix::result_type"); + + using params_type = Params; + using mix_type = Mix; + using state_type = typename Mix::state_type; + + public: + // C++11 URBG interface: + using result_type = typename Mix::result_type; + + static constexpr result_type(min)() { + return (std::numeric_limits<result_type>::min)(); + } + + static constexpr result_type(max)() { + return (std::numeric_limits<result_type>::max)(); + } + + explicit pcg_engine(uint64_t seed_value = 0) { seed(seed_value); } + + template <class SeedSequence, + typename = typename absl::enable_if_t< + !std::is_same<SeedSequence, pcg_engine>::value>> + explicit pcg_engine(SeedSequence&& seq) { + seed(seq); + } + + pcg_engine(const pcg_engine&) = default; + pcg_engine& operator=(const pcg_engine&) = default; + pcg_engine(pcg_engine&&) = default; + pcg_engine& operator=(pcg_engine&&) = default; + + result_type operator()() { + // Advance the LCG state, always using the new value to generate the output. + state_ = lcg(state_); + return Mix{}(state_); + } + + void seed(uint64_t seed_value = 0) { + state_type tmp = seed_value; + state_ = lcg(tmp + Params::increment()); + } + + template <class SeedSequence> + typename absl::enable_if_t< + !std::is_convertible<SeedSequence, uint64_t>::value, void> + seed(SeedSequence&& seq) { + reseed(seq); + } + + void discard(uint64_t count) { state_ = advance(state_, count); } + + bool operator==(const pcg_engine& other) const { + return state_ == other.state_; + } + + bool operator!=(const pcg_engine& other) const { return !(*this == other); } + + template <class CharT, class Traits> + friend typename absl::enable_if_t<(sizeof(state_type) == 16), + std::basic_ostream<CharT, Traits>&> + operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const pcg_engine& engine) { + auto saver = random_internal::make_ostream_state_saver(os); + random_internal::stream_u128_helper<state_type> helper; + helper.write(pcg_engine::params_type::multiplier(), os); + os << os.fill(); + helper.write(pcg_engine::params_type::increment(), os); + os << os.fill(); + helper.write(engine.state_, os); + return os; + } + + template <class CharT, class Traits> + friend typename absl::enable_if_t<(sizeof(state_type) <= 8), + std::basic_ostream<CharT, Traits>&> + operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const pcg_engine& engine) { + auto saver = random_internal::make_ostream_state_saver(os); + os << pcg_engine::params_type::multiplier() << os.fill(); + os << pcg_engine::params_type::increment() << os.fill(); + os << engine.state_; + return os; + } + + template <class CharT, class Traits> + friend typename absl::enable_if_t<(sizeof(state_type) == 16), + std::basic_istream<CharT, Traits>&> + operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + pcg_engine& engine) { // NOLINT(runtime/references) + random_internal::stream_u128_helper<state_type> helper; + auto mult = helper.read(is); + auto inc = helper.read(is); + auto tmp = helper.read(is); + if (mult != pcg_engine::params_type::multiplier() || + inc != pcg_engine::params_type::increment()) { + // signal failure by setting the failbit. + is.setstate(is.rdstate() | std::ios_base::failbit); + } + if (!is.fail()) { + engine.state_ = tmp; + } + return is; + } + + template <class CharT, class Traits> + friend typename absl::enable_if_t<(sizeof(state_type) <= 8), + std::basic_istream<CharT, Traits>&> + operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + pcg_engine& engine) { // NOLINT(runtime/references) + state_type mult{}, inc{}, tmp{}; + is >> mult >> inc >> tmp; + if (mult != pcg_engine::params_type::multiplier() || + inc != pcg_engine::params_type::increment()) { + // signal failure by setting the failbit. + is.setstate(is.rdstate() | std::ios_base::failbit); + } + if (!is.fail()) { + engine.state_ = tmp; + } + return is; + } + + private: + state_type state_; + + // Returns the linear-congruential generator next state. + static inline constexpr state_type lcg(state_type s) { + return s * Params::multiplier() + Params::increment(); + } + + // Returns the linear-congruential arbitrary seek state. + inline state_type advance(state_type s, uint64_t n) const { + state_type mult = Params::multiplier(); + state_type inc = Params::increment(); + state_type m = 1; + state_type i = 0; + while (n > 0) { + if (n & 1) { + m *= mult; + i = i * mult + inc; + } + inc = (mult + 1) * inc; + mult *= mult; + n >>= 1; + } + return m * s + i; + } + + template <class SeedSequence> + void reseed(SeedSequence& seq) { + using sequence_result_type = typename SeedSequence::result_type; + constexpr size_t kBufferSize = + sizeof(state_type) / sizeof(sequence_result_type); + sequence_result_type buffer[kBufferSize]; + seq.generate(std::begin(buffer), std::end(buffer)); + // Convert the seed output to a single state value. + state_type tmp = buffer[0]; + for (size_t i = 1; i < kBufferSize; i++) { + tmp <<= (sizeof(sequence_result_type) * 8); + tmp |= buffer[i]; + } + state_ = lcg(tmp + params_type::increment()); + } +}; + +// Parameterized implementation of the PCG 128-bit oneseq state. +// This provides state_type, multiplier, and increment for pcg_engine. +template <uint64_t kMultA, uint64_t kMultB, uint64_t kIncA, uint64_t kIncB> +class pcg128_params { + public: +#if ABSL_HAVE_INTRINSIC_INT128 + using state_type = __uint128_t; + static inline constexpr state_type make_u128(uint64_t a, uint64_t b) { + return (static_cast<__uint128_t>(a) << 64) | b; + } +#else + using state_type = absl::uint128; + static inline constexpr state_type make_u128(uint64_t a, uint64_t b) { + return absl::MakeUint128(a, b); + } +#endif + + static inline constexpr state_type multiplier() { + return make_u128(kMultA, kMultB); + } + static inline constexpr state_type increment() { + return make_u128(kIncA, kIncB); + } +}; + +// Implementation of the PCG xsl_rr_128_64 128-bit mixing function, which +// accepts an input of state_type and mixes it into an output of result_type. +struct pcg_xsl_rr_128_64 { +#if ABSL_HAVE_INTRINSIC_INT128 + using state_type = __uint128_t; +#else + using state_type = absl::uint128; +#endif + using result_type = uint64_t; + + inline uint64_t operator()(state_type state) { + // This is equivalent to the xsl_rr_128_64 mixing function. +#if ABSL_HAVE_INTRINSIC_INT128 + uint64_t rotate = static_cast<uint64_t>(state >> 122u); + state ^= state >> 64; + uint64_t s = static_cast<uint64_t>(state); +#else + uint64_t h = Uint128High64(state); + uint64_t rotate = h >> 58u; + uint64_t s = Uint128Low64(state) ^ h; +#endif + return random_internal::rotr(s, rotate); + } +}; + +// Parameterized implementation of the PCG 64-bit oneseq state. +// This provides state_type, multiplier, and increment for pcg_engine. +template <uint64_t kMult, uint64_t kInc> +class pcg64_params { + public: + using state_type = uint64_t; + static inline constexpr state_type multiplier() { return kMult; } + static inline constexpr state_type increment() { return kInc; } +}; + +// Implementation of the PCG xsh_rr_64_32 64-bit mixing function, which accepts +// an input of state_type and mixes it into an output of result_type. +struct pcg_xsh_rr_64_32 { + using state_type = uint64_t; + using result_type = uint32_t; + inline uint32_t operator()(uint64_t state) { + return random_internal::rotr( + static_cast<uint32_t>(((state >> 18) ^ state) >> 27), state >> 59); + } +}; + +// Stable pcg_engine implementations: +// This is a 64-bit generator using 128-bits of state. +// The output sequence is equivalent to Melissa O'Neil's pcg64_oneseq. +using pcg64_2018_engine = pcg_engine< + random_internal::pcg128_params<0x2360ed051fc65da4ull, 0x4385df649fccf645ull, + 0x5851f42d4c957f2d, 0x14057b7ef767814f>, + random_internal::pcg_xsl_rr_128_64>; + +// This is a 32-bit generator using 64-bits of state. +// This is equivalent to Melissa O'Neil's pcg32_oneseq. +using pcg32_2018_engine = pcg_engine< + random_internal::pcg64_params<0x5851f42d4c957f2dull, 0x14057b7ef767814full>, + random_internal::pcg_xsh_rr_64_32>; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_PCG_ENGINE_H_ diff --git a/absl/random/internal/pcg_engine_test.cc b/absl/random/internal/pcg_engine_test.cc new file mode 100644 index 00000000..4d763e89 --- /dev/null +++ b/absl/random/internal/pcg_engine_test.cc @@ -0,0 +1,638 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/pcg_engine.h" + +#include <algorithm> +#include <bitset> +#include <random> +#include <sstream> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/internal/explicit_seed_seq.h" +#include "absl/time/clock.h" + +#define UPDATE_GOLDEN 0 + +namespace { + +using absl::random_internal::ExplicitSeedSeq; +using absl::random_internal::pcg32_2018_engine; +using absl::random_internal::pcg64_2018_engine; + +template <typename EngineType> +class PCGEngineTest : public ::testing::Test {}; + +using EngineTypes = ::testing::Types<pcg64_2018_engine, pcg32_2018_engine>; + +TYPED_TEST_SUITE(PCGEngineTest, EngineTypes); + +TYPED_TEST(PCGEngineTest, VerifyReseedChangesAllValues) { + using engine_type = TypeParam; + using result_type = typename engine_type::result_type; + + const size_t kNumOutputs = 16; + engine_type engine; + + // MSVC emits error 2719 without the use of std::ref below. + // * formal parameter with __declspec(align('#')) won't be aligned + + { + std::seed_seq seq1{1, 2, 3, 4, 5, 6, 7}; + engine.seed(seq1); + } + result_type a[kNumOutputs]; + std::generate(std::begin(a), std::end(a), std::ref(engine)); + + { + std::random_device rd; + std::seed_seq seq2{rd(), rd(), rd()}; + engine.seed(seq2); + } + result_type b[kNumOutputs]; + std::generate(std::begin(b), std::end(b), std::ref(engine)); + + // Verify that two uncorrelated values have ~50% of there bits in common. Use + // a 10% margin-of-error to reduce flakiness. + size_t changed_bits = 0; + size_t unchanged_bits = 0; + size_t total_set = 0; + size_t total_bits = 0; + size_t equal_count = 0; + for (size_t i = 0; i < kNumOutputs; ++i) { + equal_count += (a[i] == b[i]) ? 1 : 0; + std::bitset<sizeof(result_type) * 8> bitset(a[i] ^ b[i]); + changed_bits += bitset.count(); + unchanged_bits += bitset.size() - bitset.count(); + + std::bitset<sizeof(result_type) * 8> a_set(a[i]); + std::bitset<sizeof(result_type) * 8> b_set(b[i]); + total_set += a_set.count() + b_set.count(); + total_bits += 2 * 8 * sizeof(result_type); + } + // On average, half the bits are changed between two calls. + EXPECT_LE(changed_bits, 0.60 * (changed_bits + unchanged_bits)); + EXPECT_GE(changed_bits, 0.40 * (changed_bits + unchanged_bits)); + + // verify using a quick normal-approximation to the binomial. + EXPECT_NEAR(total_set, total_bits * 0.5, 4 * std::sqrt(total_bits)) + << "@" << total_set / static_cast<double>(total_bits); + + // Also, A[i] == B[i] with probability (1/range) * N. + // Give this a pretty wide latitude, though. + const double kExpected = kNumOutputs / (1.0 * sizeof(result_type) * 8); + EXPECT_LE(equal_count, 1.0 + kExpected); +} + +// Number of values that needs to be consumed to clean two sizes of buffer +// and trigger third refresh. (slightly overestimates the actual state size). +constexpr size_t kTwoBufferValues = 16; + +TYPED_TEST(PCGEngineTest, VerifyDiscard) { + using engine_type = TypeParam; + + for (size_t num_used = 0; num_used < kTwoBufferValues; ++num_used) { + engine_type engine_used; + for (size_t i = 0; i < num_used; ++i) { + engine_used(); + } + + for (size_t num_discard = 0; num_discard < kTwoBufferValues; + ++num_discard) { + engine_type engine1 = engine_used; + engine_type engine2 = engine_used; + for (size_t i = 0; i < num_discard; ++i) { + engine1(); + } + engine2.discard(num_discard); + for (size_t i = 0; i < kTwoBufferValues; ++i) { + const auto r1 = engine1(); + const auto r2 = engine2(); + ASSERT_EQ(r1, r2) << "used=" << num_used << " discard=" << num_discard; + } + } + } +} + +TYPED_TEST(PCGEngineTest, StreamOperatorsResult) { + using engine_type = TypeParam; + + std::wostringstream os; + std::wistringstream is; + engine_type engine; + + EXPECT_EQ(&(os << engine), &os); + EXPECT_EQ(&(is >> engine), &is); +} + +TYPED_TEST(PCGEngineTest, StreamSerialization) { + using engine_type = TypeParam; + + for (size_t discard = 0; discard < kTwoBufferValues; ++discard) { + ExplicitSeedSeq seed_sequence{12, 34, 56}; + engine_type engine(seed_sequence); + engine.discard(discard); + + std::stringstream stream; + stream << engine; + + engine_type new_engine; + stream >> new_engine; + for (size_t i = 0; i < 64; ++i) { + EXPECT_EQ(engine(), new_engine()) << " " << i; + } + } +} + +constexpr size_t kNumGoldenOutputs = 127; + +// This test is checking if randen_engine is meets interface requirements +// defined in [rand.req.urbg]. +TYPED_TEST(PCGEngineTest, RandomNumberEngineInterface) { + using engine_type = TypeParam; + + using E = engine_type; + using T = typename E::result_type; + + static_assert(std::is_copy_constructible<E>::value, + "engine_type must be copy constructible"); + + static_assert(absl::is_copy_assignable<E>::value, + "engine_type must be copy assignable"); + + static_assert(std::is_move_constructible<E>::value, + "engine_type must be move constructible"); + + static_assert(absl::is_move_assignable<E>::value, + "engine_type must be move assignable"); + + static_assert(std::is_same<decltype(std::declval<E>()()), T>::value, + "return type of operator() must be result_type"); + + // Names after definition of [rand.req.urbg] in C++ standard. + // e us a value of E + // v is a lvalue of E + // x, y are possibly const values of E + // s is a value of T + // q is a value satisfying requirements of seed_sequence + // z is a value of type unsigned long long + // os is a some specialization of basic_ostream + // is is a some specialization of basic_istream + + E e, v; + const E x, y; + T s = 1; + std::seed_seq q{1, 2, 3}; + unsigned long long z = 1; // NOLINT(runtime/int) + std::wostringstream os; + std::wistringstream is; + + E{}; + E{x}; + E{s}; + E{q}; + + e.seed(); + + // MSVC emits error 2718 when using EXPECT_EQ(e, x) + // * actual parameter with __declspec(align('#')) won't be aligned + EXPECT_TRUE(e == x); + + e.seed(q); + { + E tmp(q); + EXPECT_TRUE(e == tmp); + } + + e(); + { + E tmp(q); + EXPECT_TRUE(e != tmp); + } + + e.discard(z); + + static_assert(std::is_same<decltype(x == y), bool>::value, + "return type of operator== must be bool"); + + static_assert(std::is_same<decltype(x != y), bool>::value, + "return type of operator== must be bool"); +} + +TYPED_TEST(PCGEngineTest, RandenEngineSFINAETest) { + using engine_type = TypeParam; + using result_type = typename engine_type::result_type; + + { + engine_type engine(result_type(1)); + engine.seed(result_type(1)); + } + + { + result_type n = 1; + engine_type engine(n); + engine.seed(n); + } + + { + engine_type engine(1); + engine.seed(1); + } + + { + int n = 1; + engine_type engine(n); + engine.seed(n); + } + + { + std::seed_seq seed_seq; + engine_type engine(seed_seq); + engine.seed(seed_seq); + } + + { + engine_type engine{std::seed_seq()}; + engine.seed(std::seed_seq()); + } +} + +// ------------------------------------------------------------------ +// Stability tests for pcg64_2018_engine +// ------------------------------------------------------------------ +TEST(PCG642018EngineTest, VerifyGolden) { + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0x01070196e695f8f1, 0x703ec840c59f4493, 0xe54954914b3a44fa, + 0x96130ff204b9285e, 0x7d9fdef535ceb21a, 0x666feed42e1219a0, + 0x981f685721c8326f, 0xad80710d6eab4dda, 0xe202c480b037a029, + 0x5d3390eaedd907e2, 0x0756befb39c6b8aa, 0x1fb44ba6634d62a3, + 0x8d20423662426642, 0x34ea910167a39fb4, 0x93010b43a80d0ab6, + 0x663db08a98fc568a, 0x720b0a1335956fae, 0x2c35483e31e1d3ba, + 0x429f39776337409d, 0xb46d99e638687344, 0x105370b96aedcaee, + 0x3999e92f811cff71, 0xd230f8bcb591cfc9, 0x0dce3db2ba7bdea5, + 0xcf2f52c91eec99af, 0x2bc7c24a8b998a39, 0xbd8af1b0d599a19c, + 0x56bc45abc66059f5, 0x170a46dc170f7f1e, 0xc25daf5277b85fad, + 0xe629c2e0c948eadb, 0x1720a796915542ed, 0x22fb0caa4f909951, + 0x7e0c0f4175acd83d, 0xd9fcab37ff2a860c, 0xab2280fb2054bad1, + 0x58e8a06f37fa9e99, 0xc3a52a30b06528c7, 0x0175f773a13fc1bd, + 0x731cfc584b00e840, 0x404cc7b2648069cb, 0x5bc29153b0b7f783, + 0x771310a38cc999d1, 0x766a572f0a71a916, 0x90f450fb4fc48348, + 0xf080ea3e1c7b1a0d, 0x15471a4507d66a44, 0x7d58e55a78f3df69, + 0x0130a094576ac99c, 0x46669cb2d04b1d87, 0x17ab5bed20191840, + 0x95b177d260adff3e, 0x025fb624b6ee4c07, 0xb35de4330154a95f, + 0xe8510fff67e24c79, 0x132c3cbcd76ed2d3, 0x35e7cc145a093904, + 0x9f5b5b5f81583b79, 0x3ee749a533966233, 0x4af85886cdeda8cd, + 0x0ca5380ecb3ef3aa, 0x4f674eb7661d3192, 0x88a29aad00cd7733, + 0x70b627ca045ffac6, 0x5912b43ea887623d, 0x95dc9fc6f62cf221, + 0x926081a12a5c905b, 0x9c57d4cd7dfce651, 0x85ab2cbf23e3bb5d, + 0xc5cd669f63023152, 0x3067be0fad5d898e, 0x12b56f444cb53d05, + 0xbc2e5a640c3434fc, 0x9280bff0e4613fe1, 0x98819094c528743e, + 0x999d1c98d829df33, 0x9ff82a012dc89242, 0xf99183ed39c8be94, + 0xf0f59161cd421c55, 0x3c705730c2f6c48d, 0x66ad85c6e9278a61, + 0x2a3428e4a428d5d0, 0x79207d68fd04940d, 0xea7f2b402edc8430, + 0xa06b419ac857f63b, 0xcb1dd0e6fbc47e1c, 0x4f55229200ada6a4, + 0x9647b5e6359c927f, 0x30bf8f9197c7efe5, 0xa79519529cc384d0, + 0xbb22c4f339ad6497, 0xd7b9782f59d14175, 0x0dff12fff2ec0118, + 0xa331ad8305343a7c, 0x48dad7e3f17e0862, 0x324c6fb3fd3c9665, + 0xf0e4350e7933dfc4, 0x7ccda2f30b8b03b6, 0xa0afc6179005de40, + 0xee65da6d063b3a30, 0xb9506f42f2bfe87a, 0xc9a2e26b0ef5baa0, + 0x39fa9d4f495011d6, 0xbecc21a45d023948, 0x6bf484c6593f737f, + 0x8065e0070cadc3b7, 0x9ef617ed8d419799, 0xac692cf8c233dd15, + 0xd2ed87583c4ebb98, 0xad95ba1bebfedc62, 0x9b60b160a8264e43, + 0x0bc8c45f71fcf25b, 0x4a78035cdf1c9931, 0x4602dc106667e029, + 0xb335a3c250498ac8, 0x0256ebc4df20cab8, 0x0c61efd153f0c8d9, + 0xe5d0150a4f806f88, 0x99d6521d351e7d87, 0x8d4888c9f80f4325, + 0x106c5735c1ba868d, 0x73414881b880a878, 0x808a9a58a3064751, + 0x339a29f3746de3d5, 0x5410d7fa4f873896, 0xd84623c81d7b8a03, + 0x1f7c7e7a7f47f462, + }; + + pcg64_2018_engine engine(0); +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + printf("0x%016lx, ", engine()); + if (i % 3 == 2) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } + engine.seed(); + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +TEST(PCG642018EngineTest, VerifyGoldenSeeded) { + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0xb03988f1e39691ee, 0xbd2a1eb5ac31e97a, 0x8f00d6d433634d02, + 0x1823c28d483d5776, 0x000c3ee3e1aeb74a, 0xfa82ef27a4f3df9c, + 0xc6f382308654e454, 0x414afb1a238996c2, 0x4703a4bc252eb411, + 0x99d64f62c8f7f654, 0xbb07ebe11a34fa44, 0x79eb06a363c06131, + 0xf66ad3756f1c6b21, 0x130c01d5e869f457, 0x5ca2b9963aecbc81, + 0xfef7bebc1de27e6c, 0x1d174faa5ed2cdbf, 0xd75b7a773f2bb889, + 0xc35c872327a170a5, 0x46da6d88646a42fe, 0x4622985e0442dae2, + 0xbe3cbd67297f1f9b, 0xe7c37b4a4798bfd1, 0x173d5dfad15a25c3, + 0x0eb6849ba2961522, 0xb0ff7246e6700d73, 0x88cb9c42d3afa577, + 0xb609731dbd94d917, 0xd3941cda04b40081, 0x28d140f7409bea3a, + 0x3c96699a920a124a, 0xdb28be521958b2fd, 0x0a3f44db3d4c5124, + 0x7ac8e60ba13b70d2, 0x75f03a41ded5195a, 0xaed10ac7c4e4825d, + 0xb92a3b18aadb7adc, 0xda45e0081f2bca46, 0x74d39ab3753143fc, + 0xb686038018fac9ca, 0x4cc309fe99542dbb, 0xf3e1a4fcb311097c, + 0x58763d6fa698d69d, 0xd11c365dbecd8d60, 0x2c15d55725b1dee7, + 0x89805f254d85658c, 0x2374c44dfc62158b, 0x9a8350fa7995328d, + 0x198f838970cf91da, 0x96aff569562c0e53, 0xd76c8c52b7ec6e3f, + 0x23a01cd9ae4baa81, 0x3adb366b6d02a893, 0xb3313e2a4c5b333f, + 0x04c11230b96a5425, 0x1f7f7af04787d571, 0xaddb019365275ec7, + 0x5c960468ccb09f42, 0x8438db698c69a44a, 0x492be1e46111637e, + 0x9c6c01e18100c610, 0xbfe48e75b7d0aceb, 0xb5e0b89ec1ce6a00, + 0x9d280ecbc2fe8997, 0x290d9e991ba5fcab, 0xeec5bec7d9d2a4f0, + 0x726e81488f19150e, 0x1a6df7955a7e462c, 0x37a12d174ba46bb5, + 0x3cdcdffd96b1b5c5, 0x2c5d5ac10661a26e, 0xa742ed18f22e50c4, + 0x00e0ed88ff0d8a35, 0x3d3c1718cb1efc0b, 0x1d70c51ffbccbf11, + 0xfbbb895132a4092f, 0x619d27f2fb095f24, 0x69af68200985e5c4, + 0xbee4885f57373f8d, 0x10b7a6bfe0587e40, 0xa885e6cf2f7e5f0a, + 0x59f879464f767550, 0x24e805d69056990d, 0x860970b911095891, + 0xca3189954f84170d, 0x6652a5edd4590134, 0x5e1008cef76174bf, + 0xcbd417881f2bcfe5, 0xfd49fc9d706ecd17, 0xeebf540221ebd066, + 0x46af7679464504cb, 0xd4028486946956f1, 0xd4f41864b86c2103, + 0x7af090e751583372, 0x98cdaa09278cb642, 0xffd42b921215602f, + 0x1d05bec8466b1740, 0xf036fa78a0132044, 0x787880589d1ecc78, + 0x5644552cfef33230, 0x0a97e275fe06884b, 0x96d1b13333d470b5, + 0xc8b3cdad52d3b034, 0x091357b9db7376fd, 0xa5fe4232555edf8c, + 0x3371bc3b6ada76b5, 0x7deeb2300477c995, 0x6fc6d4244f2849c1, + 0x750e8cc797ca340a, 0x81728613cd79899f, 0x3467f4ee6f9aeb93, + 0x5ef0a905f58c640f, 0x432db85e5101c98a, 0x6488e96f46ac80c2, + 0x22fddb282625048c, 0x15b287a0bc2d4c5d, 0xa7e2343ef1f28bce, + 0xc87ee1aa89bed09e, 0x220610107812c5e9, 0xcbdab6fcd640f586, + 0x8d41047970928784, 0x1aa431509ec1ade0, 0xac3f0be53f518ddc, + 0x16f4428ad81d0cbb, 0x675b13c2736fc4bb, 0x6db073afdd87e32d, + 0x572f3ca2f1a078c6, + }; + + ExplicitSeedSeq seed_sequence{12, 34, 56}; + pcg64_2018_engine engine(seed_sequence); +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + printf("0x%016lx, ", engine()); + if (i % 3 == 2) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } + engine.seed(seed_sequence); + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +TEST(PCG642018EngineTest, VerifyGoldenFromDeserializedEngine) { + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0xdd425b47b4113dea, 0x1b07176479d444b0, 0x6b391027586f2e42, + 0xa166f2b15f4a2143, 0xffb6dbd7a179ee97, 0xb2c00035365bf0b1, + 0x8fbb518b45855521, 0xfc789a55ddf87c3b, 0x429531f0f17ff355, + 0xbe708560d603d283, 0x5bff415175c5cb6b, 0xe813491f4ad45394, + 0xa853f4506d55880d, 0x7e538453e568172e, 0xe101f1e098ddd0ec, + 0x6ee31266ee4c766d, 0xa8786d92d66b39d7, 0xfee622a2acf5e5b0, + 0x5fe8e82c102fa7b3, 0x01f10be4cdb53c9d, 0xbe0545366f857022, + 0x12e74f010a339bca, 0xb10d85ca40d5ce34, 0xe80d6feba5054875, + 0x2b7c1ee6d567d4ee, 0x2a9cd043bfd03b66, 0x5cfc531bd239f3f1, + 0x1c4734e4647d70f5, 0x85a8f60f006b5760, 0x6a4239ce76dca387, + 0x8da0f86d7339335c, 0xf055b0468551374d, 0x486e8567e9bea9a0, + 0x4cb531b8405192dd, 0xf813b1ee3157110b, 0x214c2a664a875d8e, + 0x74531237b29b35f7, 0xa6f0267bb77a771e, 0x64b552bff54184a4, + 0xa2d6f7af2d75b6fc, 0x460a10018e03b5ab, 0x76fd1fdcb81d0800, + 0x76f5f81805070d9d, 0x1fb75cb1a70b289a, 0x9dfd25a022c4b27f, + 0x9a31a14a80528e9e, 0x910dc565ddc25820, 0xd6aef8e2b0936c10, + 0xe1773c507fe70225, 0xe027fd7aadd632bc, 0xc1fecb427089c8b8, + 0xb5c74c69fa9dbf26, 0x71bf9b0e4670227d, 0x25f48fad205dcfdd, + 0x905248ec4d689c56, 0x5c2b7631b0de5c9d, 0x9f2ee0f8f485036c, + 0xfd6ce4ebb90bf7ea, 0xd435d20046085574, 0x6b7eadcb0625f986, + 0x679d7d44b48be89e, 0x49683b8e1cdc49de, 0x4366cf76e9a2f4ca, + 0x54026ec1cdad7bed, 0xa9a04385207f28d3, 0xc8e66de4eba074b2, + 0x40b08c42de0f4cc0, 0x1d4c5e0e93c5bbc0, 0x19b80792e470ae2d, + 0x6fcaaeaa4c2a5bd9, 0xa92cb07c4238438e, 0x8bb5c918a007e298, + 0x7cd671e944874cf4, 0x88166470b1ba3cac, 0xd013d476eaeeade6, + 0xcee416947189b3c3, 0x5d7c16ab0dce6088, 0xd3578a5c32b13d27, + 0x3875db5adc9cc973, 0xfbdaba01c5b5dc56, 0xffc4fdd391b231c3, + 0x2334520ecb164fec, 0x361c115e7b6de1fa, 0xeee58106cc3563d7, + 0x8b7f35a8db25ebb8, 0xb29d00211e2cafa6, 0x22a39fe4614b646b, + 0x92ca6de8b998506d, 0x40922fe3d388d1db, 0x9da47f1e540f802a, + 0x811dceebf16a25db, 0xf6524ae22e0e53a9, 0x52d9e780a16eb99d, + 0x4f504286bb830207, 0xf6654d4786bd5cc3, 0x00bd98316003a7e1, + 0xefda054a6ab8f5f3, 0x46cfb0f4c1872827, 0xc22b316965c0f3b2, + 0xd1a28087c7e7562a, 0xaa4f6a094b7f5cff, 0xfe2bc853a041f7da, + 0xe9d531402a83c3ba, 0xe545d8663d3ce4dd, 0xfa2dcd7d91a13fa8, + 0xda1a080e52a127b8, 0x19c98f1f809c3d84, 0x2cef109af4678c88, + 0x53462accab3b9132, 0x176b13a80415394e, 0xea70047ef6bc178b, + 0x57bca80506d6dcdf, 0xd853ba09ff09f5c4, 0x75f4df3a7ddd4775, + 0x209c367ade62f4fe, 0xa9a0bbc74d5f4682, 0x5dfe34bada86c21a, + 0xc2c05bbcd38566d1, 0x6de8088e348c916a, 0x6a7001c6000c2196, + 0xd9fb51865fc4a367, 0x12f320e444ece8ff, 0x6d56f7f793d65035, + 0x138f31b7a865f8aa, 0x58fc68b4026b9adf, 0xcd48954b79fb6436, + 0x27dfce4a0232af87, + }; + +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + std::seed_seq seed_sequence{1, 2, 3}; + pcg64_2018_engine engine(seed_sequence); + std::ostringstream stream; + stream << engine; + auto str = stream.str(); + printf("%s\n\n", str.c_str()); + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + printf("0x%016lx, ", engine()); + if (i % 3 == 2) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + pcg64_2018_engine engine; + std::istringstream stream( + "2549297995355413924 4865540595714422341 6364136223846793005 " + "1442695040888963407 18088519957565336995 4845369368158826708"); + stream >> engine; + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +// ------------------------------------------------------------------ +// Stability tests for pcg32_2018_engine +// ------------------------------------------------------------------ +TEST(PCG322018EngineTest, VerifyGolden) { + constexpr uint32_t kGolden[kNumGoldenOutputs] = { + 0x7a7ecbd9, 0x89fd6c06, 0xae646aa8, 0xcd3cf945, 0x6204b303, 0x198c8585, + 0x49fce611, 0xd1e9297a, 0x142d9440, 0xee75f56b, 0x473a9117, 0xe3a45903, + 0xbce807a1, 0xe54e5f4d, 0x497d6c51, 0x61829166, 0xa740474b, 0x031912a8, + 0x9de3defa, 0xd266dbf1, 0x0f38bebb, 0xec3c4f65, 0x07c5057d, 0xbbce03c8, + 0xfd2ac7a8, 0xffcf4773, 0x5b10affb, 0xede1c842, 0xe22b01b7, 0xda133c8c, + 0xaf89b0f4, 0x25d1b8bc, 0x9f625482, 0x7bfd6882, 0x2e2210c0, 0x2c8fb9a6, + 0x42cb3b83, 0x40ce0dab, 0x644a3510, 0x36230ef2, 0xe2cb6d43, 0x1012b343, + 0x746c6c9f, 0x36714cf8, 0xed1f5026, 0x8bbbf83e, 0xe98710f4, 0x8a2afa36, + 0x09035349, 0x6dc1a487, 0x682b634b, 0xc106794f, 0x7dd78beb, 0x628c262b, + 0x852fb232, 0xb153ac4c, 0x4f169d1b, 0xa69ab774, 0x4bd4b6f2, 0xdc351dd3, + 0x93ff3c8c, 0xa30819ab, 0xff07758c, 0x5ab13c62, 0xd16d7fb5, 0xc4950ffa, + 0xd309ae49, 0xb9677a87, 0x4464e317, 0x90dc44f1, 0xc694c1d4, 0x1d5e1168, + 0xadf37a2d, 0xda38990d, 0x1ec4bd33, 0x36ca25ce, 0xfa0dc76a, 0x968a9d43, + 0x6950ac39, 0xdd3276bc, 0x06d5a71e, 0x1f6f282d, 0x5c626c62, 0xdde3fc31, + 0x152194ce, 0xc35ed14c, 0xb1f7224e, 0x47f76bb8, 0xb34fdd08, 0x7011395e, + 0x162d2a49, 0x0d1bf09f, 0x9428a952, 0x03c5c344, 0xd3525616, 0x7816fff3, + 0x6bceb8a8, 0x8345a081, 0x366420fd, 0x182abeda, 0x70f82745, 0xaf15ded8, + 0xc7f52ca2, 0xa98db9c5, 0x919d99ba, 0x9c376c1c, 0xed8d34c2, 0x716ae9f5, + 0xef062fa5, 0xee3b6c56, 0x52325658, 0x61afa9c3, 0xfdaf02f0, 0x961cf3ab, + 0x9f291565, 0x4fbf3045, 0x0590c899, 0xde901385, 0x45005ffb, 0x509db162, + 0x262fa941, 0x4c421653, 0x4b17c21e, 0xea0d1530, 0xde803845, 0x61bfd515, + 0x438523ef, + }; + + pcg32_2018_engine engine(0); +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + printf("0x%08x, ", engine()); + if (i % 6 == 5) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } + engine.seed(); + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +TEST(PCG322018EngineTest, VerifyGoldenSeeded) { + constexpr uint32_t kGolden[kNumGoldenOutputs] = { + 0x60b5a64c, 0x978502f9, 0x80a75f60, 0x241f1158, 0xa4cd1dbb, 0xe7284017, + 0x3b678da5, 0x5223ec99, 0xe4bdd5d9, 0x72190e6d, 0xe6e702c9, 0xff80c768, + 0xcf126ed3, 0x1fbd20ab, 0x60980489, 0xbc72bf89, 0x407ac6c0, 0x00bf3c51, + 0xf9087897, 0x172e4eb6, 0xe9e4f443, 0x1a6098bf, 0xbf44f8c2, 0xdd84a0e5, + 0xd9a52364, 0xc0e2e786, 0x061ae2ba, 0x9facb8e3, 0x6109432d, 0xd4e0a013, + 0xbd8eb9a6, 0x7e86c3b6, 0x629c0e68, 0x05337430, 0xb495b9f4, 0x11ccd65d, + 0xb578db25, 0x66f1246d, 0x6ef20a7f, 0x5e429812, 0x11772130, 0xb944b5c2, + 0x01624128, 0xa2385ab7, 0xd3e10d35, 0xbe570ec3, 0xc951656f, 0xbe8944a0, + 0x7be41062, 0x5709f919, 0xd745feda, 0x9870b9ae, 0xb44b8168, 0x19e7683b, + 0xded8017f, 0xc6e4d544, 0x91ae4225, 0xd6745fba, 0xb992f284, 0x65b12b33, + 0xa9d5fdb4, 0xf105ce1a, 0x35ca1a6e, 0x2ff70dd0, 0xd8335e49, 0xfb71ddf2, + 0xcaeabb89, 0x5c6f5f84, 0x9a811a7d, 0xbcecbbd1, 0x0f661ba0, 0x9ad93b9d, + 0xedd23e0b, 0x42062f48, 0xd38dd7e4, 0x6cd63c9c, 0x640b98ae, 0x4bff5653, + 0x12626371, 0x13266017, 0xe7a698d8, 0x39c74667, 0xe8fdf2e3, 0x52803bf8, + 0x2af6895b, 0x91335b7b, 0x699e4961, 0x00a40fff, 0x253ff2b6, 0x4a6cf672, + 0x9584e85f, 0xf2a5000c, 0x4d58aba8, 0xb8513e6a, 0x767fad65, 0x8e326f9e, + 0x182f15a1, 0x163dab52, 0xdf99c780, 0x047282a1, 0xee4f90dd, 0xd50394ae, + 0x6c9fd5f0, 0xb06a9194, 0x387e3840, 0x04a9487b, 0xf678a4c2, 0xd0a78810, + 0xd502c97e, 0xd6a9b12a, 0x4accc5dc, 0x416ed53e, 0x50411536, 0xeeb89c24, + 0x813a7902, 0x034ebca6, 0xffa52e7c, 0x7ecd3d0e, 0xfa37a0d2, 0xb1fbe2c1, + 0xb7efc6d1, 0xefa4ccee, 0xf6f80424, 0x2283f3d9, 0x68732284, 0x94f3b5c8, + 0xbbdeceb9, + }; + + ExplicitSeedSeq seed_sequence{12, 34, 56}; + pcg32_2018_engine engine(seed_sequence); +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + printf("0x%08x, ", engine()); + if (i % 6 == 5) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } + engine.seed(seed_sequence); + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +TEST(PCG322018EngineTest, VerifyGoldenFromDeserializedEngine) { + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0x780f7042, 0xba137215, 0x43ab6f22, 0x0cb55f46, 0x44b2627d, 0x835597af, + 0xea973ea1, 0x0d2abd35, 0x4fdd601c, 0xac4342fe, 0x7db7e93c, 0xe56ebcaf, + 0x3596470a, 0x7770a9ad, 0x9b893320, 0x57db3415, 0xb432de54, 0xa02baf71, + 0xa256aadb, 0x88921fc7, 0xa35fa6b3, 0xde3eca46, 0x605739a7, 0xa890b82b, + 0xe457b7ad, 0x335fb903, 0xeb06790c, 0xb3c54bf6, 0x6141e442, 0xa599a482, + 0xb78987cc, 0xc61dfe9d, 0x0f1d6ace, 0x17460594, 0x8f6a5061, 0x083dc354, + 0xe9c337fb, 0xcfd105f7, 0x926764b6, 0x638d24dc, 0xeaac650a, 0x67d2cb9c, + 0xd807733c, 0x205fc52e, 0xf5399e2e, 0x6c46ddcc, 0xb603e875, 0xce113a25, + 0x3c8d4813, 0xfb584db8, 0xf6d255ff, 0xea80954f, 0x42e8be85, 0xb2feee72, + 0x62bd8d16, 0x1be4a142, 0x97dca1a4, 0xdd6e7333, 0xb2caa20e, 0xa12b1588, + 0xeb3a5a1a, 0x6fa5ba89, 0x077ea931, 0x8ddb1713, 0x0dd03079, 0x2c2ba965, + 0xa77fac17, 0xc8325742, 0x8bb893bf, 0xc2315741, 0xeaceee92, 0x81dd2ee2, + 0xe5214216, 0x1b9b8fb2, 0x01646d03, 0x24facc25, 0xd8c0e0bb, 0xa33fe106, + 0xf34fe976, 0xb3b4b44e, 0x65618fed, 0x032c6192, 0xa9dd72ce, 0xf391887b, + 0xf41c6a6e, 0x05c4bd6d, 0x37fa260e, 0x46b05659, 0xb5f6348a, 0x62d26d89, + 0x39f6452d, 0xb17b30a2, 0xbdd82743, 0x38ecae3b, 0xfe90f0a2, 0xcb2d226d, + 0xcf8a0b1c, 0x0eed3d4d, 0xa1f69cfc, 0xd7ac3ba5, 0xce9d9a6b, 0x121deb4c, + 0x4a0d03f3, 0xc1821ed1, 0x59c249ac, 0xc0abb474, 0x28149985, 0xfd9a82ba, + 0x5960c3b2, 0xeff00cba, 0x6073aa17, 0x25dc0919, 0x9976626e, 0xdd2ccc33, + 0x39ecb6ec, 0xc6e15d13, 0xfac94cfd, 0x28cfd34f, 0xf2d2c32d, 0x51c23d08, + 0x4fdb2f48, 0x97baa807, 0xf2c1004c, 0xc4ae8136, 0x71f31c94, 0x8c92d601, + 0x36caf5cd, + }; + +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + std::seed_seq seed_sequence{1, 2, 3}; + pcg32_2018_engine engine(seed_sequence); + std::ostringstream stream; + stream << engine; + auto str = stream.str(); + printf("%s\n\n", str.c_str()); + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + printf("0x%08x, ", engine()); + if (i % 6 == 5) { + printf("\n"); + } + } + printf("\n\n\n"); + + EXPECT_FALSE(true); +#else + pcg32_2018_engine engine; + std::istringstream stream( + "6364136223846793005 1442695040888963407 6537028157270659894"); + stream >> engine; + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +} // namespace diff --git a/absl/random/internal/platform.h b/absl/random/internal/platform.h new file mode 100644 index 00000000..a5a42cbb --- /dev/null +++ b/absl/random/internal/platform.h @@ -0,0 +1,170 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_PLATFORM_H_ +#define ABSL_RANDOM_INTERNAL_PLATFORM_H_ + +// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate +// symbols from arbitrary system and other headers, since it may be built +// with different flags from other targets, using different levels of +// optimization, potentially introducing ODR violations. + +// ----------------------------------------------------------------------------- +// Platform Feature Checks +// ----------------------------------------------------------------------------- + +// Currently supported operating systems and associated preprocessor +// symbols: +// +// Linux and Linux-derived __linux__ +// Android __ANDROID__ (implies __linux__) +// Linux (non-Android) __linux__ && !__ANDROID__ +// Darwin (macOS and iOS) __APPLE__ +// Akaros (http://akaros.org) __ros__ +// Windows _WIN32 +// NaCL __native_client__ +// AsmJS __asmjs__ +// WebAssembly __wasm__ +// Fuchsia __Fuchsia__ +// +// Note that since Android defines both __ANDROID__ and __linux__, one +// may probe for either Linux or Android by simply testing for __linux__. +// +// NOTE: For __APPLE__ platforms, we use #include <TargetConditionals.h> +// to distinguish os variants. +// +// http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system + +#if defined(__APPLE__) +#include <TargetConditionals.h> +#endif + +// ----------------------------------------------------------------------------- +// Architecture Checks +// ----------------------------------------------------------------------------- + +// These preprocessor directives are trying to determine CPU architecture, +// including necessary headers to support hardware AES. +// +// ABSL_ARCH_{X86/PPC/ARM} macros determine the platform. +#if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || \ + defined(_M_X64) +#define ABSL_ARCH_X86_64 +#elif defined(__i386) || defined(_M_IX86) +#define ABSL_ARCH_X86_32 +#elif defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) +#define ABSL_ARCH_AARCH64 +#elif defined(__arm__) || defined(__ARMEL__) || defined(_M_ARM) +#define ABSL_ARCH_ARM +#elif defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ + defined(__ppc__) || defined(__PPC__) +#define ABSL_ARCH_PPC +#else +// Unsupported architecture. +// * https://sourceforge.net/p/predef/wiki/Architectures/ +// * https://msdn.microsoft.com/en-us/library/b0084kay.aspx +// * for gcc, clang: "echo | gcc -E -dM -" +#endif + +// ----------------------------------------------------------------------------- +// Attribute Checks +// ----------------------------------------------------------------------------- + +// ABSL_RANDOM_INTERNAL_RESTRICT annotates whether pointers may be considered +// to be unaliased. +#if defined(__clang__) || defined(__GNUC__) +#define ABSL_RANDOM_INTERNAL_RESTRICT __restrict__ +#elif defined(_MSC_VER) +#define ABSL_RANDOM_INTERNAL_RESTRICT __restrict +#else +#define ABSL_RANDOM_INTERNAL_RESTRICT +#endif + +// ABSL_HAVE_ACCELERATED_AES indicates whether the currently active compiler +// flags (e.g. -maes) allow using hardware accelerated AES instructions, which +// implies us assuming that the target platform supports them. +#define ABSL_HAVE_ACCELERATED_AES 0 + +#if defined(ABSL_ARCH_X86_64) + +#if defined(__AES__) || defined(__AVX__) +#undef ABSL_HAVE_ACCELERATED_AES +#define ABSL_HAVE_ACCELERATED_AES 1 +#endif + +#elif defined(ABSL_ARCH_PPC) + +// Rely on VSX and CRYPTO extensions for vcipher on PowerPC. +#if (defined(__VEC__) || defined(__ALTIVEC__)) && defined(__VSX__) && \ + defined(__CRYPTO__) +#undef ABSL_HAVE_ACCELERATED_AES +#define ABSL_HAVE_ACCELERATED_AES 1 +#endif + +#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64) + +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0053c/IHI0053C_acle_2_0.pdf +// Rely on NEON+CRYPTO extensions for ARM. +#if defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO) +#undef ABSL_HAVE_ACCELERATED_AES +#define ABSL_HAVE_ACCELERATED_AES 1 +#endif + +#endif + +// NaCl does not allow AES. +#if defined(__native_client__) +#undef ABSL_HAVE_ACCELERATED_AES +#define ABSL_HAVE_ACCELERATED_AES 0 +#endif + +// ABSL_RANDOM_INTERNAL_AES_DISPATCH indicates whether the currently active +// platform has, or should use run-time dispatch for selecting the +// acclerated Randen implementation. +#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0 + +#if defined(ABSL_ARCH_X86_64) +// Dispatch is available on x86_64 +#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH +#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1 +#elif defined(__linux__) && defined(ABSL_ARCH_PPC) +// Or when running linux PPC +#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH +#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1 +#elif defined(__linux__) && defined(ABSL_ARCH_AARCH64) +// Or when running linux AArch64 +#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH +#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1 +#elif defined(__linux__) && defined(ABSL_ARCH_ARM) && (__ARM_ARCH >= 8) +// Or when running linux ARM v8 or higher. +// (This captures a lot of Android configurations.) +#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH +#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 1 +#endif + +// NaCl does not allow dispatch. +#if defined(__native_client__) +#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH +#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0 +#endif + +// iOS does not support dispatch, even on x86, since applications +// should be bundled as fat binaries, with a different build tailored for +// each specific supported platform/architecture. +#if defined(__APPLE__) && (TARGET_OS_IPHONE || TARGET_OS_IPHONE_SIMULATOR) +#undef ABSL_RANDOM_INTERNAL_AES_DISPATCH +#define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0 +#endif + +#endif // ABSL_RANDOM_INTERNAL_PLATFORM_H_ diff --git a/absl/random/internal/pool_urbg.cc b/absl/random/internal/pool_urbg.cc new file mode 100644 index 00000000..304d9b16 --- /dev/null +++ b/absl/random/internal/pool_urbg.cc @@ -0,0 +1,254 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/pool_urbg.h" + +#include <algorithm> +#include <atomic> +#include <cstdint> +#include <cstring> +#include <iterator> + +#include "absl/base/attributes.h" +#include "absl/base/call_once.h" +#include "absl/base/config.h" +#include "absl/base/internal/endian.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/spinlock.h" +#include "absl/base/internal/sysinfo.h" +#include "absl/base/internal/unaligned_access.h" +#include "absl/base/optimization.h" +#include "absl/random/internal/randen.h" +#include "absl/random/internal/seed_material.h" +#include "absl/random/seed_gen_exception.h" + +using absl::base_internal::SpinLock; +using absl::base_internal::SpinLockHolder; + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { +namespace { + +// RandenPoolEntry is a thread-safe pseudorandom bit generator, implementing a +// single generator within a RandenPool<T>. It is an internal implementation +// detail, and does not aim to conform to [rand.req.urng]. +// +// NOTE: There are alignment issues when used on ARM, for instance. +// See the allocation code in PoolAlignedAlloc(). +class RandenPoolEntry { + public: + static constexpr size_t kState = RandenTraits::kStateBytes / sizeof(uint32_t); + static constexpr size_t kCapacity = + RandenTraits::kCapacityBytes / sizeof(uint32_t); + + void Init(absl::Span<const uint32_t> data) { + SpinLockHolder l(&mu_); // Always uncontested. + std::copy(data.begin(), data.end(), std::begin(state_)); + next_ = kState; + } + + // Copy bytes into out. + void Fill(uint8_t* out, size_t bytes) LOCKS_EXCLUDED(mu_); + + // Returns random bits from the buffer in units of T. + template <typename T> + inline T Generate() LOCKS_EXCLUDED(mu_); + + inline void MaybeRefill() EXCLUSIVE_LOCKS_REQUIRED(mu_) { + if (next_ >= kState) { + next_ = kCapacity; + impl_.Generate(state_); + } + } + + private: + // Randen URBG state. + uint32_t state_[kState] GUARDED_BY(mu_); // First to satisfy alignment. + SpinLock mu_; + const Randen impl_; + size_t next_ GUARDED_BY(mu_); +}; + +template <> +inline uint8_t RandenPoolEntry::Generate<uint8_t>() { + SpinLockHolder l(&mu_); + MaybeRefill(); + return static_cast<uint8_t>(state_[next_++]); +} + +template <> +inline uint16_t RandenPoolEntry::Generate<uint16_t>() { + SpinLockHolder l(&mu_); + MaybeRefill(); + return static_cast<uint16_t>(state_[next_++]); +} + +template <> +inline uint32_t RandenPoolEntry::Generate<uint32_t>() { + SpinLockHolder l(&mu_); + MaybeRefill(); + return state_[next_++]; +} + +template <> +inline uint64_t RandenPoolEntry::Generate<uint64_t>() { + SpinLockHolder l(&mu_); + if (next_ >= kState - 1) { + next_ = kCapacity; + impl_.Generate(state_); + } + auto p = state_ + next_; + next_ += 2; + + uint64_t result; + std::memcpy(&result, p, sizeof(result)); + return result; +} + +void RandenPoolEntry::Fill(uint8_t* out, size_t bytes) { + SpinLockHolder l(&mu_); + while (bytes > 0) { + MaybeRefill(); + size_t remaining = (kState - next_) * sizeof(state_[0]); + size_t to_copy = std::min(bytes, remaining); + std::memcpy(out, &state_[next_], to_copy); + out += to_copy; + bytes -= to_copy; + next_ += (to_copy + sizeof(state_[0]) - 1) / sizeof(state_[0]); + } +} + +// Number of pooled urbg entries. +static constexpr int kPoolSize = 8; + +// Shared pool entries. +static absl::once_flag pool_once; +ABSL_CACHELINE_ALIGNED static RandenPoolEntry* shared_pools[kPoolSize]; + +// Returns an id in the range [0 ... kPoolSize), which indexes into the +// pool of random engines. +// +// Each thread to access the pool is assigned a sequential ID (without reuse) +// from the pool-id space; the id is cached in a thread_local variable. +// This id is assigned based on the arrival-order of the thread to the +// GetPoolID call; this has no binary, CL, or runtime stability because +// on subsequent runs the order within the same program may be significantly +// different. However, as other thread IDs are not assigned sequentially, +// this is not expected to matter. +int GetPoolID() { + static_assert(kPoolSize >= 1, + "At least one urbg instance is required for PoolURBG"); + + ABSL_CONST_INIT static std::atomic<int64_t> sequence{0}; + +#ifdef ABSL_HAVE_THREAD_LOCAL + static thread_local int my_pool_id = -1; + if (ABSL_PREDICT_FALSE(my_pool_id < 0)) { + my_pool_id = (sequence++ % kPoolSize); + } + return my_pool_id; +#else + static pthread_key_t tid_key = [] { + pthread_key_t tmp_key; + int err = pthread_key_create(&tmp_key, nullptr); + if (err) { + ABSL_RAW_LOG(FATAL, "pthread_key_create failed with %d", err); + } + return tmp_key; + }(); + + // Store the value in the pthread_{get/set}specific. However an uninitialized + // value is 0, so add +1 to distinguish from the null value. + intptr_t my_pool_id = + reinterpret_cast<intptr_t>(pthread_getspecific(tid_key)); + if (ABSL_PREDICT_FALSE(my_pool_id == 0)) { + // No allocated ID, allocate the next value, cache it, and return. + my_pool_id = (sequence++ % kPoolSize) + 1; + int err = pthread_setspecific(tid_key, reinterpret_cast<void*>(my_pool_id)); + if (err) { + ABSL_RAW_LOG(FATAL, "pthread_setspecific failed with %d", err); + } + } + return my_pool_id - 1; +#endif +} + +// Allocate a RandenPoolEntry with at least 32-byte alignment, which is required +// by ARM platform code. +RandenPoolEntry* PoolAlignedAlloc() { + constexpr size_t kAlignment = + ABSL_CACHELINE_SIZE > 32 ? ABSL_CACHELINE_SIZE : 32; + + // Not all the platforms that we build for have std::aligned_alloc, however + // since we never free these objects, we can over allocate and munge the + // pointers to the correct alignment. + void* memory = std::malloc(sizeof(RandenPoolEntry) + kAlignment); + auto x = reinterpret_cast<intptr_t>(memory); + auto y = x % kAlignment; + void* aligned = + (y == 0) ? memory : reinterpret_cast<void*>(x + kAlignment - y); + return new (aligned) RandenPoolEntry(); +} + +// Allocate and initialize kPoolSize objects of type RandenPoolEntry. +// +// The initialization strategy is to initialize one object directly from +// OS entropy, then to use that object to seed all of the individual +// pool instances. +void InitPoolURBG() { + static constexpr size_t kSeedSize = + RandenTraits::kStateBytes / sizeof(uint32_t); + // Read the seed data from OS entropy once. + uint32_t seed_material[kPoolSize * kSeedSize]; + if (!random_internal::ReadSeedMaterialFromOSEntropy( + absl::MakeSpan(seed_material))) { + random_internal::ThrowSeedGenException(); + } + for (int i = 0; i < kPoolSize; i++) { + shared_pools[i] = PoolAlignedAlloc(); + shared_pools[i]->Init( + absl::MakeSpan(&seed_material[i * kSeedSize], kSeedSize)); + } +} + +// Returns the pool entry for the current thread. +RandenPoolEntry* GetPoolForCurrentThread() { + absl::call_once(pool_once, InitPoolURBG); + return shared_pools[GetPoolID()]; +} + +} // namespace + +template <typename T> +typename RandenPool<T>::result_type RandenPool<T>::Generate() { + auto* pool = GetPoolForCurrentThread(); + return pool->Generate<T>(); +} + +template <typename T> +void RandenPool<T>::Fill(absl::Span<result_type> data) { + auto* pool = GetPoolForCurrentThread(); + pool->Fill(reinterpret_cast<uint8_t*>(data.data()), + data.size() * sizeof(result_type)); +} + +template class RandenPool<uint8_t>; +template class RandenPool<uint16_t>; +template class RandenPool<uint32_t>; +template class RandenPool<uint64_t>; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/internal/pool_urbg.h b/absl/random/internal/pool_urbg.h new file mode 100644 index 00000000..eac75e2c --- /dev/null +++ b/absl/random/internal/pool_urbg.h @@ -0,0 +1,131 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_POOL_URBG_H_ +#define ABSL_RANDOM_INTERNAL_POOL_URBG_H_ + +#include <cinttypes> +#include <limits> + +#include "absl/random/internal/traits.h" +#include "absl/types/span.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// RandenPool is a thread-safe random number generator [random.req.urbg] that +// uses an underlying pool of Randen generators to generate values. Each thread +// has affinity to one instance of the underlying pool generators. Concurrent +// access is guarded by a spin-lock. +template <typename T> +class RandenPool { + public: + using result_type = T; + static_assert(std::is_unsigned<result_type>::value, + "RandenPool template argument must be a built-in unsigned " + "integer type"); + + static constexpr result_type(min)() { + return (std::numeric_limits<result_type>::min)(); + } + + static constexpr result_type(max)() { + return (std::numeric_limits<result_type>::max)(); + } + + RandenPool() {} + + // Returns a single value. + inline result_type operator()() { return Generate(); } + + // Fill data with random values. + static void Fill(absl::Span<result_type> data); + + protected: + // Generate returns a single value. + static result_type Generate(); +}; + +extern template class RandenPool<uint8_t>; +extern template class RandenPool<uint16_t>; +extern template class RandenPool<uint32_t>; +extern template class RandenPool<uint64_t>; + +// PoolURBG uses an underlying pool of random generators to implement a +// thread-compatible [random.req.urbg] interface with an internal cache of +// values. +template <typename T, size_t kBufferSize> +class PoolURBG { + // Inheritance to access the protected static members of RandenPool. + using unsigned_type = typename make_unsigned_bits<T>::type; + using PoolType = RandenPool<unsigned_type>; + using SpanType = absl::Span<unsigned_type>; + + static constexpr size_t kInitialBuffer = kBufferSize + 1; + static constexpr size_t kHalfBuffer = kBufferSize / 2; + + public: + using result_type = T; + + static_assert(std::is_unsigned<result_type>::value, + "PoolURBG must be parameterized by an unsigned integer type"); + + static_assert(kBufferSize > 1, + "PoolURBG must be parameterized by a buffer-size > 1"); + + static_assert(kBufferSize <= 256, + "PoolURBG must be parameterized by a buffer-size <= 256"); + + static constexpr result_type(min)() { + return (std::numeric_limits<result_type>::min)(); + } + + static constexpr result_type(max)() { + return (std::numeric_limits<result_type>::max)(); + } + + PoolURBG() : next_(kInitialBuffer) {} + + // copy-constructor does not copy cache. + PoolURBG(const PoolURBG&) : next_(kInitialBuffer) {} + const PoolURBG& operator=(const PoolURBG&) { + next_ = kInitialBuffer; + return *this; + } + + // move-constructor does move cache. + PoolURBG(PoolURBG&&) = default; + PoolURBG& operator=(PoolURBG&&) = default; + + inline result_type operator()() { + if (next_ >= kBufferSize) { + next_ = (kBufferSize > 2 && next_ > kBufferSize) ? kHalfBuffer : 0; + PoolType::Fill(SpanType(reinterpret_cast<unsigned_type*>(state_ + next_), + kBufferSize - next_)); + } + return state_[next_++]; + } + + private: + // Buffer size. + size_t next_; // index within state_ + result_type state_[kBufferSize]; +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_POOL_URBG_H_ diff --git a/absl/random/internal/pool_urbg_test.cc b/absl/random/internal/pool_urbg_test.cc new file mode 100644 index 00000000..53f4eacf --- /dev/null +++ b/absl/random/internal/pool_urbg_test.cc @@ -0,0 +1,182 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/pool_urbg.h" + +#include <algorithm> +#include <bitset> +#include <cmath> +#include <cstdint> +#include <iterator> + +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" +#include "absl/types/span.h" + +using absl::random_internal::PoolURBG; +using absl::random_internal::RandenPool; + +namespace { + +// is_randen_pool trait is true when parameterized by an RandenPool +template <typename T> +using is_randen_pool = typename absl::disjunction< // + std::is_same<T, RandenPool<uint8_t>>, // + std::is_same<T, RandenPool<uint16_t>>, // + std::is_same<T, RandenPool<uint32_t>>, // + std::is_same<T, RandenPool<uint64_t>>>; // + +// MyFill either calls RandenPool::Fill() or std::generate(..., rng) +template <typename T, typename V> +typename absl::enable_if_t<absl::negation<is_randen_pool<T>>::value, void> // +MyFill(T& rng, absl::Span<V> data) { // NOLINT(runtime/references) + std::generate(std::begin(data), std::end(data), rng); +} + +template <typename T, typename V> +typename absl::enable_if_t<is_randen_pool<T>::value, void> // +MyFill(T& rng, absl::Span<V> data) { // NOLINT(runtime/references) + rng.Fill(data); +} + +template <typename EngineType> +class PoolURBGTypedTest : public ::testing::Test {}; + +using EngineTypes = ::testing::Types< // + RandenPool<uint8_t>, // + RandenPool<uint16_t>, // + RandenPool<uint32_t>, // + RandenPool<uint64_t>, // + PoolURBG<uint8_t, 2>, // + PoolURBG<uint16_t, 2>, // + PoolURBG<uint32_t, 2>, // + PoolURBG<uint64_t, 2>, // + PoolURBG<unsigned int, 8>, // NOLINT(runtime/int) + PoolURBG<unsigned long, 8>, // NOLINT(runtime/int) + PoolURBG<unsigned long int, 4>, // NOLINT(runtime/int) + PoolURBG<unsigned long long, 4>>; // NOLINT(runtime/int) + +TYPED_TEST_SUITE(PoolURBGTypedTest, EngineTypes); + +// This test is checks that the engines meet the URBG interface requirements +// defined in [rand.req.urbg]. +TYPED_TEST(PoolURBGTypedTest, URBGInterface) { + using E = TypeParam; + using T = typename E::result_type; + + static_assert(std::is_copy_constructible<E>::value, + "engine must be copy constructible"); + + static_assert(absl::is_copy_assignable<E>::value, + "engine must be copy assignable"); + + E e; + const E x; + + e(); + + static_assert(std::is_same<decltype(e()), T>::value, + "return type of operator() must be result_type"); + + E u0(x); + u0(); + + E u1 = e; + u1(); +} + +// This validates that sequences are independent. +TYPED_TEST(PoolURBGTypedTest, VerifySequences) { + using E = TypeParam; + using result_type = typename E::result_type; + + E rng; + (void)rng(); // Discard one value. + + constexpr int kNumOutputs = 64; + result_type a[kNumOutputs]; + result_type b[kNumOutputs]; + std::fill(std::begin(b), std::end(b), 0); + + // Fill a using Fill or generate, depending on the engine type. + { + E x = rng; + MyFill(x, absl::MakeSpan(a)); + } + + // Fill b using std::generate(). + { + E x = rng; + std::generate(std::begin(b), std::end(b), x); + } + + // Test that generated sequence changed as sequence of bits, i.e. if about + // half of the bites were flipped between two non-correlated values. + size_t changed_bits = 0; + size_t unchanged_bits = 0; + size_t total_set = 0; + size_t total_bits = 0; + size_t equal_count = 0; + for (size_t i = 0; i < kNumOutputs; ++i) { + equal_count += (a[i] == b[i]) ? 1 : 0; + std::bitset<sizeof(result_type) * 8> bitset(a[i] ^ b[i]); + changed_bits += bitset.count(); + unchanged_bits += bitset.size() - bitset.count(); + + std::bitset<sizeof(result_type) * 8> a_set(a[i]); + std::bitset<sizeof(result_type) * 8> b_set(b[i]); + total_set += a_set.count() + b_set.count(); + total_bits += 2 * 8 * sizeof(result_type); + } + // On average, half the bits are changed between two calls. + EXPECT_LE(changed_bits, 0.60 * (changed_bits + unchanged_bits)); + EXPECT_GE(changed_bits, 0.40 * (changed_bits + unchanged_bits)); + + // verify using a quick normal-approximation to the binomial. + EXPECT_NEAR(total_set, total_bits * 0.5, 4 * std::sqrt(total_bits)) + << "@" << total_set / static_cast<double>(total_bits); + + // Also, A[i] == B[i] with probability (1/range) * N. + // Give this a pretty wide latitude, though. + const double kExpected = kNumOutputs / (1.0 * sizeof(result_type) * 8); + EXPECT_LE(equal_count, 1.0 + kExpected); +} + +} // namespace + +/* +$ nanobenchmarks 1 RandenPool construct +$ nanobenchmarks 1 PoolURBG construct + +RandenPool<uint32_t> | 1 | 1000 | 48482.00 ticks | 48.48 ticks | 13.9 ns +RandenPool<uint32_t> | 10 | 2000 | 1028795.00 ticks | 51.44 ticks | 14.7 ns +RandenPool<uint32_t> | 100 | 1000 | 5119968.00 ticks | 51.20 ticks | 14.6 ns +RandenPool<uint32_t> | 1000 | 500 | 25867936.00 ticks | 51.74 ticks | 14.8 ns + +RandenPool<uint64_t> | 1 | 1000 | 49921.00 ticks | 49.92 ticks | 14.3 ns +RandenPool<uint64_t> | 10 | 2000 | 1208269.00 ticks | 60.41 ticks | 17.3 ns +RandenPool<uint64_t> | 100 | 1000 | 5844955.00 ticks | 58.45 ticks | 16.7 ns +RandenPool<uint64_t> | 1000 | 500 | 28767404.00 ticks | 57.53 ticks | 16.4 ns + +PoolURBG<uint32_t,8> | 1 | 1000 | 86431.00 ticks | 86.43 ticks | 24.7 ns +PoolURBG<uint32_t,8> | 10 | 1000 | 206191.00 ticks | 20.62 ticks | 5.9 ns +PoolURBG<uint32_t,8> | 100 | 1000 | 1516049.00 ticks | 15.16 ticks | 4.3 ns +PoolURBG<uint32_t,8> | 1000 | 500 | 7613936.00 ticks | 15.23 ticks | 4.4 ns + +PoolURBG<uint64_t,4> | 1 | 1000 | 96668.00 ticks | 96.67 ticks | 27.6 ns +PoolURBG<uint64_t,4> | 10 | 1000 | 282423.00 ticks | 28.24 ticks | 8.1 ns +PoolURBG<uint64_t,4> | 100 | 1000 | 2609587.00 ticks | 26.10 ticks | 7.5 ns +PoolURBG<uint64_t,4> | 1000 | 500 | 12408757.00 ticks | 24.82 ticks | 7.1 ns + +*/ diff --git a/absl/random/internal/randen-keys.inc b/absl/random/internal/randen-keys.inc new file mode 100644 index 00000000..fa4b1668 --- /dev/null +++ b/absl/random/internal/randen-keys.inc @@ -0,0 +1,207 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_RANDEN_KEYS_INC_ +#define ABSL_RANDOM_INTERNAL_RANDEN_KEYS_INC_ + +// Textual header to include the randen_keys where necessary. +// REQUIRES: struct u64x2{} +// +// PROVIDES: kKeys +// PROVIDES: round_keys[] + +// "Nothing up my sleeve" numbers from the first hex digits of Pi, obtained +// from http://hexpi.sourceforge.net/. The array was generated by following +// Python script: +/* +python << EOF +"""Generates Randen round keys array from pi-hex.62500.txt file.""" +import binascii + +KEYS = 136 + +def chunks(l, n): + """Yield successive n-sized chunks from l.""" + for i in range(0, len(l), n): + yield l[i:i + n] + +def pairwise(t): + """Transforms sequence into sequence of pairs.""" + it = iter(t) + return zip(it,it) + +def digits_from_pi(): + """Reads digits from hexpi.sourceforge.net file.""" + with open("pi-hex.62500.txt") as file: + return file.read() + +def digits_from_urandom(): + """Reads digits from /dev/urandom.""" + with open("/dev/urandom") as file: + return binascii.hexlify(file.read(KEYS * 16)) + +digits = digits_from_pi() +print("static constexpr const size_t kRoundKeys = {0};\n".format(KEYS)) +print("alignas(16) constexpr const u64x2 round_keys[kRoundKeys] = {") + +for i, (hi, lo) in zip(range(KEYS), pairwise(chunks(digits, 16))): + hi = "0x{0}ull".format(hi) + lo = "0x{0}ull".format(lo) + print(" u64x2({0}, {1}){2}".format(hi, lo, ',' if i+1 < KEYS else '')) + +print("};") +EOF +*/ + +static constexpr const size_t kRoundKeys = 136; + +alignas(16) constexpr u64x2 round_keys[kRoundKeys] = { + u64x2(0x243F6A8885A308D3ull, 0x13198A2E03707344ull), + u64x2(0xA4093822299F31D0ull, 0x082EFA98EC4E6C89ull), + u64x2(0x452821E638D01377ull, 0xBE5466CF34E90C6Cull), + u64x2(0xC0AC29B7C97C50DDull, 0x3F84D5B5B5470917ull), + u64x2(0x9216D5D98979FB1Bull, 0xD1310BA698DFB5ACull), + u64x2(0x2FFD72DBD01ADFB7ull, 0xB8E1AFED6A267E96ull), + u64x2(0xBA7C9045F12C7F99ull, 0x24A19947B3916CF7ull), + u64x2(0x0801F2E2858EFC16ull, 0x636920D871574E69ull), + u64x2(0xA458FEA3F4933D7Eull, 0x0D95748F728EB658ull), + u64x2(0x718BCD5882154AEEull, 0x7B54A41DC25A59B5ull), + u64x2(0x9C30D5392AF26013ull, 0xC5D1B023286085F0ull), + u64x2(0xCA417918B8DB38EFull, 0x8E79DCB0603A180Eull), + u64x2(0x6C9E0E8BB01E8A3Eull, 0xD71577C1BD314B27ull), + u64x2(0x78AF2FDA55605C60ull, 0xE65525F3AA55AB94ull), + u64x2(0x5748986263E81440ull, 0x55CA396A2AAB10B6ull), + u64x2(0xB4CC5C341141E8CEull, 0xA15486AF7C72E993ull), + u64x2(0xB3EE1411636FBC2Aull, 0x2BA9C55D741831F6ull), + u64x2(0xCE5C3E169B87931Eull, 0xAFD6BA336C24CF5Cull), + u64x2(0x7A32538128958677ull, 0x3B8F48986B4BB9AFull), + u64x2(0xC4BFE81B66282193ull, 0x61D809CCFB21A991ull), + u64x2(0x487CAC605DEC8032ull, 0xEF845D5DE98575B1ull), + u64x2(0xDC262302EB651B88ull, 0x23893E81D396ACC5ull), + u64x2(0x0F6D6FF383F44239ull, 0x2E0B4482A4842004ull), + u64x2(0x69C8F04A9E1F9B5Eull, 0x21C66842F6E96C9Aull), + u64x2(0x670C9C61ABD388F0ull, 0x6A51A0D2D8542F68ull), + u64x2(0x960FA728AB5133A3ull, 0x6EEF0B6C137A3BE4ull), + u64x2(0xBA3BF0507EFB2A98ull, 0xA1F1651D39AF0176ull), + u64x2(0x66CA593E82430E88ull, 0x8CEE8619456F9FB4ull), + u64x2(0x7D84A5C33B8B5EBEull, 0xE06F75D885C12073ull), + u64x2(0x401A449F56C16AA6ull, 0x4ED3AA62363F7706ull), + u64x2(0x1BFEDF72429B023Dull, 0x37D0D724D00A1248ull), + u64x2(0xDB0FEAD349F1C09Bull, 0x075372C980991B7Bull), + u64x2(0x25D479D8F6E8DEF7ull, 0xE3FE501AB6794C3Bull), + u64x2(0x976CE0BD04C006BAull, 0xC1A94FB6409F60C4ull), + u64x2(0x5E5C9EC2196A2463ull, 0x68FB6FAF3E6C53B5ull), + u64x2(0x1339B2EB3B52EC6Full, 0x6DFC511F9B30952Cull), + u64x2(0xCC814544AF5EBD09ull, 0xBEE3D004DE334AFDull), + u64x2(0x660F2807192E4BB3ull, 0xC0CBA85745C8740Full), + u64x2(0xD20B5F39B9D3FBDBull, 0x5579C0BD1A60320Aull), + u64x2(0xD6A100C6402C7279ull, 0x679F25FEFB1FA3CCull), + u64x2(0x8EA5E9F8DB3222F8ull, 0x3C7516DFFD616B15ull), + u64x2(0x2F501EC8AD0552ABull, 0x323DB5FAFD238760ull), + u64x2(0x53317B483E00DF82ull, 0x9E5C57BBCA6F8CA0ull), + u64x2(0x1A87562EDF1769DBull, 0xD542A8F6287EFFC3ull), + u64x2(0xAC6732C68C4F5573ull, 0x695B27B0BBCA58C8ull), + u64x2(0xE1FFA35DB8F011A0ull, 0x10FA3D98FD2183B8ull), + u64x2(0x4AFCB56C2DD1D35Bull, 0x9A53E479B6F84565ull), + u64x2(0xD28E49BC4BFB9790ull, 0xE1DDF2DAA4CB7E33ull), + u64x2(0x62FB1341CEE4C6E8ull, 0xEF20CADA36774C01ull), + u64x2(0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull), + u64x2(0xEAAD8E716B93D5A0ull, 0xD08ED1D0AFC725E0ull), + u64x2(0x8E3C5B2F8E7594B7ull, 0x8FF6E2FBF2122B64ull), + u64x2(0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull), + u64x2(0xD1CFF191B3A8C1ADull, 0x2F2F2218BE0E1777ull), + u64x2(0xEA752DFE8B021FA1ull, 0xE5A0CC0FB56F74E8ull), + u64x2(0x18ACF3D6CE89E299ull, 0xB4A84FE0FD13E0B7ull), + u64x2(0x7CC43B81D2ADA8D9ull, 0x165FA26680957705ull), + u64x2(0x93CC7314211A1477ull, 0xE6AD206577B5FA86ull), + u64x2(0xC75442F5FB9D35CFull, 0xEBCDAF0C7B3E89A0ull), + u64x2(0xD6411BD3AE1E7E49ull, 0x00250E2D2071B35Eull), + u64x2(0x226800BB57B8E0AFull, 0x2464369BF009B91Eull), + u64x2(0x5563911D59DFA6AAull, 0x78C14389D95A537Full), + u64x2(0x207D5BA202E5B9C5ull, 0x832603766295CFA9ull), + u64x2(0x11C819684E734A41ull, 0xB3472DCA7B14A94Aull), + u64x2(0x1B5100529A532915ull, 0xD60F573FBC9BC6E4ull), + u64x2(0x2B60A47681E67400ull, 0x08BA6FB5571BE91Full), + u64x2(0xF296EC6B2A0DD915ull, 0xB6636521E7B9F9B6ull), + u64x2(0xFF34052EC5855664ull, 0x53B02D5DA99F8FA1ull), + u64x2(0x08BA47996E85076Aull, 0x4B7A70E9B5B32944ull), + u64x2(0xDB75092EC4192623ull, 0xAD6EA6B049A7DF7Dull), + u64x2(0x9CEE60B88FEDB266ull, 0xECAA8C71699A18FFull), + u64x2(0x5664526CC2B19EE1ull, 0x193602A575094C29ull), + u64x2(0xA0591340E4183A3Eull, 0x3F54989A5B429D65ull), + u64x2(0x6B8FE4D699F73FD6ull, 0xA1D29C07EFE830F5ull), + u64x2(0x4D2D38E6F0255DC1ull, 0x4CDD20868470EB26ull), + u64x2(0x6382E9C6021ECC5Eull, 0x09686B3F3EBAEFC9ull), + u64x2(0x3C9718146B6A70A1ull, 0x687F358452A0E286ull), + u64x2(0xB79C5305AA500737ull, 0x3E07841C7FDEAE5Cull), + u64x2(0x8E7D44EC5716F2B8ull, 0xB03ADA37F0500C0Dull), + u64x2(0xF01C1F040200B3FFull, 0xAE0CF51A3CB574B2ull), + u64x2(0x25837A58DC0921BDull, 0xD19113F97CA92FF6ull), + u64x2(0x9432477322F54701ull, 0x3AE5E58137C2DADCull), + u64x2(0xC8B576349AF3DDA7ull, 0xA94461460FD0030Eull), + u64x2(0xECC8C73EA4751E41ull, 0xE238CD993BEA0E2Full), + u64x2(0x3280BBA1183EB331ull, 0x4E548B384F6DB908ull), + u64x2(0x6F420D03F60A04BFull, 0x2CB8129024977C79ull), + u64x2(0x5679B072BCAF89AFull, 0xDE9A771FD9930810ull), + u64x2(0xB38BAE12DCCF3F2Eull, 0x5512721F2E6B7124ull), + u64x2(0x501ADDE69F84CD87ull, 0x7A5847187408DA17ull), + u64x2(0xBC9F9ABCE94B7D8Cull, 0xEC7AEC3ADB851DFAull), + u64x2(0x63094366C464C3D2ull, 0xEF1C18473215D808ull), + u64x2(0xDD433B3724C2BA16ull, 0x12A14D432A65C451ull), + u64x2(0x50940002133AE4DDull, 0x71DFF89E10314E55ull), + u64x2(0x81AC77D65F11199Bull, 0x043556F1D7A3C76Bull), + u64x2(0x3C11183B5924A509ull, 0xF28FE6ED97F1FBFAull), + u64x2(0x9EBABF2C1E153C6Eull, 0x86E34570EAE96FB1ull), + u64x2(0x860E5E0A5A3E2AB3ull, 0x771FE71C4E3D06FAull), + u64x2(0x2965DCB999E71D0Full, 0x803E89D65266C825ull), + u64x2(0x2E4CC9789C10B36Aull, 0xC6150EBA94E2EA78ull), + u64x2(0xA6FC3C531E0A2DF4ull, 0xF2F74EA7361D2B3Dull), + u64x2(0x1939260F19C27960ull, 0x5223A708F71312B6ull), + u64x2(0xEBADFE6EEAC31F66ull, 0xE3BC4595A67BC883ull), + u64x2(0xB17F37D1018CFF28ull, 0xC332DDEFBE6C5AA5ull), + u64x2(0x6558218568AB9702ull, 0xEECEA50FDB2F953Bull), + u64x2(0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull), + u64x2(0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull), + u64x2(0x0334FE1EAA0363CFull, 0xB5735C904C70A239ull), + u64x2(0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull), + u64x2(0x9CAB5CABB2F3846Eull, 0x648B1EAF19BDF0CAull), + u64x2(0xA02369B9655ABB50ull, 0x40685A323C2AB4B3ull), + u64x2(0x319EE9D5C021B8F7ull, 0x9B540B19875FA099ull), + u64x2(0x95F7997E623D7DA8ull, 0xF837889A97E32D77ull), + u64x2(0x11ED935F16681281ull, 0x0E358829C7E61FD6ull), + u64x2(0x96DEDFA17858BA99ull, 0x57F584A51B227263ull), + u64x2(0x9B83C3FF1AC24696ull, 0xCDB30AEB532E3054ull), + u64x2(0x8FD948E46DBC3128ull, 0x58EBF2EF34C6FFEAull), + u64x2(0xFE28ED61EE7C3C73ull, 0x5D4A14D9E864B7E3ull), + u64x2(0x42105D14203E13E0ull, 0x45EEE2B6A3AAABEAull), + u64x2(0xDB6C4F15FACB4FD0ull, 0xC742F442EF6ABBB5ull), + u64x2(0x654F3B1D41CD2105ull, 0xD81E799E86854DC7ull), + u64x2(0xE44B476A3D816250ull, 0xCF62A1F25B8D2646ull), + u64x2(0xFC8883A0C1C7B6A3ull, 0x7F1524C369CB7492ull), + u64x2(0x47848A0B5692B285ull, 0x095BBF00AD19489Dull), + u64x2(0x1462B17423820D00ull, 0x58428D2A0C55F5EAull), + u64x2(0x1DADF43E233F7061ull, 0x3372F0928D937E41ull), + u64x2(0xD65FECF16C223BDBull, 0x7CDE3759CBEE7460ull), + u64x2(0x4085F2A7CE77326Eull, 0xA607808419F8509Eull), + u64x2(0xE8EFD85561D99735ull, 0xA969A7AAC50C06C2ull), + u64x2(0x5A04ABFC800BCADCull, 0x9E447A2EC3453484ull), + u64x2(0xFDD567050E1E9EC9ull, 0xDB73DBD3105588CDull), + u64x2(0x675FDA79E3674340ull, 0xC5C43465713E38D8ull), + u64x2(0x3D28F89EF16DFF20ull, 0x153E21E78FB03D4Aull), + u64x2(0xE6E39F2BDB83ADF7ull, 0xE93D5A68948140F7ull), + u64x2(0xF64C261C94692934ull, 0x411520F77602D4F7ull), + u64x2(0xBCF46B2ED4A10068ull, 0xD40824713320F46Aull), + u64x2(0x43B7D4B7500061AFull, 0x1E39F62E97244546ull)}; + +#endif // ABSL_RANDOM_INTERNAL_RANDEN_KEYS_INC_ diff --git a/absl/random/internal/randen.cc b/absl/random/internal/randen.cc new file mode 100644 index 00000000..3b087605 --- /dev/null +++ b/absl/random/internal/randen.cc @@ -0,0 +1,91 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/randen.h" + +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/randen_detect.h" + +// RANDen = RANDom generator or beetroots in Swiss German. +// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random +// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. +// +// High-level summary: +// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is +// a sponge-like random generator that requires a cryptographic permutation. +// It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by +// achieving backtracking resistance with only one Permute() per buffer. +// +// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round +// Function" constructs up to 1024-bit permutations using an improved +// Generalized Feistel network with 2-round AES-128 functions. This Feistel +// block shuffle achieves diffusion faster and is less vulnerable to +// sliced-biclique attacks than the Type-2 cyclic shuffle. +// +// 3) "Improving the Generalized Feistel" and "New criterion for diffusion +// property" extends the same kind of improved Feistel block shuffle to 16 +// branches, which enables a 2048-bit permutation. +// +// We combine these three ideas and also change Simpira's subround keys from +// structured/low-entropy counters to digits of Pi. + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { +namespace { + +struct RandenState { + const void* keys; + bool has_crypto; +}; + +RandenState GetRandenState() { + static const RandenState state = []() { + RandenState tmp; +#if ABSL_RANDOM_INTERNAL_AES_DISPATCH + // HW AES Dispatch. + if (HasRandenHwAesImplementation() && CPUSupportsRandenHwAes()) { + tmp.has_crypto = true; + tmp.keys = RandenHwAes::GetKeys(); + } else { + tmp.has_crypto = false; + tmp.keys = RandenSlow::GetKeys(); + } +#elif ABSL_HAVE_ACCELERATED_AES + // HW AES is enabled. + tmp.has_crypto = true; + tmp.keys = RandenHwAes::GetKeys(); +#else + // HW AES is disabled. + tmp.has_crypto = false; + tmp.keys = RandenSlow::GetKeys(); +#endif + return tmp; + }(); + return state; +} + +} // namespace + +Randen::Randen() { + auto tmp = GetRandenState(); + keys_ = tmp.keys; +#if ABSL_RANDOM_INTERNAL_AES_DISPATCH + has_crypto_ = tmp.has_crypto; +#endif +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/internal/randen.h b/absl/random/internal/randen.h new file mode 100644 index 00000000..ef375f90 --- /dev/null +++ b/absl/random/internal/randen.h @@ -0,0 +1,102 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_RANDEN_H_ +#define ABSL_RANDOM_INTERNAL_RANDEN_H_ + +#include <cstddef> + +#include "absl/random/internal/platform.h" +#include "absl/random/internal/randen_hwaes.h" +#include "absl/random/internal/randen_slow.h" +#include "absl/random/internal/randen_traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// RANDen = RANDom generator or beetroots in Swiss German. +// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random +// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. +// +// Randen implements the basic state manipulation methods. +class Randen { + public: + static constexpr size_t kStateBytes = RandenTraits::kStateBytes; + static constexpr size_t kCapacityBytes = RandenTraits::kCapacityBytes; + static constexpr size_t kSeedBytes = RandenTraits::kSeedBytes; + + ~Randen() = default; + + Randen(); + + // Generate updates the randen sponge. The outer portion of the sponge + // (kCapacityBytes .. kStateBytes) may be consumed as PRNG state. + template <typename T, size_t N> + void Generate(T (&state)[N]) const { + static_assert(N * sizeof(T) == kStateBytes, + "Randen::Generate() requires kStateBytes of state"); +#if ABSL_RANDOM_INTERNAL_AES_DISPATCH + // HW AES Dispatch. + if (has_crypto_) { + RandenHwAes::Generate(keys_, state); + } else { + RandenSlow::Generate(keys_, state); + } +#elif ABSL_HAVE_ACCELERATED_AES + // HW AES is enabled. + RandenHwAes::Generate(keys_, state); +#else + // HW AES is disabled. + RandenSlow::Generate(keys_, state); +#endif + } + + // Absorb incorporates additional seed material into the randen sponge. After + // absorb returns, Generate must be called before the state may be consumed. + template <typename S, size_t M, typename T, size_t N> + void Absorb(const S (&seed)[M], T (&state)[N]) const { + static_assert(M * sizeof(S) == RandenTraits::kSeedBytes, + "Randen::Absorb() requires kSeedBytes of seed"); + + static_assert(N * sizeof(T) == RandenTraits::kStateBytes, + "Randen::Absorb() requires kStateBytes of state"); +#if ABSL_RANDOM_INTERNAL_AES_DISPATCH + // HW AES Dispatch. + if (has_crypto_) { + RandenHwAes::Absorb(seed, state); + } else { + RandenSlow::Absorb(seed, state); + } +#elif ABSL_HAVE_ACCELERATED_AES + // HW AES is enabled. + RandenHwAes::Absorb(seed, state); +#else + // HW AES is disabled. + RandenSlow::Absorb(seed, state); +#endif + } + + private: + const void* keys_; +#if ABSL_RANDOM_INTERNAL_AES_DISPATCH + bool has_crypto_; +#endif +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_RANDEN_H_ diff --git a/absl/random/internal/randen_benchmarks.cc b/absl/random/internal/randen_benchmarks.cc new file mode 100644 index 00000000..f589172c --- /dev/null +++ b/absl/random/internal/randen_benchmarks.cc @@ -0,0 +1,174 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "absl/random/internal/randen.h" + +#include <cstdint> +#include <cstdio> +#include <cstring> + +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/nanobenchmark.h" +#include "absl/random/internal/platform.h" +#include "absl/random/internal/randen_engine.h" +#include "absl/random/internal/randen_hwaes.h" +#include "absl/random/internal/randen_slow.h" +#include "absl/strings/numbers.h" + +namespace { + +using absl::random_internal::Randen; +using absl::random_internal::RandenHwAes; +using absl::random_internal::RandenSlow; + +using absl::random_internal_nanobenchmark::FuncInput; +using absl::random_internal_nanobenchmark::FuncOutput; +using absl::random_internal_nanobenchmark::InvariantTicksPerSecond; +using absl::random_internal_nanobenchmark::MeasureClosure; +using absl::random_internal_nanobenchmark::Params; +using absl::random_internal_nanobenchmark::PinThreadToCPU; +using absl::random_internal_nanobenchmark::Result; + +// Local state parameters. +static constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t); +static constexpr size_t kSeedSizeT = Randen::kSeedBytes / sizeof(uint32_t); + +// Randen implementation benchmarks. +template <typename T> +struct AbsorbFn : public T { + mutable uint64_t state[kStateSizeT] = {}; + mutable uint32_t seed[kSeedSizeT] = {}; + + static constexpr size_t bytes() { return sizeof(seed); } + + FuncOutput operator()(const FuncInput num_iters) const { + for (size_t i = 0; i < num_iters; ++i) { + this->Absorb(seed, state); + } + return state[0]; + } +}; + +template <typename T> +struct GenerateFn : public T { + mutable uint64_t state[kStateSizeT]; + GenerateFn() { std::memset(state, 0, sizeof(state)); } + + static constexpr size_t bytes() { return sizeof(state); } + + FuncOutput operator()(const FuncInput num_iters) const { + const auto* keys = this->GetKeys(); + for (size_t i = 0; i < num_iters; ++i) { + this->Generate(keys, state); + } + return state[0]; + } +}; + +template <typename UInt> +struct Engine { + mutable absl::random_internal::randen_engine<UInt> rng; + + static constexpr size_t bytes() { return sizeof(UInt); } + + FuncOutput operator()(const FuncInput num_iters) const { + for (size_t i = 0; i < num_iters - 1; ++i) { + rng(); + } + return rng(); + } +}; + +template <size_t N> +void Print(const char* name, const size_t n, const Result (&results)[N], + const size_t bytes) { + if (n == 0) { + ABSL_RAW_LOG( + WARNING, + "WARNING: Measurement failed, should not happen when using " + "PinThreadToCPU unless the region to measure takes > 1 second.\n"); + return; + } + + static const double ns_per_tick = 1e9 / InvariantTicksPerSecond(); + static constexpr const double kNsPerS = 1e9; // ns/s + static constexpr const double kMBPerByte = 1.0 / 1048576.0; // Mb / b + static auto header = [] { + return printf("%20s %8s: %12s ticks; %9s (%9s) %8s\n", "Name", "Count", + "Total", "Variance", "Time", "bytes/s"); + }(); + (void)header; + + for (size_t i = 0; i < n; ++i) { + const double ticks_per_call = results[i].ticks / results[i].input; + const double ns_per_call = ns_per_tick * ticks_per_call; + const double bytes_per_ns = bytes / ns_per_call; + const double mb_per_s = bytes_per_ns * kNsPerS * kMBPerByte; + // Output + printf("%20s %8zu: %12.2f ticks; MAD=%4.2f%% (%6.1f ns) %8.1f Mb/s\n", + name, results[i].input, results[i].ticks, + results[i].variability * 100.0, ns_per_call, mb_per_s); + } +} + +// Fails here +template <typename Op, size_t N> +void Measure(const char* name, const FuncInput (&inputs)[N]) { + Op op; + + Result results[N]; + Params params; + params.verbose = false; + params.max_evals = 6; // avoid test timeout + const size_t num_results = MeasureClosure(op, inputs, N, results, params); + Print(name, num_results, results, op.bytes()); +} + +// unpredictable == 1 but the compiler does not know that. +void RunAll(const int argc, char* argv[]) { + if (argc == 2) { + int cpu = -1; + if (!absl::SimpleAtoi(argv[1], &cpu)) { + ABSL_RAW_LOG(FATAL, "The optional argument must be a CPU number >= 0.\n"); + } + PinThreadToCPU(cpu); + } + + // The compiler cannot reduce this to a constant. + const FuncInput unpredictable = (argc != 999); + static const FuncInput inputs[] = {unpredictable * 100, unpredictable * 1000}; + +#if !defined(ABSL_INTERNAL_DISABLE_AES) && ABSL_HAVE_ACCELERATED_AES + Measure<AbsorbFn<RandenHwAes>>("Absorb (HwAes)", inputs); +#endif + Measure<AbsorbFn<RandenSlow>>("Absorb (Slow)", inputs); + +#if !defined(ABSL_INTERNAL_DISABLE_AES) && ABSL_HAVE_ACCELERATED_AES + Measure<GenerateFn<RandenHwAes>>("Generate (HwAes)", inputs); +#endif + Measure<GenerateFn<RandenSlow>>("Generate (Slow)", inputs); + + // Measure the production engine. + static const FuncInput inputs1[] = {unpredictable * 1000, + unpredictable * 10000}; + Measure<Engine<uint64_t>>("randen_engine<uint64_t>", inputs1); + Measure<Engine<uint32_t>>("randen_engine<uint32_t>", inputs1); +} + +} // namespace + +int main(int argc, char* argv[]) { + RunAll(argc, argv); + return 0; +} diff --git a/absl/random/internal/randen_detect.cc b/absl/random/internal/randen_detect.cc new file mode 100644 index 00000000..610ae319 --- /dev/null +++ b/absl/random/internal/randen_detect.cc @@ -0,0 +1,221 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the"License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an"AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate +// symbols from arbitrary system and other headers, since it may be built +// with different flags from other targets, using different levels of +// optimization, potentially introducing ODR violations. + +#include "absl/random/internal/randen_detect.h" + +#include <cstdint> +#include <cstring> + +#include "absl/random/internal/platform.h" + +#if defined(ABSL_ARCH_X86_64) +#define ABSL_INTERNAL_USE_X86_CPUID +#elif defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \ + defined(ABSL_ARCH_AARCH64) +#if defined(__ANDROID__) +#define ABSL_INTERNAL_USE_ANDROID_GETAUXVAL +#define ABSL_INTERNAL_USE_GETAUXVAL +#elif defined(__linux__) +#define ABSL_INTERNAL_USE_LINUX_GETAUXVAL +#define ABSL_INTERNAL_USE_GETAUXVAL +#endif +#endif + +#if defined(ABSL_INTERNAL_USE_X86_CPUID) +#if defined(_WIN32) || defined(_WIN64) +#include <intrin.h> // NOLINT(build/include_order) +#pragma intrinsic(__cpuid) +#else +// MSVC-equivalent __cpuid intrinsic function. +static void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile("cpuid \n\t" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "a"(info_type), "c"(0)); +} +#endif +#endif // ABSL_INTERNAL_USE_X86_CPUID + +// On linux, just use the c-library getauxval call. +#if defined(ABSL_INTERNAL_USE_LINUX_GETAUXVAL) + +extern "C" unsigned long getauxval(unsigned long type); // NOLINT(runtime/int) + +static uint32_t GetAuxval(uint32_t hwcap_type) { + return static_cast<uint32_t>(getauxval(hwcap_type)); +} + +#endif + +// On android, probe the system's C library for getauxval(). +// This is the same technique used by the android NDK cpu features library +// as well as the google open-source cpu_features library. +// +// TODO(absl-team): Consider implementing a fallback of directly reading +// /proc/self/auxval. +#if defined(ABSL_INTERNAL_USE_ANDROID_GETAUXVAL) +#include <dlfcn.h> + +static uint32_t GetAuxval(uint32_t hwcap_type) { + // NOLINTNEXTLINE(runtime/int) + typedef unsigned long (*getauxval_func_t)(unsigned long); + + dlerror(); // Cleaning error state before calling dlopen. + void* libc_handle = dlopen("libc.so", RTLD_NOW); + if (!libc_handle) { + return 0; + } + uint32_t result = 0; + void* sym = dlsym(libc_handle, "getauxval"); + if (sym) { + getauxval_func_t func; + memcpy(&func, &sym, sizeof(func)); + result = static_cast<uint32_t>((*func)(hwcap_type)); + } + dlclose(libc_handle); + return result; +} + +#endif + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// The default return at the end of the function might be unreachable depending +// on the configuration. Ignore that warning. +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code-return" +#endif + +// CPUSupportsRandenHwAes returns whether the CPU is a microarchitecture +// which supports the crpyto/aes instructions or extensions necessary to use the +// accelerated RandenHwAes implementation. +// +// 1. For x86 it is sufficient to use the CPUID instruction to detect whether +// the cpu supports AES instructions. Done. +// +// Fon non-x86 it is much more complicated. +// +// 2. When ABSL_INTERNAL_USE_GETAUXVAL is defined, use getauxval() (either +// the direct c-library version, or the android probing version which loads +// libc), and read the hardware capability bits. +// This is based on the technique used by boringssl uses to detect +// cpu capabilities, and should allow us to enable crypto in the android +// builds where it is supported. +// +// 3. Use the default for the compiler architecture. +// + +bool CPUSupportsRandenHwAes() { +#if defined(ABSL_INTERNAL_USE_X86_CPUID) + // 1. For x86: Use CPUID to detect the required AES instruction set. + int regs[4]; + __cpuid(reinterpret_cast<int*>(regs), 1); + return regs[2] & (1 << 25); // AES + +#elif defined(ABSL_INTERNAL_USE_GETAUXVAL) + // 2. Use getauxval() to read the hardware bits and determine + // cpu capabilities. + +#define AT_HWCAP 16 +#define AT_HWCAP2 26 +#if defined(ABSL_ARCH_PPC) + // For Power / PPC: Expect that the cpu supports VCRYPTO + // See https://members.openpowerfoundation.org/document/dl/576 + // VCRYPTO should be present in POWER8 >= 2.07. + // Uses Linux kernel constants from arch/powerpc/include/uapi/asm/cputable.h + static const uint32_t kVCRYPTO = 0x02000000; + const uint32_t hwcap = GetAuxval(AT_HWCAP2); + return (hwcap & kVCRYPTO) != 0; + +#elif defined(ABSL_ARCH_ARM) + // For ARM: Require crypto+neon + // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500f/CIHBIBBA.html + // Uses Linux kernel constants from arch/arm64/include/asm/hwcap.h + static const uint32_t kNEON = 1 << 12; + uint32_t hwcap = GetAuxval(AT_HWCAP); + if ((hwcap & kNEON) == 0) { + return false; + } + + // And use it again to detect AES. + static const uint32_t kAES = 1 << 0; + const uint32_t hwcap2 = GetAuxval(AT_HWCAP2); + return (hwcap2 & kAES) != 0; + +#elif defined(ABSL_ARCH_AARCH64) + // For AARCH64: Require crypto+neon + // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0500f/CIHBIBBA.html + static const uint32_t kNEON = 1 << 1; + static const uint32_t kAES = 1 << 3; + const uint32_t hwcap = GetAuxval(AT_HWCAP); + return ((hwcap & kNEON) != 0) && ((hwcap & kAES) != 0); +#endif + +#else // ABSL_INTERNAL_USE_GETAUXVAL + // 3. By default, assume that the compiler default. + return ABSL_HAVE_ACCELERATED_AES ? true : false; + +#endif + // NOTE: There are some other techniques that may be worth trying: + // + // * Use an environment variable: ABSL_RANDOM_USE_HWAES + // + // * Rely on compiler-generated target-based dispatch. + // Using x86/gcc it might look something like this: + // + // int __attribute__((target("aes"))) HasAes() { return 1; } + // int __attribute__((target("default"))) HasAes() { return 0; } + // + // This does not work on all architecture/compiler combinations. + // + // * On Linux consider reading /proc/cpuinfo and/or /proc/self/auxv. + // These files have lines which are easy to parse; for ARM/AARCH64 it is quite + // easy to find the Features: line and extract aes / neon. Likewise for + // PPC. + // + // * Fork a process and test for SIGILL: + // + // * Many architectures have instructions to read the ISA. Unfortunately + // most of those require that the code is running in ring 0 / + // protected-mode. + // + // There are several examples. e.g. Valgrind detects PPC ISA 2.07: + // https://github.com/lu-zero/valgrind/blob/master/none/tests/ppc64/test_isa_2_07_part1.c + // + // MRS <Xt>, ID_AA64ISAR0_EL1 ; Read ID_AA64ISAR0_EL1 into Xt + // + // uint64_t val; + // __asm __volatile("mrs %0, id_aa64isar0_el1" :"=&r" (val)); + // + // * Use a CPUID-style heuristic database. + // + // * On Apple (__APPLE__), AES is available on Arm v8. + // https://stackoverflow.com/questions/45637888/how-to-determine-armv8-features-at-runtime-on-ios +} + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/internal/randen_detect.h b/absl/random/internal/randen_detect.h new file mode 100644 index 00000000..cb777550 --- /dev/null +++ b/absl/random/internal/randen_detect.h @@ -0,0 +1,31 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_ +#define ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_ + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// Returns whether the current CPU supports RandenHwAes implementation. +// This typically involves supporting cryptographic extensions on whichever +// platform is currently running. +bool CPUSupportsRandenHwAes(); + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_RANDEN_DETECT_H_ diff --git a/absl/random/internal/randen_engine.h b/absl/random/internal/randen_engine.h new file mode 100644 index 00000000..e721559e --- /dev/null +++ b/absl/random/internal/randen_engine.h @@ -0,0 +1,230 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_ +#define ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_ + +#include <algorithm> +#include <cinttypes> +#include <cstdlib> +#include <iostream> +#include <iterator> +#include <limits> +#include <type_traits> + +#include "absl/meta/type_traits.h" +#include "absl/random/internal/iostream_state_saver.h" +#include "absl/random/internal/randen.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// Deterministic pseudorandom byte generator with backtracking resistance +// (leaking the state does not compromise prior outputs). Based on Reverie +// (see "A Robust and Sponge-Like PRNG with Improved Efficiency") instantiated +// with an improved Simpira-like permutation. +// Returns values of type "T" (must be a built-in unsigned integer type). +// +// RANDen = RANDom generator or beetroots in Swiss High German. +// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random +// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. +template <typename T> +class alignas(16) randen_engine { + public: + // C++11 URBG interface: + using result_type = T; + static_assert(std::is_unsigned<result_type>::value, + "randen_engine template argument must be a built-in unsigned " + "integer type"); + + static constexpr result_type(min)() { + return (std::numeric_limits<result_type>::min)(); + } + + static constexpr result_type(max)() { + return (std::numeric_limits<result_type>::max)(); + } + + explicit randen_engine(result_type seed_value = 0) { seed(seed_value); } + + template <class SeedSequence, + typename = typename absl::enable_if_t< + !std::is_same<SeedSequence, randen_engine>::value>> + explicit randen_engine(SeedSequence&& seq) { + seed(seq); + } + + randen_engine(const randen_engine&) = default; + + // Returns random bits from the buffer in units of result_type. + result_type operator()() { + // Refill the buffer if needed (unlikely). + if (next_ >= kStateSizeT) { + next_ = kCapacityT; + impl_.Generate(state_); + } + + return state_[next_++]; + } + + template <class SeedSequence> + typename absl::enable_if_t< + !std::is_convertible<SeedSequence, result_type>::value> + seed(SeedSequence&& seq) { + // Zeroes the state. + seed(); + reseed(seq); + } + + void seed(result_type seed_value = 0) { + next_ = kStateSizeT; + // Zeroes the inner state and fills the outer state with seed_value to + // mimics behaviour of reseed + std::fill(std::begin(state_), std::begin(state_) + kCapacityT, 0); + std::fill(std::begin(state_) + kCapacityT, std::end(state_), seed_value); + } + + // Inserts entropy into (part of) the state. Calling this periodically with + // sufficient entropy ensures prediction resistance (attackers cannot predict + // future outputs even if state is compromised). + template <class SeedSequence> + void reseed(SeedSequence& seq) { + using sequence_result_type = typename SeedSequence::result_type; + static_assert(sizeof(sequence_result_type) == 4, + "SeedSequence::result_type must be 32-bit"); + + constexpr size_t kBufferSize = + Randen::kSeedBytes / sizeof(sequence_result_type); + alignas(16) sequence_result_type buffer[kBufferSize]; + + // Randen::Absorb XORs the seed into state, which is then mixed by a call + // to Randen::Generate. Seeding with only the provided entropy is preferred + // to using an arbitrary generate() call, so use [rand.req.seed_seq] + // size as a proxy for the number of entropy units that can be generated + // without relying on seed sequence mixing... + const size_t entropy_size = seq.size(); + if (entropy_size < kBufferSize) { + // ... and only request that many values, or 256-bits, when unspecified. + const size_t requested_entropy = (entropy_size == 0) ? 8u : entropy_size; + std::fill(std::begin(buffer) + requested_entropy, std::end(buffer), 0); + seq.generate(std::begin(buffer), std::begin(buffer) + requested_entropy); + // The Randen paper suggests preferentially initializing even-numbered + // 128-bit vectors of the randen state (there are 16 such vectors). + // The seed data is merged into the state offset by 128-bits, which + // implies prefering seed bytes [16..31, ..., 208..223]. Since the + // buffer is 32-bit values, we swap the corresponding buffer positions in + // 128-bit chunks. + size_t dst = kBufferSize; + while (dst > 7) { + // leave the odd bucket as-is. + dst -= 4; + size_t src = dst >> 1; + // swap 128-bits into the even bucket + std::swap(buffer[--dst], buffer[--src]); + std::swap(buffer[--dst], buffer[--src]); + std::swap(buffer[--dst], buffer[--src]); + std::swap(buffer[--dst], buffer[--src]); + } + } else { + seq.generate(std::begin(buffer), std::end(buffer)); + } + impl_.Absorb(buffer, state_); + + // Generate will be called when operator() is called + next_ = kStateSizeT; + } + + void discard(uint64_t count) { + uint64_t step = std::min<uint64_t>(kStateSizeT - next_, count); + count -= step; + + constexpr uint64_t kRateT = kStateSizeT - kCapacityT; + while (count > 0) { + next_ = kCapacityT; + impl_.Generate(state_); + step = std::min<uint64_t>(kRateT, count); + count -= step; + } + next_ += step; + } + + bool operator==(const randen_engine& other) const { + return next_ == other.next_ && + std::equal(std::begin(state_), std::end(state_), + std::begin(other.state_)); + } + + bool operator!=(const randen_engine& other) const { + return !(*this == other); + } + + template <class CharT, class Traits> + friend std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const randen_engine<T>& engine) { // NOLINT(runtime/references) + using numeric_type = + typename random_internal::stream_format_type<result_type>::type; + auto saver = random_internal::make_ostream_state_saver(os); + for (const auto& elem : engine.state_) { + // In the case that `elem` is `uint8_t`, it must be cast to something + // larger so that it prints as an integer rather than a character. For + // simplicity, apply the cast all circumstances. + os << static_cast<numeric_type>(elem) << os.fill(); + } + os << engine.next_; + return os; + } + + template <class CharT, class Traits> + friend std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + randen_engine<T>& engine) { // NOLINT(runtime/references) + using numeric_type = + typename random_internal::stream_format_type<result_type>::type; + result_type state[kStateSizeT]; + size_t next; + for (auto& elem : state) { + // It is not possible to read uint8_t from wide streams, so it is + // necessary to read a wider type and then cast it to uint8_t. + numeric_type value; + is >> value; + elem = static_cast<result_type>(value); + } + is >> next; + if (is.fail()) { + return is; + } + std::memcpy(engine.state_, state, sizeof(engine.state_)); + engine.next_ = next; + return is; + } + + private: + static constexpr size_t kStateSizeT = + Randen::kStateBytes / sizeof(result_type); + static constexpr size_t kCapacityT = + Randen::kCapacityBytes / sizeof(result_type); + + // First kCapacityT are `inner', the others are accessible random bits. + alignas(16) result_type state_[kStateSizeT]; + size_t next_; // index within state_ + Randen impl_; +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_RANDEN_ENGINE_H_ diff --git a/absl/random/internal/randen_engine_test.cc b/absl/random/internal/randen_engine_test.cc new file mode 100644 index 00000000..c8e7685b --- /dev/null +++ b/absl/random/internal/randen_engine_test.cc @@ -0,0 +1,656 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/randen_engine.h" + +#include <algorithm> +#include <bitset> +#include <random> +#include <sstream> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/explicit_seed_seq.h" +#include "absl/strings/str_cat.h" +#include "absl/time/clock.h" + +#define UPDATE_GOLDEN 0 + +using randen_u64 = absl::random_internal::randen_engine<uint64_t>; +using randen_u32 = absl::random_internal::randen_engine<uint32_t>; +using absl::random_internal::ExplicitSeedSeq; + +namespace { + +template <typename UIntType> +class RandenEngineTypedTest : public ::testing::Test {}; + +using UIntTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t>; + +TYPED_TEST_SUITE(RandenEngineTypedTest, UIntTypes); + +TYPED_TEST(RandenEngineTypedTest, VerifyReseedChangesAllValues) { + using randen = typename absl::random_internal::randen_engine<TypeParam>; + using result_type = typename randen::result_type; + + const size_t kNumOutputs = (sizeof(randen) * 2 / sizeof(TypeParam)) + 1; + randen engine; + + // MSVC emits error 2719 without the use of std::ref below. + // * formal parameter with __declspec(align('#')) won't be aligned + + { + std::seed_seq seq1{1, 2, 3, 4, 5, 6, 7}; + engine.seed(seq1); + } + result_type a[kNumOutputs]; + std::generate(std::begin(a), std::end(a), std::ref(engine)); + + { + std::random_device rd; + std::seed_seq seq2{rd(), rd(), rd()}; + engine.seed(seq2); + } + result_type b[kNumOutputs]; + std::generate(std::begin(b), std::end(b), std::ref(engine)); + + // Test that generated sequence changed as sequence of bits, i.e. if about + // half of the bites were flipped between two non-correlated values. + size_t changed_bits = 0; + size_t unchanged_bits = 0; + size_t total_set = 0; + size_t total_bits = 0; + size_t equal_count = 0; + for (size_t i = 0; i < kNumOutputs; ++i) { + equal_count += (a[i] == b[i]) ? 1 : 0; + std::bitset<sizeof(result_type) * 8> bitset(a[i] ^ b[i]); + changed_bits += bitset.count(); + unchanged_bits += bitset.size() - bitset.count(); + + std::bitset<sizeof(result_type) * 8> a_set(a[i]); + std::bitset<sizeof(result_type) * 8> b_set(b[i]); + total_set += a_set.count() + b_set.count(); + total_bits += 2 * 8 * sizeof(result_type); + } + // On average, half the bits are changed between two calls. + EXPECT_LE(changed_bits, 0.60 * (changed_bits + unchanged_bits)); + EXPECT_GE(changed_bits, 0.40 * (changed_bits + unchanged_bits)); + + // Verify using a quick normal-approximation to the binomial. + EXPECT_NEAR(total_set, total_bits * 0.5, 4 * std::sqrt(total_bits)) + << "@" << total_set / static_cast<double>(total_bits); + + // Also, A[i] == B[i] with probability (1/range) * N. + // Give this a pretty wide latitude, though. + const double kExpected = kNumOutputs / (1.0 * sizeof(result_type) * 8); + EXPECT_LE(equal_count, 1.0 + kExpected); +} + +// Number of values that needs to be consumed to clean two sizes of buffer +// and trigger third refresh. (slightly overestimates the actual state size). +constexpr size_t kTwoBufferValues = sizeof(randen_u64) / sizeof(uint16_t) + 1; + +TYPED_TEST(RandenEngineTypedTest, VerifyDiscard) { + using randen = typename absl::random_internal::randen_engine<TypeParam>; + + for (size_t num_used = 0; num_used < kTwoBufferValues; ++num_used) { + randen engine_used; + for (size_t i = 0; i < num_used; ++i) { + engine_used(); + } + + for (size_t num_discard = 0; num_discard < kTwoBufferValues; + ++num_discard) { + randen engine1 = engine_used; + randen engine2 = engine_used; + for (size_t i = 0; i < num_discard; ++i) { + engine1(); + } + engine2.discard(num_discard); + for (size_t i = 0; i < kTwoBufferValues; ++i) { + const auto r1 = engine1(); + const auto r2 = engine2(); + ASSERT_EQ(r1, r2) << "used=" << num_used << " discard=" << num_discard; + } + } + } +} + +TYPED_TEST(RandenEngineTypedTest, StreamOperatorsResult) { + using randen = typename absl::random_internal::randen_engine<TypeParam>; + std::wostringstream os; + std::wistringstream is; + randen engine; + + EXPECT_EQ(&(os << engine), &os); + EXPECT_EQ(&(is >> engine), &is); +} + +TYPED_TEST(RandenEngineTypedTest, StreamSerialization) { + using randen = typename absl::random_internal::randen_engine<TypeParam>; + + for (size_t discard = 0; discard < kTwoBufferValues; ++discard) { + ExplicitSeedSeq seed_sequence{12, 34, 56}; + randen engine(seed_sequence); + engine.discard(discard); + + std::stringstream stream; + stream << engine; + + randen new_engine; + stream >> new_engine; + for (size_t i = 0; i < 64; ++i) { + EXPECT_EQ(engine(), new_engine()) << " " << i; + } + } +} + +constexpr size_t kNumGoldenOutputs = 127; + +// This test is checking if randen_engine is meets interface requirements +// defined in [rand.req.urbg]. +TYPED_TEST(RandenEngineTypedTest, RandomNumberEngineInterface) { + using randen = typename absl::random_internal::randen_engine<TypeParam>; + + using E = randen; + using T = typename E::result_type; + + static_assert(std::is_copy_constructible<E>::value, + "randen_engine must be copy constructible"); + + static_assert(absl::is_copy_assignable<E>::value, + "randen_engine must be copy assignable"); + + static_assert(std::is_move_constructible<E>::value, + "randen_engine must be move constructible"); + + static_assert(absl::is_move_assignable<E>::value, + "randen_engine must be move assignable"); + + static_assert(std::is_same<decltype(std::declval<E>()()), T>::value, + "return type of operator() must be result_type"); + + // Names after definition of [rand.req.urbg] in C++ standard. + // e us a value of E + // v is a lvalue of E + // x, y are possibly const values of E + // s is a value of T + // q is a value satisfying requirements of seed_sequence + // z is a value of type unsigned long long + // os is a some specialization of basic_ostream + // is is a some specialization of basic_istream + + E e, v; + const E x, y; + T s = 1; + std::seed_seq q{1, 2, 3}; + unsigned long long z = 1; // NOLINT(runtime/int) + std::wostringstream os; + std::wistringstream is; + + E{}; + E{x}; + E{s}; + E{q}; + + e.seed(); + + // MSVC emits error 2718 when using EXPECT_EQ(e, x) + // * actual parameter with __declspec(align('#')) won't be aligned + EXPECT_TRUE(e == x); + + e.seed(q); + { + E tmp(q); + EXPECT_TRUE(e == tmp); + } + + e(); + { + E tmp(q); + EXPECT_TRUE(e != tmp); + } + + e.discard(z); + + static_assert(std::is_same<decltype(x == y), bool>::value, + "return type of operator== must be bool"); + + static_assert(std::is_same<decltype(x != y), bool>::value, + "return type of operator== must be bool"); +} + +TYPED_TEST(RandenEngineTypedTest, RandenEngineSFINAETest) { + using randen = typename absl::random_internal::randen_engine<TypeParam>; + using result_type = typename randen::result_type; + + { + randen engine(result_type(1)); + engine.seed(result_type(1)); + } + + { + result_type n = 1; + randen engine(n); + engine.seed(n); + } + + { + randen engine(1); + engine.seed(1); + } + + { + int n = 1; + randen engine(n); + engine.seed(n); + } + + { + std::seed_seq seed_seq; + randen engine(seed_seq); + engine.seed(seed_seq); + } + + { + randen engine{std::seed_seq()}; + engine.seed(std::seed_seq()); + } +} + +TEST(RandenTest, VerifyGoldenRanden64Default) { + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0xc3c14f134e433977, 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, + 0xf0b780f545c72912, 0x15dbb1d37696599f, 0x30ec63baff3c6d59, + 0xb29f73606f7f20a6, 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, + 0x9cbf605e3fd9de8a, 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, + 0xf4b327fe0fc73c37, 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, + 0xd5af05dd3eff9556, 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, + 0x58d3575834956d42, 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, + 0x497cabf3431154fc, 0x4e24370570029a8b, 0xd88b5749f090e5ea, + 0xc651a582a970692f, 0x78fcec2cbb6342f5, 0x463cb745612f55db, + 0x352ee4ad1816afe3, 0x026ff374c101da7e, 0x811ef0821c3de851, + 0x6f7e616704c4fa59, 0xa0660379992d58fc, 0x04b0a374a3b795c7, + 0x915f3445685da798, 0x26802a8ac76571ce, 0x4663352533ce1882, + 0xb9fdefb4a24dc738, 0x5588ba3a4d6e6c51, 0xa2101a42d35f1956, + 0x607195a5e200f5fd, 0x7e100308f3290764, 0xe1e5e03c759c0709, + 0x082572cc5da6606f, 0xcbcf585399e432f1, 0xe8a2be4f8335d8f1, + 0x0904469acbfee8f2, 0xf08bd31b6daecd51, 0x08e8a1f1a69da69a, + 0x6542a20aad57bff5, 0x2e9705bb053d6b46, 0xda2fc9db0713c391, + 0x78e3a810213b6ffb, 0xdc16a59cdd85f8a6, 0xc0932718cd55781f, + 0xb9bfb29c2b20bfe5, 0xb97289c1be0f2f9c, 0xc0a2a0e403a892d4, + 0x5524bb834771435b, 0x8265da3d39d1a750, 0xff4af3ab8d1b78c5, + 0xf0ec5f424bcad77f, 0x66e455f627495189, 0xc82d3120b57e3270, + 0x3424e47dc22596e3, 0xbc0c95129ccedcdd, 0xc191c595afc4dcbf, + 0x120392bd2bb70939, 0x7f90650ea6cd6ab4, 0x7287491832695ad3, + 0xa7c8fac5a7917eb0, 0xd088cb9418be0361, 0x7c1bf9839c7c1ce5, + 0xe2e991fa58e1e79e, 0x78565cdefd28c4ad, 0x7351b9fef98bafad, + 0x2a9eac28b08c96bf, 0x6c4f179696cb2225, 0x13a685861bab87e0, + 0x64c6de5aa0501971, 0x30537425cac70991, 0x01590d9dc6c532b7, + 0x7e05e3aa8ec720dc, 0x74a07d9c54e3e63f, 0x738184388f3bc1d2, + 0x26ffdc5067be3acb, 0x6bcdf185561f255f, 0xa0eaf2e1cf99b1c6, + 0x171df81934f68604, 0x7ea5a21665683e5a, 0x5d1cb02075ba1cea, + 0x957f38cbd2123fdf, 0xba6364eff80de02f, 0x606e0a0e41d452ee, + 0x892d8317de82f7a2, 0xe707b1db50f7b43e, 0x4eb28826766fcf5b, + 0x5a362d56e80a0951, 0x6ee217df16527d78, 0xf6737962ba6b23dd, + 0x443e63857d4076ca, 0x790d9a5f048adfeb, 0xd796b052151ee94d, + 0x033ed95c12b04a03, 0x8b833ff84893da5d, 0x3d6724b1bb15eab9, + 0x9877c4225061ca76, 0xd68d6810adf74fb3, 0x42e5352fe30ce989, + 0x265b565a7431fde7, 0x3cdbf7e358df4b8b, 0x2922a47f6d3e8779, + 0x52d2242f65b37f88, 0x5d836d6e2958d6b5, 0x29d40f00566d5e26, + 0x288db0e1124b14a0, 0x6c056608b7d9c1b6, 0x0b9471bdb8f19d32, + 0x8fb946504faa6c9d, 0x8943a9464540251c, 0xfd1fe27d144a09e0, + 0xea6ac458da141bda, 0x8048f217633fce36, 0xfeda1384ade74d31, + 0x4334b8b02ff7612f, 0xdbc8441f5227e216, 0x096d119a3605c85b, + 0x2b72b31c21b7d7d0}; + + randen_u64 engine; +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + printf("0x%016lx, ", engine()); + if (i % 3 == 2) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } + engine.seed(); + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +TEST(RandenTest, VerifyGoldenRanden64Seeded) { + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0x83a9e58f94d3dcd5, 0x70bbdff3d97949fb, 0x0438481f7471c1b4, + 0x34fdc58ee5fb5930, 0xceee4f2d2a937d17, 0xb5a26a68e432aea9, + 0x8b64774a3fb51740, 0xd89ac1fc74249c74, 0x03910d1d23fc3fdf, + 0xd38f630878aa897f, 0x0ee8f0f5615f7e44, 0x98f5a53df8279d52, + 0xb403f52c25938d0e, 0x240072996ea6e838, 0xd3a791246190fa61, + 0xaaedd3df7a7b4f80, 0xc6eacabe05deaf6e, 0xb7967dd8790edf4d, + 0x9a0a8e67e049d279, 0x0494f606aebc23e7, 0x598dcd687bc3e0ee, + 0x010ac81802d452a1, 0x6407c87160aa2842, 0x5a56e276486f93a0, + 0xc887a399d46a8f02, 0x9e1e6100fe93b740, 0x12d02e330f8901f6, + 0xc39ca52b47e790b7, 0xb0b0a2fa11e82e61, 0x1542d841a303806a, + 0x1fe659fd7d6e9d86, 0xb8c90d80746541ac, 0x239d56a5669ddc94, + 0xd40db57c8123d13c, 0x3abc2414153a0db0, 0x9bad665630cb8d61, + 0x0bd1fb90ee3f4bbc, 0x8f0b4d7e079b4e42, 0xfa0fb0e0ee59e793, + 0x51080b283e071100, 0x2c4b9e715081cc15, 0xbe10ed49de4941df, + 0xf8eaac9d4b1b0d37, 0x4bcce4b54605e139, 0xa64722b76765dda6, + 0xb9377d738ca28ab5, 0x779fad81a8ccc1af, 0x65cb3ee61ffd3ba7, + 0xd74e79087862836f, 0xd05b9c584c3f25bf, 0x2ba93a4693579827, + 0xd81530aff05420ce, 0xec06cea215478621, 0x4b1798a6796d65ad, + 0xf142f3fb3a6f6fa6, 0x002b7bf7e237b560, 0xf47f2605ef65b4f8, + 0x9804ec5517effc18, 0xaed3d7f8b7d481cd, 0x5651c24c1ce338d1, + 0x3e7a38208bf0a3c6, 0x6796a7b614534aed, 0x0d0f3b848358460f, + 0x0fa5fe7600b19524, 0x2b0cf38253faaedc, 0x10df9188233a9fd6, + 0x3a10033880138b59, 0x5fb0b0d23948e80f, 0x9e76f7b02fbf5350, + 0x0816052304b1a985, 0x30c9880db41fd218, 0x14aa399b65e20f28, + 0xe1454a8cace787b4, 0x325ac971b6c6f0f5, 0x716b1aa2784f3d36, + 0x3d5ce14accfd144f, 0x6c0c97710f651792, 0xbc5b0f59fb333532, + 0x2a90a7d2140470bc, 0x8da269f55c1e1c8d, 0xcfc37143895792ca, + 0xbe21eab1f30b238f, 0x8c47229dee4d65fd, 0x5743614ed1ed7d54, + 0x351372a99e9c476e, 0x2bd5ea15e5db085f, 0x6925fde46e0af4ca, + 0xed3eda2bdc1f45bd, 0xdef68c68d460fa6e, 0xe42a0de76253e2b5, + 0x4e5176dcbc29c305, 0xbfd85fba9f810f6e, 0x76a5a2a9beb815c6, + 0x01edc4ddceaf414c, 0xa4e98904b4bb3b4b, 0x00bd63ac7d2f1ddd, + 0xb8491fe6e998ddbb, 0xb386a3463dda6800, 0x0081887688871619, + 0x33d394b3344e9a38, 0x815dba65a3a8baf9, 0x4232f6ec02c2fd1a, + 0xb5cff603edd20834, 0x580189243f687663, 0xa8d5a2cbdc27fe99, + 0x725d881693fa0131, 0xa2be2c13db2c7ac5, 0x7b6a9614b509fd78, + 0xb6b136d71e717636, 0x660f1a71aff046ea, 0x0ba10ae346c8ec9e, + 0xe66dde53e3145b41, 0x3b18288c88c26be6, 0x4d9d9d2ff02db933, + 0x4167da8c70f46e8a, 0xf183beef8c6318b4, 0x4d889e1e71eeeef1, + 0x7175c71ad6689b6b, 0xfb9e42beacd1b7dd, 0xc33d0e91b29b5e0d, + 0xd39b83291ce47922, 0xc4d570fb8493d12e, 0x23d5a5724f424ae6, + 0x5245f161876b6616, 0x38d77dbd21ab578d, 0x9c3423311f4ecbfe, + 0x76fe31389bacd9d5, + }; + + ExplicitSeedSeq seed_sequence{12, 34, 56}; + randen_u64 engine(seed_sequence); +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + printf("0x%016lx, ", engine()); + if (i % 3 == 2) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } + engine.seed(seed_sequence); + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +TEST(RandenTest, VerifyGoldenRanden32Default) { + constexpr uint64_t kGolden[2 * kNumGoldenOutputs] = { + 0x4e433977, 0xc3c14f13, 0xd90410ee, 0xdda9f47c, 0x7fd8ca10, 0x887bf308, + 0x45c72912, 0xf0b780f5, 0x7696599f, 0x15dbb1d3, 0xff3c6d59, 0x30ec63ba, + 0x6f7f20a6, 0xb29f7360, 0x6f49a54c, 0x02808a31, 0xd5c8e50e, 0x3b8feaf9, + 0x3fd9de8a, 0x9cbf605e, 0x78183bbb, 0xc970ae1a, 0x56301ed5, 0xd8b2ffd3, + 0x0fc73c37, 0xf4b327fe, 0xeb8f9a19, 0xcdfd8d76, 0x91420c9d, 0xc3a506eb, + 0x3eff9556, 0xd5af05dd, 0x8f83c4a1, 0x48db1bb7, 0x0d6bfe8c, 0x7023920e, + 0x34956d42, 0x58d35758, 0x6b87b840, 0xed1ef4c2, 0x3e0b2df3, 0x8eef32a2, + 0x431154fc, 0x497cabf3, 0x70029a8b, 0x4e243705, 0xf090e5ea, 0xd88b5749, + 0xa970692f, 0xc651a582, 0xbb6342f5, 0x78fcec2c, 0x612f55db, 0x463cb745, + 0x1816afe3, 0x352ee4ad, 0xc101da7e, 0x026ff374, 0x1c3de851, 0x811ef082, + 0x04c4fa59, 0x6f7e6167, 0x992d58fc, 0xa0660379, 0xa3b795c7, 0x04b0a374, + 0x685da798, 0x915f3445, 0xc76571ce, 0x26802a8a, 0x33ce1882, 0x46633525, + 0xa24dc738, 0xb9fdefb4, 0x4d6e6c51, 0x5588ba3a, 0xd35f1956, 0xa2101a42, + 0xe200f5fd, 0x607195a5, 0xf3290764, 0x7e100308, 0x759c0709, 0xe1e5e03c, + 0x5da6606f, 0x082572cc, 0x99e432f1, 0xcbcf5853, 0x8335d8f1, 0xe8a2be4f, + 0xcbfee8f2, 0x0904469a, 0x6daecd51, 0xf08bd31b, 0xa69da69a, 0x08e8a1f1, + 0xad57bff5, 0x6542a20a, 0x053d6b46, 0x2e9705bb, 0x0713c391, 0xda2fc9db, + 0x213b6ffb, 0x78e3a810, 0xdd85f8a6, 0xdc16a59c, 0xcd55781f, 0xc0932718, + 0x2b20bfe5, 0xb9bfb29c, 0xbe0f2f9c, 0xb97289c1, 0x03a892d4, 0xc0a2a0e4, + 0x4771435b, 0x5524bb83, 0x39d1a750, 0x8265da3d, 0x8d1b78c5, 0xff4af3ab, + 0x4bcad77f, 0xf0ec5f42, 0x27495189, 0x66e455f6, 0xb57e3270, 0xc82d3120, + 0xc22596e3, 0x3424e47d, 0x9ccedcdd, 0xbc0c9512, 0xafc4dcbf, 0xc191c595, + 0x2bb70939, 0x120392bd, 0xa6cd6ab4, 0x7f90650e, 0x32695ad3, 0x72874918, + 0xa7917eb0, 0xa7c8fac5, 0x18be0361, 0xd088cb94, 0x9c7c1ce5, 0x7c1bf983, + 0x58e1e79e, 0xe2e991fa, 0xfd28c4ad, 0x78565cde, 0xf98bafad, 0x7351b9fe, + 0xb08c96bf, 0x2a9eac28, 0x96cb2225, 0x6c4f1796, 0x1bab87e0, 0x13a68586, + 0xa0501971, 0x64c6de5a, 0xcac70991, 0x30537425, 0xc6c532b7, 0x01590d9d, + 0x8ec720dc, 0x7e05e3aa, 0x54e3e63f, 0x74a07d9c, 0x8f3bc1d2, 0x73818438, + 0x67be3acb, 0x26ffdc50, 0x561f255f, 0x6bcdf185, 0xcf99b1c6, 0xa0eaf2e1, + 0x34f68604, 0x171df819, 0x65683e5a, 0x7ea5a216, 0x75ba1cea, 0x5d1cb020, + 0xd2123fdf, 0x957f38cb, 0xf80de02f, 0xba6364ef, 0x41d452ee, 0x606e0a0e, + 0xde82f7a2, 0x892d8317, 0x50f7b43e, 0xe707b1db, 0x766fcf5b, 0x4eb28826, + 0xe80a0951, 0x5a362d56, 0x16527d78, 0x6ee217df, 0xba6b23dd, 0xf6737962, + 0x7d4076ca, 0x443e6385, 0x048adfeb, 0x790d9a5f, 0x151ee94d, 0xd796b052, + 0x12b04a03, 0x033ed95c, 0x4893da5d, 0x8b833ff8, 0xbb15eab9, 0x3d6724b1, + 0x5061ca76, 0x9877c422, 0xadf74fb3, 0xd68d6810, 0xe30ce989, 0x42e5352f, + 0x7431fde7, 0x265b565a, 0x58df4b8b, 0x3cdbf7e3, 0x6d3e8779, 0x2922a47f, + 0x65b37f88, 0x52d2242f, 0x2958d6b5, 0x5d836d6e, 0x566d5e26, 0x29d40f00, + 0x124b14a0, 0x288db0e1, 0xb7d9c1b6, 0x6c056608, 0xb8f19d32, 0x0b9471bd, + 0x4faa6c9d, 0x8fb94650, 0x4540251c, 0x8943a946, 0x144a09e0, 0xfd1fe27d, + 0xda141bda, 0xea6ac458, 0x633fce36, 0x8048f217, 0xade74d31, 0xfeda1384, + 0x2ff7612f, 0x4334b8b0, 0x5227e216, 0xdbc8441f, 0x3605c85b, 0x096d119a, + 0x21b7d7d0, 0x2b72b31c}; + + randen_u32 engine; +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < 2 * kNumGoldenOutputs; ++i) { + printf("0x%08x, ", engine()); + if (i % 6 == 5) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } + engine.seed(); + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +TEST(RandenTest, VerifyGoldenRanden32Seeded) { + constexpr uint64_t kGolden[2 * kNumGoldenOutputs] = { + 0x94d3dcd5, 0x83a9e58f, 0xd97949fb, 0x70bbdff3, 0x7471c1b4, 0x0438481f, + 0xe5fb5930, 0x34fdc58e, 0x2a937d17, 0xceee4f2d, 0xe432aea9, 0xb5a26a68, + 0x3fb51740, 0x8b64774a, 0x74249c74, 0xd89ac1fc, 0x23fc3fdf, 0x03910d1d, + 0x78aa897f, 0xd38f6308, 0x615f7e44, 0x0ee8f0f5, 0xf8279d52, 0x98f5a53d, + 0x25938d0e, 0xb403f52c, 0x6ea6e838, 0x24007299, 0x6190fa61, 0xd3a79124, + 0x7a7b4f80, 0xaaedd3df, 0x05deaf6e, 0xc6eacabe, 0x790edf4d, 0xb7967dd8, + 0xe049d279, 0x9a0a8e67, 0xaebc23e7, 0x0494f606, 0x7bc3e0ee, 0x598dcd68, + 0x02d452a1, 0x010ac818, 0x60aa2842, 0x6407c871, 0x486f93a0, 0x5a56e276, + 0xd46a8f02, 0xc887a399, 0xfe93b740, 0x9e1e6100, 0x0f8901f6, 0x12d02e33, + 0x47e790b7, 0xc39ca52b, 0x11e82e61, 0xb0b0a2fa, 0xa303806a, 0x1542d841, + 0x7d6e9d86, 0x1fe659fd, 0x746541ac, 0xb8c90d80, 0x669ddc94, 0x239d56a5, + 0x8123d13c, 0xd40db57c, 0x153a0db0, 0x3abc2414, 0x30cb8d61, 0x9bad6656, + 0xee3f4bbc, 0x0bd1fb90, 0x079b4e42, 0x8f0b4d7e, 0xee59e793, 0xfa0fb0e0, + 0x3e071100, 0x51080b28, 0x5081cc15, 0x2c4b9e71, 0xde4941df, 0xbe10ed49, + 0x4b1b0d37, 0xf8eaac9d, 0x4605e139, 0x4bcce4b5, 0x6765dda6, 0xa64722b7, + 0x8ca28ab5, 0xb9377d73, 0xa8ccc1af, 0x779fad81, 0x1ffd3ba7, 0x65cb3ee6, + 0x7862836f, 0xd74e7908, 0x4c3f25bf, 0xd05b9c58, 0x93579827, 0x2ba93a46, + 0xf05420ce, 0xd81530af, 0x15478621, 0xec06cea2, 0x796d65ad, 0x4b1798a6, + 0x3a6f6fa6, 0xf142f3fb, 0xe237b560, 0x002b7bf7, 0xef65b4f8, 0xf47f2605, + 0x17effc18, 0x9804ec55, 0xb7d481cd, 0xaed3d7f8, 0x1ce338d1, 0x5651c24c, + 0x8bf0a3c6, 0x3e7a3820, 0x14534aed, 0x6796a7b6, 0x8358460f, 0x0d0f3b84, + 0x00b19524, 0x0fa5fe76, 0x53faaedc, 0x2b0cf382, 0x233a9fd6, 0x10df9188, + 0x80138b59, 0x3a100338, 0x3948e80f, 0x5fb0b0d2, 0x2fbf5350, 0x9e76f7b0, + 0x04b1a985, 0x08160523, 0xb41fd218, 0x30c9880d, 0x65e20f28, 0x14aa399b, + 0xace787b4, 0xe1454a8c, 0xb6c6f0f5, 0x325ac971, 0x784f3d36, 0x716b1aa2, + 0xccfd144f, 0x3d5ce14a, 0x0f651792, 0x6c0c9771, 0xfb333532, 0xbc5b0f59, + 0x140470bc, 0x2a90a7d2, 0x5c1e1c8d, 0x8da269f5, 0x895792ca, 0xcfc37143, + 0xf30b238f, 0xbe21eab1, 0xee4d65fd, 0x8c47229d, 0xd1ed7d54, 0x5743614e, + 0x9e9c476e, 0x351372a9, 0xe5db085f, 0x2bd5ea15, 0x6e0af4ca, 0x6925fde4, + 0xdc1f45bd, 0xed3eda2b, 0xd460fa6e, 0xdef68c68, 0x6253e2b5, 0xe42a0de7, + 0xbc29c305, 0x4e5176dc, 0x9f810f6e, 0xbfd85fba, 0xbeb815c6, 0x76a5a2a9, + 0xceaf414c, 0x01edc4dd, 0xb4bb3b4b, 0xa4e98904, 0x7d2f1ddd, 0x00bd63ac, + 0xe998ddbb, 0xb8491fe6, 0x3dda6800, 0xb386a346, 0x88871619, 0x00818876, + 0x344e9a38, 0x33d394b3, 0xa3a8baf9, 0x815dba65, 0x02c2fd1a, 0x4232f6ec, + 0xedd20834, 0xb5cff603, 0x3f687663, 0x58018924, 0xdc27fe99, 0xa8d5a2cb, + 0x93fa0131, 0x725d8816, 0xdb2c7ac5, 0xa2be2c13, 0xb509fd78, 0x7b6a9614, + 0x1e717636, 0xb6b136d7, 0xaff046ea, 0x660f1a71, 0x46c8ec9e, 0x0ba10ae3, + 0xe3145b41, 0xe66dde53, 0x88c26be6, 0x3b18288c, 0xf02db933, 0x4d9d9d2f, + 0x70f46e8a, 0x4167da8c, 0x8c6318b4, 0xf183beef, 0x71eeeef1, 0x4d889e1e, + 0xd6689b6b, 0x7175c71a, 0xacd1b7dd, 0xfb9e42be, 0xb29b5e0d, 0xc33d0e91, + 0x1ce47922, 0xd39b8329, 0x8493d12e, 0xc4d570fb, 0x4f424ae6, 0x23d5a572, + 0x876b6616, 0x5245f161, 0x21ab578d, 0x38d77dbd, 0x1f4ecbfe, 0x9c342331, + 0x9bacd9d5, 0x76fe3138, + }; + + ExplicitSeedSeq seed_sequence{12, 34, 56}; + randen_u32 engine(seed_sequence); +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + for (size_t i = 0; i < 2 * kNumGoldenOutputs; ++i) { + printf("0x%08x, ", engine()); + if (i % 6 == 5) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } + engine.seed(seed_sequence); + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +TEST(RandenTest, VerifyGoldenFromDeserializedEngine) { + constexpr uint64_t kGolden[kNumGoldenOutputs] = { + 0x067f9f9ab919657a, 0x0534605912988583, 0x8a303f72feaa673f, + 0x77b7fd747909185c, 0xd9af90403c56d891, 0xd939c6cb204d14b5, + 0x7fbe6b954a47b483, 0x8b31a47cc34c768d, 0x3a9e546da2701a9c, + 0x5246539046253e71, 0x417191ffb2a848a1, 0x7b1c7bf5a5001d09, + 0x9489b15d194f2361, 0xfcebdeea3bcd2461, 0xd643027c854cec97, + 0x5885397f91e0d21c, 0x53173b0efae30d58, 0x1c9c71168449fac1, + 0xe358202b711ed8aa, 0x94e3918ed1d8227c, 0x5bb4e251450144cf, + 0xb5c7a519b489af3b, 0x6f8b560b1f7b3469, 0xfde11dd4a1c74eef, + 0x33383d2f76457dcf, 0x3060c0ec6db9fce1, 0x18f451fcddeec766, + 0xe73c5d6b9f26da2a, 0x8d4cc566671b32a4, 0xb8189b73776bc9ff, + 0x497a70f9caf0bc23, 0x23afcc509791dcea, 0x18af70dc4b27d306, + 0xd3853f955a0ce5b9, 0x441db6c01a0afb17, 0xd0136c3fb8e1f13f, + 0x5e4fd6fc2f33783c, 0xe0d24548adb5da51, 0x0f4d8362a7d3485a, + 0x9f572d68270fa563, 0x6351fbc823024393, 0xa66dbfc61810e9ab, + 0x0ff17fc14b651af8, 0xd74c55dafb99e623, 0x36303bc1ad85c6c2, + 0x4920cd6a2af7e897, 0x0b8848addc30fecd, 0x9e1562eda6488e93, + 0x197553807d607828, 0xbef5eaeda5e21235, 0x18d91d2616aca527, + 0xb7821937f5c873cd, 0x2cd4ae5650dbeefc, 0xb35a64376f75ffdf, + 0x9226d414d647fe07, 0x663f3db455bbb35e, 0xa829eead6ae93247, + 0x7fd69c204dd0d25f, 0xbe1411f891c9acb1, 0xd476f34a506d5f11, + 0xf423d2831649c5ca, 0x1e503962951abd75, 0xeccc9e8b1e34b537, + 0xb11a147294044854, 0xc4cf27f0abf4929d, 0xe9193abf6fa24c8c, + 0xa94a259e3aba8808, 0x21dc414197deffa3, 0xa2ae211d1ff622ae, + 0xfe3995c46be5a4f4, 0xe9984c284bf11128, 0xcb1ce9d2f0851a80, + 0x42fee17971d87cd8, 0xac76a98d177adc88, 0xa0973b3dedc4af6f, + 0xdf56d6bbcb1b8e86, 0xf1e6485f407b11c9, 0x2c63de4deccb15c0, + 0x6fe69db32ed4fad7, 0xaa51a65f84bca1f1, 0x242f2ee81d608afc, + 0x8eb88b2b69fc153b, 0x22c20098baf73fd1, 0x57759466f576488c, + 0x075ca562cea1be9d, 0x9a74814d73d28891, 0x73d1555fc02f4d3d, + 0xc17f8f210ee89337, 0x46cca7999eaeafd4, 0x5db8d6a327a0d8ac, + 0xb79b4f93c738d7a1, 0x9994512f0036ded1, 0xd3883026f38747f4, + 0xf31f7458078d097c, 0x736ce4d480680669, 0x7a496f4c7e1033e3, + 0xecf85bf297fbc68c, 0x9e37e1d0f24f3c4e, 0x15b6e067ca0746fc, + 0xdd4a39905c5db81c, 0xb5dfafa7bcfdf7da, 0xca6646fb6f92a276, + 0x1c6b35f363ef0efd, 0x6a33d06037ad9f76, 0x45544241afd8f80f, + 0x83f8d83f859c90c5, 0x22aea9c5365e8c19, 0xfac35b11f20b6a6a, + 0xd1acf49d1a27dd2f, 0xf281cd09c4fed405, 0x076000a42cd38e4f, + 0x6ace300565070445, 0x463a62781bddc4db, 0x1477126b46b569ac, + 0x127f2bb15035fbb8, 0xdfa30946049c04a8, 0x89072a586ba8dd3e, + 0x62c809582bb7e74d, 0x22c0c3641406c28b, 0x9b66e36c47ff004d, + 0xb9cd2c7519653330, 0x18608d79cd7a598d, 0x92c0bd1323e53e32, + 0x887ff00de8524aa5, 0xa074410b787abd10, 0x18ab41b8057a2063, + 0x1560abf26bc5f987}; + +#if UPDATE_GOLDEN + (void)kGolden; // Silence warning. + std::seed_seq seed_sequence{1, 2, 3, 4, 5}; + randen_u64 engine(seed_sequence); + std::ostringstream stream; + stream << engine; + auto str = stream.str(); + printf("%s\n\n", str.c_str()); + for (size_t i = 0; i < kNumGoldenOutputs; ++i) { + printf("0x%016lx, ", engine()); + if (i % 3 == 2) { + printf("\n"); + } + } + printf("\n\n\n"); +#else + randen_u64 engine; + std::istringstream stream( + "0 0 9824501439887287479 3242284395352394785 243836530774933777 " + "4047941804708365596 17165468127298385802 949276103645889255 " + "10659970394998657921 1657570836810929787 11697746266668051452 " + "9967209969299905230 14140390331161524430 7383014124183271684 " + "13146719127702337852 13983155220295807171 11121125587542359264 " + "195757810993252695 17138580243103178492 11326030747260920501 " + "8585097322474965590 18342582839328350995 15052982824209724634 " + "7321861343874683609 1806786911778767826 10100850842665572955 " + "9249328950653985078 13600624835326909759 11137960060943860251 " + "10208781341792329629 9282723971471525577 16373271619486811032 32"); + stream >> engine; + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, engine()); + } +#endif +} + +TEST(RandenTest, IsFastOrSlow) { + // randen_engine typically costs ~5ns per value for the optimized code paths, + // and the ~1000ns per value for slow code paths. However when running under + // msan, asan, etc. it can take much longer. + // + // The estimated operation time is something like: + // + // linux, optimized ~5ns + // ppc, optimized ~7ns + // nacl (slow), ~1100ns + // + // `kCount` is chosen below so that, in debug builds and without hardware + // acceleration, the test (assuming ~1us per call) should finish in ~0.1s + static constexpr size_t kCount = 100000; + randen_u64 engine; + randen_u64::result_type sum = 0; + auto start = absl::GetCurrentTimeNanos(); + for (int i = 0; i < kCount; i++) { + sum += engine(); + } + auto duration = absl::GetCurrentTimeNanos() - start; + + ABSL_INTERNAL_LOG(INFO, absl::StrCat(static_cast<double>(duration) / + static_cast<double>(kCount), + "ns")); + + EXPECT_GT(sum, 0); + EXPECT_GE(duration, kCount); // Should be slower than 1ns per call. +} + +} // namespace diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc new file mode 100644 index 00000000..d7eed8b2 --- /dev/null +++ b/absl/random/internal/randen_hwaes.cc @@ -0,0 +1,704 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate +// symbols from arbitrary system and other headers, since it may be built +// with different flags from other targets, using different levels of +// optimization, potentially introducing ODR violations. + +#include "absl/random/internal/randen_hwaes.h" + +#include <cstdint> +#include <cstring> + +#include "absl/random/internal/platform.h" + +// ABSL_HAVE_ATTRIBUTE +#if !defined(ABSL_HAVE_ATTRIBUTE) +#ifdef __has_attribute +#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) +#else +#define ABSL_HAVE_ATTRIBUTE(x) 0 +#endif +#endif + +#if ABSL_HAVE_ATTRIBUTE(always_inline) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \ + __attribute__((always_inline)) +#elif defined(_MSC_VER) +// We can achieve something similar to attribute((always_inline)) with MSVC by +// using the __forceinline keyword, however this is not perfect. MSVC is +// much less aggressive about inlining, and even with the __forceinline keyword. +#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __forceinline +#else +#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE +#endif + +// ABSL_ATTRIBUTE_FLATTEN enables much more aggressive inlining within +// the indicated function. +#undef ABSL_ATTRIBUTE_FLATTEN +#if ABSL_HAVE_ATTRIBUTE(flatten) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_ATTRIBUTE_FLATTEN __attribute__((flatten)) +#else +#define ABSL_ATTRIBUTE_FLATTEN +#endif + +// ABSL_RANDEN_HWAES_IMPL indicates whether this file will contain +// a hardware accelerated implementation of randen, or whether it +// will contain stubs that exit the process. +#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) +// The platform.h directives are sufficient to indicate whether +// we should build accelerated implementations for x86. +#if (ABSL_HAVE_ACCELERATED_AES || ABSL_RANDOM_INTERNAL_AES_DISPATCH) +#define ABSL_RANDEN_HWAES_IMPL 1 +#endif +#elif defined(ABSL_ARCH_PPC) +// The platform.h directives are sufficient to indicate whether +// we should build accelerated implementations for PPC. +// +// NOTE: This has mostly been tested on 64-bit Power variants, +// and not embedded cpus such as powerpc32-8540 +#if ABSL_HAVE_ACCELERATED_AES +#define ABSL_RANDEN_HWAES_IMPL 1 +#endif +#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64) +// ARM is somewhat more complicated. We might support crypto natively... +#if ABSL_HAVE_ACCELERATED_AES || \ + (defined(__ARM_NEON) && defined(__ARM_FEATURE_CRYPTO)) +#define ABSL_RANDEN_HWAES_IMPL 1 + +#elif ABSL_RANDOM_INTERNAL_AES_DISPATCH && !defined(__APPLE__) && \ + (defined(__GNUC__) && __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 9) +// ...or, on GCC, we can use an ASM directive to +// instruct the assember to allow crypto instructions. +#define ABSL_RANDEN_HWAES_IMPL 1 +#define ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE 1 +#endif +#else +// HWAES is unsupported by these architectures / platforms: +// __myriad2__ +// __mips__ +// +// Other architectures / platforms are unknown. +// +// See the Abseil documentation on supported macros at: +// https://abseil.io/docs/cpp/platforms/macros +#endif + +#if !defined(ABSL_RANDEN_HWAES_IMPL) +// No accelerated implementation is supported. +// The RandenHwAes functions are stubs that print an error and exit. + +#include <cstdio> +#include <cstdlib> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// No accelerated implementation. +bool HasRandenHwAesImplementation() { return false; } + +// NOLINTNEXTLINE +const void* RandenHwAes::GetKeys() { + // Attempted to dispatch to an unsupported dispatch target. + const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH; + fprintf(stderr, "AES Hardware detection failed (%d).\n", d); + exit(1); + return nullptr; +} + +// NOLINTNEXTLINE +void RandenHwAes::Absorb(const void*, void*) { + // Attempted to dispatch to an unsupported dispatch target. + const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH; + fprintf(stderr, "AES Hardware detection failed (%d).\n", d); + exit(1); +} + +// NOLINTNEXTLINE +void RandenHwAes::Generate(const void*, void*) { + // Attempted to dispatch to an unsupported dispatch target. + const int d = ABSL_RANDOM_INTERNAL_AES_DISPATCH; + fprintf(stderr, "AES Hardware detection failed (%d).\n", d); + exit(1); +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#else // defined(ABSL_RANDEN_HWAES_IMPL) +// +// Accelerated implementations are supported. +// We need the per-architecture includes and defines. +// + +#include "absl/random/internal/randen_traits.h" + +// ABSL_FUNCTION_ALIGN32 defines a 32-byte alignment attribute +// for the functions in this file. +// +// NOTE: Determine whether we actually have any wins from ALIGN32 +// using microbenchmarks. If not, remove. +#undef ABSL_FUNCTION_ALIGN32 +#if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_FUNCTION_ALIGN32 __attribute__((aligned(32))) +#else +#define ABSL_FUNCTION_ALIGN32 +#endif + +// TARGET_CRYPTO defines a crypto attribute for each architecture. +// +// NOTE: Evaluate whether we should eliminate ABSL_TARGET_CRYPTO. +#if (defined(__clang__) || defined(__GNUC__)) +#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) +#define ABSL_TARGET_CRYPTO __attribute__((target("aes"))) +#elif defined(ABSL_ARCH_PPC) +#define ABSL_TARGET_CRYPTO __attribute__((target("crypto"))) +#else +#define ABSL_TARGET_CRYPTO +#endif +#else +#define ABSL_TARGET_CRYPTO +#endif + +#if defined(ABSL_ARCH_PPC) +// NOTE: Keep in mind that PPC can operate in little-endian or big-endian mode, +// however the PPC altivec vector registers (and thus the AES instructions) +// always operate in big-endian mode. + +#include <altivec.h> +// <altivec.h> #defines vector __vector; in C++, this is bad form. +#undef vector + +// Rely on the PowerPC AltiVec vector operations for accelerated AES +// instructions. GCC support of the PPC vector types is described in: +// https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gcc/PowerPC-AltiVec_002fVSX-Built-in-Functions.html +// +// Already provides operator^=. +using Vector128 = __vector unsigned long long; // NOLINT(runtime/int) + +namespace { + +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 +ReverseBytes(const Vector128& v) { + // Reverses the bytes of the vector. + const __vector unsigned char perm = {15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0}; + return vec_perm(v, v, perm); +} + +// WARNING: these load/store in native byte order. It is OK to load and then +// store an unchanged vector, but interpreting the bits as a number or input +// to AES will have undefined results. +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 +Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { + return vec_vsx_ld(0, reinterpret_cast<const Vector128*>(from)); +} + +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void +Vector128Store(const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { + vec_vsx_st(v, 0, reinterpret_cast<Vector128*>(to)); +} + +// One round of AES. "round_key" is a public constant for breaking the +// symmetry of AES (ensures previously equal columns differ afterwards). +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 +AesRound(const Vector128& state, const Vector128& round_key) { + return Vector128(__builtin_crypto_vcipher(state, round_key)); +} + +// Enables native loads in the round loop by pre-swapping. +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void +SwapEndian(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { + using absl::random_internal::RandenTraits; + constexpr size_t kLanes = 2; + constexpr size_t kFeistelBlocks = RandenTraits::kFeistelBlocks; + + for (uint32_t branch = 0; branch < kFeistelBlocks; ++branch) { + const Vector128 v = ReverseBytes(Vector128Load(state + kLanes * branch)); + Vector128Store(v, state + kLanes * branch); + } +} + +} // namespace + +#elif defined(ABSL_ARCH_ARM) || defined(ABSL_ARCH_AARCH64) + +// This asm directive will cause the file to be compiled with crypto extensions +// whether or not the cpu-architecture supports it. +#if ABSL_RANDEN_HWAES_IMPL_CRYPTO_DIRECTIVE +asm(".arch_extension crypto\n"); + +// Override missing defines. +#if !defined(__ARM_NEON) +#define __ARM_NEON 1 +#endif + +#if !defined(__ARM_FEATURE_CRYPTO) +#define __ARM_FEATURE_CRYPTO 1 +#endif + +#endif + +// Rely on the ARM NEON+Crypto advanced simd types, defined in <arm_neon.h>. +// uint8x16_t is the user alias for underlying __simd128_uint8_t type. +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf +// +// <arm_neon> defines the following +// +// typedef __attribute__((neon_vector_type(16))) uint8_t uint8x16_t; +// typedef __attribute__((neon_vector_type(16))) int8_t int8x16_t; +// typedef __attribute__((neon_polyvector_type(16))) int8_t poly8x16_t; +// +// vld1q_v +// vst1q_v +// vaeseq_v +// vaesmcq_v +#include <arm_neon.h> + +// Already provides operator^=. +using Vector128 = uint8x16_t; + +namespace { + +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 +Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { + return vld1q_u8(reinterpret_cast<const uint8_t*>(from)); +} + +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void +Vector128Store(const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { + vst1q_u8(reinterpret_cast<uint8_t*>(to), v); +} + +// One round of AES. "round_key" is a public constant for breaking the +// symmetry of AES (ensures previously equal columns differ afterwards). +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 +AesRound(const Vector128& state, const Vector128& round_key) { + // It is important to always use the full round function - omitting the + // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf] + // and does not help because we never decrypt. + // + // Note that ARM divides AES instructions differently than x86 / PPC, + // And we need to skip the first AddRoundKey step and add an extra + // AddRoundKey step to the end. Lucky for us this is just XOR. + return vaesmcq_u8(vaeseq_u8(state, uint8x16_t{})) ^ round_key; +} + +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void +SwapEndian(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT) {} + +} // namespace + +#elif defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) +// On x86 we rely on the aesni instructions +#include <wmmintrin.h> + +namespace { + +// Vector128 class is only wrapper for __m128i, benchmark indicates that it's +// faster than using __m128i directly. +class Vector128 { + public: + // Convert from/to intrinsics. + inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE explicit Vector128( + const __m128i& Vector128) + : data_(Vector128) {} + + inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __m128i data() const { + return data_; + } + + inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128& operator^=( + const Vector128& other) { + data_ = _mm_xor_si128(data_, other.data()); + return *this; + } + + private: + __m128i data_; +}; + +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 +Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { + return Vector128(_mm_load_si128(reinterpret_cast<const __m128i*>(from))); +} + +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void +Vector128Store(const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { + _mm_store_si128(reinterpret_cast<__m128i * ABSL_RANDOM_INTERNAL_RESTRICT>(to), + v.data()); +} + +// One round of AES. "round_key" is a public constant for breaking the +// symmetry of AES (ensures previously equal columns differ afterwards). +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 +AesRound(const Vector128& state, const Vector128& round_key) { + // It is important to always use the full round function - omitting the + // final MixColumns reduces security [https://eprint.iacr.org/2010/041.pdf] + // and does not help because we never decrypt. + return Vector128(_mm_aesenc_si128(state.data(), round_key.data())); +} + +inline ABSL_TARGET_CRYPTO ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void +SwapEndian(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT) {} + +} // namespace + +#endif + +namespace { + +// u64x2 is a 128-bit, (2 x uint64_t lanes) struct used to store +// the randen_keys. +struct alignas(16) u64x2 { + constexpr u64x2(uint64_t hi, uint64_t lo) +#if defined(ABSL_ARCH_PPC) + // This has been tested with PPC running in little-endian mode; + // We byte-swap the u64x2 structure from little-endian to big-endian + // because altivec always runs in big-endian mode. + : v{__builtin_bswap64(hi), __builtin_bswap64(lo)} { +#else + : v{lo, hi} { +#endif + } + + constexpr bool operator==(const u64x2& other) const { + return v[0] == other.v[0] && v[1] == other.v[1]; + } + + constexpr bool operator!=(const u64x2& other) const { + return !(*this == other); + } + + uint64_t v[2]; +}; // namespace + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#endif + +// At this point, all of the platform-specific features have been defined / +// implemented. +// +// REQUIRES: using u64x2 = ... +// REQUIRES: using Vector128 = ... +// REQUIRES: Vector128 Vector128Load(void*) {...} +// REQUIRES: void Vector128Store(Vector128, void*) {...} +// REQUIRES: Vector128 AesRound(Vector128, Vector128) {...} +// REQUIRES: void SwapEndian(uint64_t*) {...} +// +// PROVIDES: absl::random_internal::RandenHwAes::Absorb +// PROVIDES: absl::random_internal::RandenHwAes::Generate + +// RANDen = RANDom generator or beetroots in Swiss German. +// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random +// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. +// +// High-level summary: +// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is +// a sponge-like random generator that requires a cryptographic permutation. +// It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by +// achieving backtracking resistance with only one Permute() per buffer. +// +// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round +// Function" constructs up to 1024-bit permutations using an improved +// Generalized Feistel network with 2-round AES-128 functions. This Feistel +// block shuffle achieves diffusion faster and is less vulnerable to +// sliced-biclique attacks than the Type-2 cyclic shuffle. +// +// 3) "Improving the Generalized Feistel" and "New criterion for diffusion +// property" extends the same kind of improved Feistel block shuffle to 16 +// branches, which enables a 2048-bit permutation. +// +// We combine these three ideas and also change Simpira's subround keys from +// structured/low-entropy counters to digits of Pi. + +// Randen constants. +using absl::random_internal::RandenTraits; +constexpr size_t kStateBytes = RandenTraits::kStateBytes; +constexpr size_t kCapacityBytes = RandenTraits::kCapacityBytes; +constexpr size_t kFeistelBlocks = RandenTraits::kFeistelBlocks; +constexpr size_t kFeistelRounds = RandenTraits::kFeistelRounds; +constexpr size_t kFeistelFunctions = RandenTraits::kFeistelFunctions; + +// Independent keys (272 = 2.1 KiB) for the first AES subround of each function. +constexpr size_t kKeys = kFeistelRounds * kFeistelFunctions; + +// INCLUDE keys. +#include "absl/random/internal/randen-keys.inc" + +static_assert(kKeys == kRoundKeys, "kKeys and kRoundKeys must be equal"); +static_assert(round_keys[kKeys - 1] != u64x2(0, 0), + "Too few round_keys initializers"); + +// Number of uint64_t lanes per 128-bit vector; +constexpr size_t kLanes = 2; + +// Block shuffles applies a shuffle to the entire state between AES rounds. +// Improved odd-even shuffle from "New criterion for diffusion property". +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO void +BlockShuffle(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { + static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); + + constexpr size_t shuffle[kFeistelBlocks] = {7, 2, 13, 4, 11, 8, 3, 6, + 15, 0, 9, 10, 1, 14, 5, 12}; + + // The fully unrolled loop without the memcpy improves the speed by about + // 30% over the equivalent loop. + const Vector128 v0 = Vector128Load(state + kLanes * shuffle[0]); + const Vector128 v1 = Vector128Load(state + kLanes * shuffle[1]); + const Vector128 v2 = Vector128Load(state + kLanes * shuffle[2]); + const Vector128 v3 = Vector128Load(state + kLanes * shuffle[3]); + const Vector128 v4 = Vector128Load(state + kLanes * shuffle[4]); + const Vector128 v5 = Vector128Load(state + kLanes * shuffle[5]); + const Vector128 v6 = Vector128Load(state + kLanes * shuffle[6]); + const Vector128 v7 = Vector128Load(state + kLanes * shuffle[7]); + const Vector128 w0 = Vector128Load(state + kLanes * shuffle[8]); + const Vector128 w1 = Vector128Load(state + kLanes * shuffle[9]); + const Vector128 w2 = Vector128Load(state + kLanes * shuffle[10]); + const Vector128 w3 = Vector128Load(state + kLanes * shuffle[11]); + const Vector128 w4 = Vector128Load(state + kLanes * shuffle[12]); + const Vector128 w5 = Vector128Load(state + kLanes * shuffle[13]); + const Vector128 w6 = Vector128Load(state + kLanes * shuffle[14]); + const Vector128 w7 = Vector128Load(state + kLanes * shuffle[15]); + + Vector128Store(v0, state + kLanes * 0); + Vector128Store(v1, state + kLanes * 1); + Vector128Store(v2, state + kLanes * 2); + Vector128Store(v3, state + kLanes * 3); + Vector128Store(v4, state + kLanes * 4); + Vector128Store(v5, state + kLanes * 5); + Vector128Store(v6, state + kLanes * 6); + Vector128Store(v7, state + kLanes * 7); + Vector128Store(w0, state + kLanes * 8); + Vector128Store(w1, state + kLanes * 9); + Vector128Store(w2, state + kLanes * 10); + Vector128Store(w3, state + kLanes * 11); + Vector128Store(w4, state + kLanes * 12); + Vector128Store(w5, state + kLanes * 13); + Vector128Store(w6, state + kLanes * 14); + Vector128Store(w7, state + kLanes * 15); +} + +// Feistel round function using two AES subrounds. Very similar to F() +// from Simpira v2, but with independent subround keys. Uses 17 AES rounds +// per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in +// parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel +// XORs are 'free' (included in the second AES instruction). +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO const + u64x2* + FeistelRound(uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state, + const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { + static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); + + // MSVC does a horrible job at unrolling loops. + // So we unroll the loop by hand to improve the performance. + const Vector128 s0 = Vector128Load(state + kLanes * 0); + const Vector128 s1 = Vector128Load(state + kLanes * 1); + const Vector128 s2 = Vector128Load(state + kLanes * 2); + const Vector128 s3 = Vector128Load(state + kLanes * 3); + const Vector128 s4 = Vector128Load(state + kLanes * 4); + const Vector128 s5 = Vector128Load(state + kLanes * 5); + const Vector128 s6 = Vector128Load(state + kLanes * 6); + const Vector128 s7 = Vector128Load(state + kLanes * 7); + const Vector128 s8 = Vector128Load(state + kLanes * 8); + const Vector128 s9 = Vector128Load(state + kLanes * 9); + const Vector128 s10 = Vector128Load(state + kLanes * 10); + const Vector128 s11 = Vector128Load(state + kLanes * 11); + const Vector128 s12 = Vector128Load(state + kLanes * 12); + const Vector128 s13 = Vector128Load(state + kLanes * 13); + const Vector128 s14 = Vector128Load(state + kLanes * 14); + const Vector128 s15 = Vector128Load(state + kLanes * 15); + + // Encode even blocks with keys. + const Vector128 e0 = AesRound(s0, Vector128Load(keys + 0)); + const Vector128 e2 = AesRound(s2, Vector128Load(keys + 1)); + const Vector128 e4 = AesRound(s4, Vector128Load(keys + 2)); + const Vector128 e6 = AesRound(s6, Vector128Load(keys + 3)); + const Vector128 e8 = AesRound(s8, Vector128Load(keys + 4)); + const Vector128 e10 = AesRound(s10, Vector128Load(keys + 5)); + const Vector128 e12 = AesRound(s12, Vector128Load(keys + 6)); + const Vector128 e14 = AesRound(s14, Vector128Load(keys + 7)); + + // Encode odd blocks with even output from above. + const Vector128 o1 = AesRound(e0, s1); + const Vector128 o3 = AesRound(e2, s3); + const Vector128 o5 = AesRound(e4, s5); + const Vector128 o7 = AesRound(e6, s7); + const Vector128 o9 = AesRound(e8, s9); + const Vector128 o11 = AesRound(e10, s11); + const Vector128 o13 = AesRound(e12, s13); + const Vector128 o15 = AesRound(e14, s15); + + // Store odd blocks. (These will be shuffled later). + Vector128Store(o1, state + kLanes * 1); + Vector128Store(o3, state + kLanes * 3); + Vector128Store(o5, state + kLanes * 5); + Vector128Store(o7, state + kLanes * 7); + Vector128Store(o9, state + kLanes * 9); + Vector128Store(o11, state + kLanes * 11); + Vector128Store(o13, state + kLanes * 13); + Vector128Store(o15, state + kLanes * 15); + + return keys + 8; +} + +// Cryptographic permutation based via type-2 Generalized Feistel Network. +// Indistinguishable from ideal by chosen-ciphertext adversaries using less than +// 2^64 queries if the round function is a PRF. This is similar to the b=8 case +// of Simpira v2, but more efficient than its generic construction for b=16. +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE ABSL_TARGET_CRYPTO void +Permute(const void* ABSL_RANDOM_INTERNAL_RESTRICT keys, + uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { + const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys128 = + static_cast<const u64x2*>(keys); + + // (Successfully unrolled; the first iteration jumps into the second half) +#ifdef __clang__ +#pragma clang loop unroll_count(2) +#endif + for (size_t round = 0; round < kFeistelRounds; ++round) { + keys128 = FeistelRound(state, keys128); + BlockShuffle(state); + } +} + +} // namespace + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +bool HasRandenHwAesImplementation() { return true; } + +const void* ABSL_TARGET_CRYPTO ABSL_FUNCTION_ALIGN32 ABSL_ATTRIBUTE_FLATTEN +RandenHwAes::GetKeys() { + // Round keys for one AES per Feistel round and branch. + // The canonical implementation uses first digits of Pi. + return round_keys; +} + +// NOLINTNEXTLINE +void ABSL_TARGET_CRYPTO ABSL_FUNCTION_ALIGN32 ABSL_ATTRIBUTE_FLATTEN +RandenHwAes::Absorb(const void* seed_void, void* state_void) { + uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = + reinterpret_cast<uint64_t*>(state_void); + const uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT seed = + reinterpret_cast<const uint64_t*>(seed_void); + + constexpr size_t kCapacityBlocks = kCapacityBytes / sizeof(Vector128); + constexpr size_t kStateBlocks = kStateBytes / sizeof(Vector128); + + static_assert(kCapacityBlocks * sizeof(Vector128) == kCapacityBytes, + "Not i*V"); + static_assert(kCapacityBlocks == 1, "Unexpected Randen kCapacityBlocks"); + static_assert(kStateBlocks == 16, "Unexpected Randen kStateBlocks"); + + Vector128 b1 = Vector128Load(state + kLanes * 1); + b1 ^= Vector128Load(seed + kLanes * 0); + Vector128Store(b1, state + kLanes * 1); + + Vector128 b2 = Vector128Load(state + kLanes * 2); + b2 ^= Vector128Load(seed + kLanes * 1); + Vector128Store(b2, state + kLanes * 2); + + Vector128 b3 = Vector128Load(state + kLanes * 3); + b3 ^= Vector128Load(seed + kLanes * 2); + Vector128Store(b3, state + kLanes * 3); + + Vector128 b4 = Vector128Load(state + kLanes * 4); + b4 ^= Vector128Load(seed + kLanes * 3); + Vector128Store(b4, state + kLanes * 4); + + Vector128 b5 = Vector128Load(state + kLanes * 5); + b5 ^= Vector128Load(seed + kLanes * 4); + Vector128Store(b5, state + kLanes * 5); + + Vector128 b6 = Vector128Load(state + kLanes * 6); + b6 ^= Vector128Load(seed + kLanes * 5); + Vector128Store(b6, state + kLanes * 6); + + Vector128 b7 = Vector128Load(state + kLanes * 7); + b7 ^= Vector128Load(seed + kLanes * 6); + Vector128Store(b7, state + kLanes * 7); + + Vector128 b8 = Vector128Load(state + kLanes * 8); + b8 ^= Vector128Load(seed + kLanes * 7); + Vector128Store(b8, state + kLanes * 8); + + Vector128 b9 = Vector128Load(state + kLanes * 9); + b9 ^= Vector128Load(seed + kLanes * 8); + Vector128Store(b9, state + kLanes * 9); + + Vector128 b10 = Vector128Load(state + kLanes * 10); + b10 ^= Vector128Load(seed + kLanes * 9); + Vector128Store(b10, state + kLanes * 10); + + Vector128 b11 = Vector128Load(state + kLanes * 11); + b11 ^= Vector128Load(seed + kLanes * 10); + Vector128Store(b11, state + kLanes * 11); + + Vector128 b12 = Vector128Load(state + kLanes * 12); + b12 ^= Vector128Load(seed + kLanes * 11); + Vector128Store(b12, state + kLanes * 12); + + Vector128 b13 = Vector128Load(state + kLanes * 13); + b13 ^= Vector128Load(seed + kLanes * 12); + Vector128Store(b13, state + kLanes * 13); + + Vector128 b14 = Vector128Load(state + kLanes * 14); + b14 ^= Vector128Load(seed + kLanes * 13); + Vector128Store(b14, state + kLanes * 14); + + Vector128 b15 = Vector128Load(state + kLanes * 15); + b15 ^= Vector128Load(seed + kLanes * 14); + Vector128Store(b15, state + kLanes * 15); +} + +// NOLINTNEXTLINE +void ABSL_TARGET_CRYPTO ABSL_FUNCTION_ALIGN32 ABSL_ATTRIBUTE_FLATTEN +RandenHwAes::Generate(const void* keys, void* state_void) { + static_assert(kCapacityBytes == sizeof(Vector128), "Capacity mismatch"); + + uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = + reinterpret_cast<uint64_t*>(state_void); + + const Vector128 prev_inner = Vector128Load(state); + + SwapEndian(state); + + Permute(keys, state); + + SwapEndian(state); + + // Ensure backtracking resistance. + Vector128 inner = Vector128Load(state); + inner ^= prev_inner; + Vector128Store(inner, state); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // (ABSL_RANDEN_HWAES_IMPL) diff --git a/absl/random/internal/randen_hwaes.h b/absl/random/internal/randen_hwaes.h new file mode 100644 index 00000000..848bcead --- /dev/null +++ b/absl/random/internal/randen_hwaes.h @@ -0,0 +1,48 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_ +#define ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_ + +// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate +// symbols from arbitrary system and other headers, since it may be built +// with different flags from other targets, using different levels of +// optimization, potentially introducing ODR violations. + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// RANDen = RANDom generator or beetroots in Swiss German. +// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random +// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. +// +// RandenHwAes implements the basic state manipulation methods. +class RandenHwAes { + public: + static void Generate(const void* keys, void* state_void); + static void Absorb(const void* seed_void, void* state_void); + static const void* GetKeys(); +}; + +// HasRandenHwAesImplementation returns true when there is an accelerated +// implementation, and false otherwise. If there is no implementation, +// then attempting to use it will abort the program. +bool HasRandenHwAesImplementation(); + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_RANDEN_HWAES_H_ diff --git a/absl/random/internal/randen_hwaes_test.cc b/absl/random/internal/randen_hwaes_test.cc new file mode 100644 index 00000000..a7cbd46b --- /dev/null +++ b/absl/random/internal/randen_hwaes_test.cc @@ -0,0 +1,102 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/randen_hwaes.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/platform.h" +#include "absl/random/internal/randen_detect.h" +#include "absl/random/internal/randen_traits.h" +#include "absl/strings/str_format.h" + +namespace { + +using absl::random_internal::RandenHwAes; +using absl::random_internal::RandenTraits; + +struct randen { + static constexpr size_t kStateSizeT = + RandenTraits::kStateBytes / sizeof(uint64_t); + uint64_t state[kStateSizeT]; + static constexpr size_t kSeedSizeT = + RandenTraits::kSeedBytes / sizeof(uint32_t); + uint32_t seed[kSeedSizeT]; +}; + +TEST(RandenHwAesTest, Default) { + EXPECT_TRUE(absl::random_internal::CPUSupportsRandenHwAes()); + + constexpr uint64_t kGolden[] = { + 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, + 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, + 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, + 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, + 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, + 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, + 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, + 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, + 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, + 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, + 0x026ff374c101da7e, 0x811ef0821c3de851, + }; + + alignas(16) randen d; + memset(d.state, 0, sizeof(d.state)); + RandenHwAes::Generate(RandenHwAes::GetKeys(), d.state); + + uint64_t* id = d.state; + for (const auto& elem : kGolden) { + auto a = absl::StrFormat("%#x", elem); + auto b = absl::StrFormat("%#x", *id++); + EXPECT_EQ(a, b); + } +} + +} // namespace + +int main(int argc, char* argv[]) { + testing::InitGoogleTest(&argc, argv); + + ABSL_RAW_LOG(INFO, "ABSL_HAVE_ACCELERATED_AES=%d", ABSL_HAVE_ACCELERATED_AES); + ABSL_RAW_LOG(INFO, "ABSL_RANDOM_INTERNAL_AES_DISPATCH=%d", + ABSL_RANDOM_INTERNAL_AES_DISPATCH); + +#if defined(ABSL_ARCH_X86_64) + ABSL_RAW_LOG(INFO, "ABSL_ARCH_X86_64"); +#elif defined(ABSL_ARCH_X86_32) + ABSL_RAW_LOG(INFO, "ABSL_ARCH_X86_32"); +#elif defined(ABSL_ARCH_AARCH64) + ABSL_RAW_LOG(INFO, "ABSL_ARCH_AARCH64"); +#elif defined(ABSL_ARCH_ARM) + ABSL_RAW_LOG(INFO, "ABSL_ARCH_ARM"); +#elif defined(ABSL_ARCH_PPC) + ABSL_RAW_LOG(INFO, "ABSL_ARCH_PPC"); +#else + ABSL_RAW_LOG(INFO, "ARCH Unknown"); +#endif + + int x = absl::random_internal::HasRandenHwAesImplementation(); + ABSL_RAW_LOG(INFO, "HasRandenHwAesImplementation = %d", x); + + int y = absl::random_internal::CPUSupportsRandenHwAes(); + ABSL_RAW_LOG(INFO, "CPUSupportsRandenHwAes = %d", x); + + if (!x || !y) { + ABSL_RAW_LOG(INFO, "Skipping Randen HWAES tests."); + return 0; + } + return RUN_ALL_TESTS(); +} diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc new file mode 100644 index 00000000..e2d44f88 --- /dev/null +++ b/absl/random/internal/randen_slow.cc @@ -0,0 +1,514 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/randen_slow.h" + +#include <cstddef> +#include <cstdint> +#include <cstring> + +#include "absl/random/internal/platform.h" + +// ABSL_HAVE_ATTRIBUTE +#if !defined(ABSL_HAVE_ATTRIBUTE) +#ifdef __has_attribute +#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) +#else +#define ABSL_HAVE_ATTRIBUTE(x) 0 +#endif +#endif + +#if ABSL_HAVE_ATTRIBUTE(always_inline) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \ + __attribute__((always_inline)) +#elif defined(_MSC_VER) +// We can achieve something similar to attribute((always_inline)) with MSVC by +// using the __forceinline keyword, however this is not perfect. MSVC is +// much less aggressive about inlining, and even with the __forceinline keyword. +#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE __forceinline +#else +#define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE +#endif + +namespace { + +// AES portions based on rijndael-alg-fst.c, +// https://fastcrypto.org/front/misc/rijndael-alg-fst.c +// +// Implementation of +// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf +constexpr uint32_t te0[256] = { + 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, + 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, + 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, + 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, + 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, + 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, + 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, + 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, + 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, + 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, + 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, + 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, + 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, + 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, + 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, + 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, + 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, + 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, + 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, + 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, + 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, + 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, + 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, + 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, + 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, + 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, + 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, + 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, + 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, + 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, + 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, + 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, + 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, + 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, + 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, + 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, + 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, + 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, + 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, + 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, + 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, + 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, + 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, +}; + +constexpr uint32_t te1[256] = { + 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, + 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, + 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, + 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, + 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, + 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, + 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, + 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, + 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, + 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, + 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, + 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, + 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, + 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, + 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, + 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, + 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, + 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, + 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, + 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, + 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, + 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, + 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, + 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, + 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, + 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, + 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, + 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, + 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, + 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, + 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, + 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, + 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, + 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, + 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, + 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, + 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, + 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, + 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, + 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, + 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, + 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, + 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, +}; + +constexpr uint32_t te2[256] = { + 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, + 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, + 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, + 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, + 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, + 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, + 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, + 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, + 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, + 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, + 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, + 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, + 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, + 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, + 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, + 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, + 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, + 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, + 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, + 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, + 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, + 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, + 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, + 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, + 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, + 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, + 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, + 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, + 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, + 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, + 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, + 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, + 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, + 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, + 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, + 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, + 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, + 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, + 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, + 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, + 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, + 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, + 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, +}; + +constexpr uint32_t te3[256] = { + 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, + 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, + 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, + 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, + 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, + 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, + 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, + 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, + 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, + 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, + 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, + 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, + 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, + 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, + 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, + 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, + 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, + 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, + 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, + 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, + 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, + 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, + 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, + 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, + 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, + 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, + 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, + 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, + 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, + 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, + 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, + 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, + 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, + 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, + 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, + 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, + 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, + 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, + 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, + 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, + 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, + 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, + 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, +}; + +struct alignas(16) u64x2 { + constexpr u64x2() : v{0, 0} {}; + constexpr u64x2(uint64_t hi, uint64_t lo) : v{lo, hi} {} + + uint64_t v[2]; +}; + +// Software implementation of the Vector128 class, using uint32_t +// as an underlying vector register. +// +struct Vector128 { + inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128& operator^=( + const Vector128& other) { + s[0] ^= other.s[0]; + s[1] ^= other.s[1]; + s[2] ^= other.s[2]; + s[3] ^= other.s[3]; + return *this; + } + + uint32_t s[4]; +}; + +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 +Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { + Vector128 result; + const uint8_t* ABSL_RANDOM_INTERNAL_RESTRICT src = + reinterpret_cast<const uint8_t*>(from); + + result.s[0] = static_cast<uint32_t>(src[0]) << 24 | + static_cast<uint32_t>(src[1]) << 16 | + static_cast<uint32_t>(src[2]) << 8 | + static_cast<uint32_t>(src[3]); + result.s[1] = static_cast<uint32_t>(src[4]) << 24 | + static_cast<uint32_t>(src[5]) << 16 | + static_cast<uint32_t>(src[6]) << 8 | + static_cast<uint32_t>(src[7]); + result.s[2] = static_cast<uint32_t>(src[8]) << 24 | + static_cast<uint32_t>(src[9]) << 16 | + static_cast<uint32_t>(src[10]) << 8 | + static_cast<uint32_t>(src[11]); + result.s[3] = static_cast<uint32_t>(src[12]) << 24 | + static_cast<uint32_t>(src[13]) << 16 | + static_cast<uint32_t>(src[14]) << 8 | + static_cast<uint32_t>(src[15]); + return result; +} + +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Vector128Store( + const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { + uint8_t* dst = reinterpret_cast<uint8_t*>(to); + dst[0] = static_cast<uint8_t>(v.s[0] >> 24); + dst[1] = static_cast<uint8_t>(v.s[0] >> 16); + dst[2] = static_cast<uint8_t>(v.s[0] >> 8); + dst[3] = static_cast<uint8_t>(v.s[0]); + dst[4] = static_cast<uint8_t>(v.s[1] >> 24); + dst[5] = static_cast<uint8_t>(v.s[1] >> 16); + dst[6] = static_cast<uint8_t>(v.s[1] >> 8); + dst[7] = static_cast<uint8_t>(v.s[1]); + dst[8] = static_cast<uint8_t>(v.s[2] >> 24); + dst[9] = static_cast<uint8_t>(v.s[2] >> 16); + dst[10] = static_cast<uint8_t>(v.s[2] >> 8); + dst[11] = static_cast<uint8_t>(v.s[2]); + dst[12] = static_cast<uint8_t>(v.s[3] >> 24); + dst[13] = static_cast<uint8_t>(v.s[3] >> 16); + dst[14] = static_cast<uint8_t>(v.s[3] >> 8); + dst[15] = static_cast<uint8_t>(v.s[3]); +} + +// One round of AES. "round_key" is a public constant for breaking the +// symmetry of AES (ensures previously equal columns differ afterwards). +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE Vector128 +AesRound(const Vector128& state, const Vector128& round_key) { + // clang-format off + Vector128 result; + result.s[0] = round_key.s[0] ^ + te0[uint8_t(state.s[0] >> 24)] ^ + te1[uint8_t(state.s[1] >> 16)] ^ + te2[uint8_t(state.s[2] >> 8)] ^ + te3[uint8_t(state.s[3])]; + result.s[1] = round_key.s[1] ^ + te0[uint8_t(state.s[1] >> 24)] ^ + te1[uint8_t(state.s[2] >> 16)] ^ + te2[uint8_t(state.s[3] >> 8)] ^ + te3[uint8_t(state.s[0])]; + result.s[2] = round_key.s[2] ^ + te0[uint8_t(state.s[2] >> 24)] ^ + te1[uint8_t(state.s[3] >> 16)] ^ + te2[uint8_t(state.s[0] >> 8)] ^ + te3[uint8_t(state.s[1])]; + result.s[3] = round_key.s[3] ^ + te0[uint8_t(state.s[3] >> 24)] ^ + te1[uint8_t(state.s[0] >> 16)] ^ + te2[uint8_t(state.s[1] >> 8)] ^ + te3[uint8_t(state.s[2])]; + return result; + // clang-format on +} + +// RANDen = RANDom generator or beetroots in Swiss German. +// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random +// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. +// +// High-level summary: +// 1) Reverie (see "A Robust and Sponge-Like PRNG with Improved Efficiency") is +// a sponge-like random generator that requires a cryptographic permutation. +// It improves upon "Provably Robust Sponge-Based PRNGs and KDFs" by +// achieving backtracking resistance with only one Permute() per buffer. +// +// 2) "Simpira v2: A Family of Efficient Permutations Using the AES Round +// Function" constructs up to 1024-bit permutations using an improved +// Generalized Feistel network with 2-round AES-128 functions. This Feistel +// block shuffle achieves diffusion faster and is less vulnerable to +// sliced-biclique attacks than the Type-2 cyclic shuffle. +// +// 3) "Improving the Generalized Feistel" and "New criterion for diffusion +// property" extends the same kind of improved Feistel block shuffle to 16 +// branches, which enables a 2048-bit permutation. +// +// Combine these three ideas and also change Simpira's subround keys from +// structured/low-entropy counters to digits of Pi. + +// Randen constants. +constexpr size_t kFeistelBlocks = 16; +constexpr size_t kFeistelFunctions = kFeistelBlocks / 2; // = 8 +constexpr size_t kFeistelRounds = 16 + 1; // > 4 * log2(kFeistelBlocks) +constexpr size_t kKeys = kFeistelRounds * kFeistelFunctions; + +// INCLUDE keys. +#include "absl/random/internal/randen-keys.inc" + +static_assert(kKeys == kRoundKeys, "kKeys and kRoundKeys must be equal"); + +// 2 uint64_t lanes per Vector128 +static constexpr size_t kLanes = 2; + +// The improved Feistel block shuffle function for 16 blocks. +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void BlockShuffle( + uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state_u64) { + static_assert(kFeistelBlocks == 16, + "Feistel block shuffle only works for 16 blocks."); + + constexpr size_t shuffle[kFeistelBlocks] = {7, 2, 13, 4, 11, 8, 3, 6, + 15, 0, 9, 10, 1, 14, 5, 12}; + + u64x2* ABSL_RANDOM_INTERNAL_RESTRICT state = + reinterpret_cast<u64x2*>(state_u64); + + // The fully unrolled loop without the memcpy improves the speed by about + // 30% over the equivalent (leaving code here as a comment): + if (false) { + u64x2 source[kFeistelBlocks]; + std::memcpy(source, state, sizeof(source)); + for (size_t i = 0; i < kFeistelBlocks; i++) { + const u64x2 v0 = source[shuffle[i]]; + state[i] = v0; + } + } + + const u64x2 v0 = state[shuffle[0]]; + const u64x2 v1 = state[shuffle[1]]; + const u64x2 v2 = state[shuffle[2]]; + const u64x2 v3 = state[shuffle[3]]; + const u64x2 v4 = state[shuffle[4]]; + const u64x2 v5 = state[shuffle[5]]; + const u64x2 v6 = state[shuffle[6]]; + const u64x2 v7 = state[shuffle[7]]; + const u64x2 w0 = state[shuffle[8]]; + const u64x2 w1 = state[shuffle[9]]; + const u64x2 w2 = state[shuffle[10]]; + const u64x2 w3 = state[shuffle[11]]; + const u64x2 w4 = state[shuffle[12]]; + const u64x2 w5 = state[shuffle[13]]; + const u64x2 w6 = state[shuffle[14]]; + const u64x2 w7 = state[shuffle[15]]; + state[0] = v0; + state[1] = v1; + state[2] = v2; + state[3] = v3; + state[4] = v4; + state[5] = v5; + state[6] = v6; + state[7] = v7; + state[8] = w0; + state[9] = w1; + state[10] = w2; + state[11] = w3; + state[12] = w4; + state[13] = w5; + state[14] = w6; + state[15] = w7; +} + +// Feistel round function using two AES subrounds. Very similar to F() +// from Simpira v2, but with independent subround keys. Uses 17 AES rounds +// per 16 bytes (vs. 10 for AES-CTR). Computing eight round functions in +// parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel +// XORs are 'free' (included in the second AES instruction). +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE const u64x2* FeistelRound( + uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state, + const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { + for (size_t branch = 0; branch < kFeistelBlocks; branch += 4) { + const Vector128 s0 = Vector128Load(state + kLanes * branch); + const Vector128 s1 = Vector128Load(state + kLanes * (branch + 1)); + const Vector128 f0 = AesRound(s0, Vector128Load(keys)); + keys++; + const Vector128 o1 = AesRound(f0, s1); + Vector128Store(o1, state + kLanes * (branch + 1)); + + // Manually unroll this loop once. about 10% better than not unrolled. + const Vector128 s2 = Vector128Load(state + kLanes * (branch + 2)); + const Vector128 s3 = Vector128Load(state + kLanes * (branch + 3)); + const Vector128 f2 = AesRound(s2, Vector128Load(keys)); + keys++; + const Vector128 o3 = AesRound(f2, s3); + Vector128Store(o3, state + kLanes * (branch + 3)); + } + return keys; +} + +// Cryptographic permutation based via type-2 Generalized Feistel Network. +// Indistinguishable from ideal by chosen-ciphertext adversaries using less than +// 2^64 queries if the round function is a PRF. This is similar to the b=8 case +// of Simpira v2, but more efficient than its generic construction for b=16. +inline ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE void Permute( + const void* keys, uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { + const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys128 = + static_cast<const u64x2*>(keys); + for (size_t round = 0; round < kFeistelRounds; ++round) { + keys128 = FeistelRound(state, keys128); + BlockShuffle(state); + } +} + +} // namespace + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +const void* RandenSlow::GetKeys() { + // Round keys for one AES per Feistel round and branch. + // The canonical implementation uses first digits of Pi. + return round_keys; +} + +void RandenSlow::Absorb(const void* seed_void, void* state_void) { + uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = + reinterpret_cast<uint64_t*>(state_void); + const uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT seed = + reinterpret_cast<const uint64_t*>(seed_void); + + constexpr size_t kCapacityBlocks = kCapacityBytes / sizeof(uint64_t); + static_assert(kCapacityBlocks * sizeof(uint64_t) == kCapacityBytes, + "Not i*V"); + for (size_t i = kCapacityBlocks; i < kStateBytes / sizeof(uint64_t); ++i) { + state[i] ^= seed[i - kCapacityBlocks]; + } +} + +void RandenSlow::Generate(const void* keys, void* state_void) { + static_assert(kCapacityBytes == sizeof(Vector128), "Capacity mismatch"); + + uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = + reinterpret_cast<uint64_t*>(state_void); + + const Vector128 prev_inner = Vector128Load(state); + + Permute(keys, state); + + // Ensure backtracking resistance. + Vector128 inner = Vector128Load(state); + inner ^= prev_inner; + Vector128Store(inner, state); +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/internal/randen_slow.h b/absl/random/internal/randen_slow.h new file mode 100644 index 00000000..2133b370 --- /dev/null +++ b/absl/random/internal/randen_slow.h @@ -0,0 +1,45 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_ +#define ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_ + +#include <cstddef> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// RANDen = RANDom generator or beetroots in Swiss German. +// RandenSlow implements the basic state manipulation methods for +// architectures lacking AES hardware acceleration intrinsics. +class RandenSlow { + public: + // Size of the entire sponge / state for the randen PRNG. + static constexpr size_t kStateBytes = 256; // 2048-bit + + // Size of the 'inner' (inaccessible) part of the sponge. Larger values would + // require more frequent calls to RandenGenerate. + static constexpr size_t kCapacityBytes = 16; // 128-bit + + static void Generate(const void* keys, void* state_void); + static void Absorb(const void* seed_void, void* state_void); + static const void* GetKeys(); +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_RANDEN_SLOW_H_ diff --git a/absl/random/internal/randen_slow_test.cc b/absl/random/internal/randen_slow_test.cc new file mode 100644 index 00000000..c07155d8 --- /dev/null +++ b/absl/random/internal/randen_slow_test.cc @@ -0,0 +1,61 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/randen_slow.h" + +#include <cstring> + +#include "gtest/gtest.h" + +namespace { + +using absl::random_internal::RandenSlow; + +// Local state parameters. +constexpr size_t kSeedBytes = + RandenSlow::kStateBytes - RandenSlow::kCapacityBytes; +constexpr size_t kStateSizeT = RandenSlow::kStateBytes / sizeof(uint64_t); +constexpr size_t kSeedSizeT = kSeedBytes / sizeof(uint32_t); + +struct randen { + uint64_t state[kStateSizeT]; + uint32_t seed[kSeedSizeT]; +}; + +TEST(RandenSlowTest, Default) { + constexpr uint64_t kGolden[] = { + 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, + 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, + 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, + 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, + 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, + 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, + 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, + 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, + 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, + 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, + 0x026ff374c101da7e, 0x811ef0821c3de851, + }; + + alignas(16) randen d; + std::memset(d.state, 0, sizeof(d.state)); + RandenSlow::Generate(RandenSlow::GetKeys(), d.state); + + uint64_t* id = d.state; + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, *id++); + } +} + +} // namespace diff --git a/absl/random/internal/randen_test.cc b/absl/random/internal/randen_test.cc new file mode 100644 index 00000000..c186fe0d --- /dev/null +++ b/absl/random/internal/randen_test.cc @@ -0,0 +1,70 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/randen.h" + +#include <cstring> + +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" + +namespace { + +using absl::random_internal::Randen; + +// Local state parameters. +constexpr size_t kStateSizeT = Randen::kStateBytes / sizeof(uint64_t); + +TEST(RandenTest, CopyAndMove) { + static_assert(std::is_copy_constructible<Randen>::value, + "Randen must be copy constructible"); + + static_assert(absl::is_copy_assignable<Randen>::value, + "Randen must be copy assignable"); + + static_assert(std::is_move_constructible<Randen>::value, + "Randen must be move constructible"); + + static_assert(absl::is_move_assignable<Randen>::value, + "Randen must be move assignable"); +} + +TEST(RandenTest, Default) { + constexpr uint64_t kGolden[] = { + 0x6c6534090ee6d3ee, 0x044e2b9b9d5333c6, 0xc3c14f134e433977, + 0xdda9f47cd90410ee, 0x887bf3087fd8ca10, 0xf0b780f545c72912, + 0x15dbb1d37696599f, 0x30ec63baff3c6d59, 0xb29f73606f7f20a6, + 0x02808a316f49a54c, 0x3b8feaf9d5c8e50e, 0x9cbf605e3fd9de8a, + 0xc970ae1a78183bbb, 0xd8b2ffd356301ed5, 0xf4b327fe0fc73c37, + 0xcdfd8d76eb8f9a19, 0xc3a506eb91420c9d, 0xd5af05dd3eff9556, + 0x48db1bb78f83c4a1, 0x7023920e0d6bfe8c, 0x58d3575834956d42, + 0xed1ef4c26b87b840, 0x8eef32a23e0b2df3, 0x497cabf3431154fc, + 0x4e24370570029a8b, 0xd88b5749f090e5ea, 0xc651a582a970692f, + 0x78fcec2cbb6342f5, 0x463cb745612f55db, 0x352ee4ad1816afe3, + 0x026ff374c101da7e, 0x811ef0821c3de851, + }; + + alignas(16) uint64_t state[kStateSizeT]; + std::memset(state, 0, sizeof(state)); + + Randen r; + r.Generate(state); + + auto id = std::begin(state); + for (const auto& elem : kGolden) { + EXPECT_EQ(elem, *id++); + } +} + +} // namespace diff --git a/absl/random/internal/randen_traits.h b/absl/random/internal/randen_traits.h new file mode 100644 index 00000000..d2562586 --- /dev/null +++ b/absl/random/internal/randen_traits.h @@ -0,0 +1,61 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_ +#define ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_ + +// HERMETIC NOTE: The randen_hwaes target must not introduce duplicate +// symbols from arbitrary system and other headers, since it may be built +// with different flags from other targets, using different levels of +// optimization, potentially introducing ODR violations. + +#include <cstddef> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// RANDen = RANDom generator or beetroots in Swiss German. +// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random +// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32. +// +// RandenTraits contains the basic algorithm traits, such as the size of the +// state, seed, sponge, etc. +struct RandenTraits { + // Size of the entire sponge / state for the randen PRNG. + static constexpr size_t kStateBytes = 256; // 2048-bit + + // Size of the 'inner' (inaccessible) part of the sponge. Larger values would + // require more frequent calls to RandenGenerate. + static constexpr size_t kCapacityBytes = 16; // 128-bit + + // Size of the default seed consumed by the sponge. + static constexpr size_t kSeedBytes = kStateBytes - kCapacityBytes; + + // Largest size for which security proofs are known. + static constexpr size_t kFeistelBlocks = 16; + + // Type-2 generalized Feistel => one round function for every two blocks. + static constexpr size_t kFeistelFunctions = kFeistelBlocks / 2; // = 8 + + // Ensures SPRP security and two full subblock diffusions. + // Must be > 4 * log2(kFeistelBlocks). + static constexpr size_t kFeistelRounds = 16 + 1; +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_RANDEN_TRAITS_H_ diff --git a/absl/random/internal/salted_seed_seq.h b/absl/random/internal/salted_seed_seq.h new file mode 100644 index 00000000..08bf369e --- /dev/null +++ b/absl/random/internal/salted_seed_seq.h @@ -0,0 +1,167 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_ +#define ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_ + +#include <cstdint> +#include <cstdlib> +#include <initializer_list> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> + +#include "absl/container/inlined_vector.h" +#include "absl/meta/type_traits.h" +#include "absl/random/internal/seed_material.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// This class conforms to the C++ Standard "Seed Sequence" concept +// [rand.req.seedseq]. +// +// A `SaltedSeedSeq` is meant to wrap an existing seed sequence and modify +// generated sequence by mixing with extra entropy. This entropy may be +// build-dependent or process-dependent. The implementation may change to be +// have either or both kinds of entropy. If salt is not available sequence is +// not modified. +template <typename SSeq> +class SaltedSeedSeq { + public: + using inner_sequence_type = SSeq; + using result_type = typename SSeq::result_type; + + SaltedSeedSeq() : seq_(absl::make_unique<SSeq>()) {} + + template <typename Iterator> + SaltedSeedSeq(Iterator begin, Iterator end) + : seq_(absl::make_unique<SSeq>(begin, end)) {} + + template <typename T> + SaltedSeedSeq(std::initializer_list<T> il) + : SaltedSeedSeq(il.begin(), il.end()) {} + + SaltedSeedSeq(const SaltedSeedSeq&) = delete; + SaltedSeedSeq& operator=(const SaltedSeedSeq&) = delete; + + SaltedSeedSeq(SaltedSeedSeq&&) = default; + SaltedSeedSeq& operator=(SaltedSeedSeq&&) = default; + + template <typename RandomAccessIterator> + void generate(RandomAccessIterator begin, RandomAccessIterator end) { + // The common case is that generate is called with ContiguousIterators + // to uint arrays. Such contiguous memory regions may be optimized, + // which we detect here. + using tag = absl::conditional_t< + (std::is_pointer<RandomAccessIterator>::value && + std::is_same<absl::decay_t<decltype(*begin)>, uint32_t>::value), + ContiguousAndUint32Tag, DefaultTag>; + if (begin != end) { + generate_impl(begin, end, tag{}); + } + } + + template <typename OutIterator> + void param(OutIterator out) const { + seq_->param(out); + } + + size_t size() const { return seq_->size(); } + + private: + struct ContiguousAndUint32Tag {}; + struct DefaultTag {}; + + // Generate which requires the iterators are contiguous pointers to uint32_t. + void generate_impl(uint32_t* begin, uint32_t* end, ContiguousAndUint32Tag) { + generate_contiguous(absl::MakeSpan(begin, end)); + } + + // The uncommon case for generate is that it is called with iterators over + // some other buffer type which is assignable from a 32-bit value. In this + // case we allocate a temporary 32-bit buffer and then copy-assign back + // to the initial inputs. + template <typename RandomAccessIterator> + void generate_impl(RandomAccessIterator begin, RandomAccessIterator end, + DefaultTag) { + return generate_and_copy(std::distance(begin, end), begin); + } + + // Fills the initial seed buffer the underlying SSeq::generate() call, + // mixing in the salt material. + void generate_contiguous(absl::Span<uint32_t> buffer) { + seq_->generate(buffer.begin(), buffer.end()); + const uint32_t salt = absl::random_internal::GetSaltMaterial().value_or(0); + MixIntoSeedMaterial(absl::MakeConstSpan(&salt, 1), buffer); + } + + // Allocates a seed buffer of `n` elements, generates the seed, then + // copies the result into the `out` iterator. + template <typename Iterator> + void generate_and_copy(size_t n, Iterator out) { + // Allocate a temporary buffer, generate, and then copy. + absl::InlinedVector<uint32_t, 8> data(n, 0); + generate_contiguous(absl::MakeSpan(data.data(), data.size())); + std::copy(data.begin(), data.end(), out); + } + + // Because [rand.req.seedseq] is not required to be copy-constructible, + // copy-assignable nor movable, we wrap it with unique pointer to be able + // to move SaltedSeedSeq. + std::unique_ptr<SSeq> seq_; +}; + +// is_salted_seed_seq indicates whether the type is a SaltedSeedSeq. +template <typename T, typename = void> +struct is_salted_seed_seq : public std::false_type {}; + +template <typename T> +struct is_salted_seed_seq< + T, typename std::enable_if<std::is_same< + T, SaltedSeedSeq<typename T::inner_sequence_type>>::value>::type> + : public std::true_type {}; + +// MakeSaltedSeedSeq returns a salted variant of the seed sequence. +// When provided with an existing SaltedSeedSeq, returns the input parameter, +// otherwise constructs a new SaltedSeedSeq which embodies the original +// non-salted seed parameters. +template < + typename SSeq, // + typename EnableIf = absl::enable_if_t<is_salted_seed_seq<SSeq>::value>> +SSeq MakeSaltedSeedSeq(SSeq&& seq) { + return SSeq(std::forward<SSeq>(seq)); +} + +template < + typename SSeq, // + typename EnableIf = absl::enable_if_t<!is_salted_seed_seq<SSeq>::value>> +SaltedSeedSeq<typename std::decay<SSeq>::type> MakeSaltedSeedSeq(SSeq&& seq) { + using sseq_type = typename std::decay<SSeq>::type; + using result_type = typename sseq_type::result_type; + + absl::InlinedVector<result_type, 8> data; + seq.param(std::back_inserter(data)); + return SaltedSeedSeq<sseq_type>(data.begin(), data.end()); +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_SALTED_SEED_SEQ_H_ diff --git a/absl/random/internal/salted_seed_seq_test.cc b/absl/random/internal/salted_seed_seq_test.cc new file mode 100644 index 00000000..0bf19a63 --- /dev/null +++ b/absl/random/internal/salted_seed_seq_test.cc @@ -0,0 +1,168 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/salted_seed_seq.h" + +#include <iterator> +#include <random> +#include <utility> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using absl::random_internal::GetSaltMaterial; +using absl::random_internal::MakeSaltedSeedSeq; +using absl::random_internal::SaltedSeedSeq; +using testing::Eq; +using testing::Pointwise; + +namespace { + +template <typename Sseq> +void ConformsToInterface() { + // Check that the SeedSequence can be default-constructed. + { Sseq default_constructed_seq; } + // Check that the SeedSequence can be constructed with two iterators. + { + uint32_t init_array[] = {1, 3, 5, 7, 9}; + Sseq iterator_constructed_seq(std::begin(init_array), std::end(init_array)); + } + // Check that the SeedSequence can be std::initializer_list-constructed. + { Sseq list_constructed_seq = {1, 3, 5, 7, 9, 11, 13}; } + // Check that param() and size() return state provided to constructor. + { + uint32_t init_array[] = {1, 2, 3, 4, 5}; + Sseq seq(std::begin(init_array), std::end(init_array)); + EXPECT_EQ(seq.size(), ABSL_ARRAYSIZE(init_array)); + + std::vector<uint32_t> state_vector; + seq.param(std::back_inserter(state_vector)); + + EXPECT_EQ(state_vector.size(), ABSL_ARRAYSIZE(init_array)); + for (int i = 0; i < state_vector.size(); i++) { + EXPECT_EQ(state_vector[i], i + 1); + } + } + // Check for presence of generate() method. + { + Sseq seq; + uint32_t seeds[5]; + + seq.generate(std::begin(seeds), std::end(seeds)); + } +} + +TEST(SaltedSeedSeq, CheckInterfaces) { + // Control case + ConformsToInterface<std::seed_seq>(); + + // Abseil classes + ConformsToInterface<SaltedSeedSeq<std::seed_seq>>(); +} + +TEST(SaltedSeedSeq, CheckConstructingFromOtherSequence) { + std::vector<uint32_t> seed_values(10, 1); + std::seed_seq seq(seed_values.begin(), seed_values.end()); + auto salted_seq = MakeSaltedSeedSeq(std::move(seq)); + + EXPECT_EQ(seq.size(), salted_seq.size()); + + std::vector<uint32_t> param_result; + seq.param(std::back_inserter(param_result)); + + EXPECT_EQ(seed_values, param_result); +} + +TEST(SaltedSeedSeq, SaltedSaltedSeedSeqIsNotDoubleSalted) { + uint32_t init[] = {1, 3, 5, 7, 9}; + + std::seed_seq seq(std::begin(init), std::end(init)); + + // The first salting. + SaltedSeedSeq<std::seed_seq> salted_seq = MakeSaltedSeedSeq(std::move(seq)); + uint32_t a[16]; + salted_seq.generate(std::begin(a), std::end(a)); + + // The second salting. + SaltedSeedSeq<std::seed_seq> salted_salted_seq = + MakeSaltedSeedSeq(std::move(salted_seq)); + uint32_t b[16]; + salted_salted_seq.generate(std::begin(b), std::end(b)); + + // ... both should be equal. + EXPECT_THAT(b, Pointwise(Eq(), a)) << "a[0] " << a[0]; +} + +TEST(SaltedSeedSeq, SeedMaterialIsSalted) { + const size_t kNumBlocks = 16; + + uint32_t seed_material[kNumBlocks]; + std::random_device urandom{"/dev/urandom"}; + for (uint32_t& seed : seed_material) { + seed = urandom(); + } + + std::seed_seq seq(std::begin(seed_material), std::end(seed_material)); + SaltedSeedSeq<std::seed_seq> salted_seq(std::begin(seed_material), + std::end(seed_material)); + + bool salt_is_available = GetSaltMaterial().has_value(); + + // If salt is available generated sequence should be different. + if (salt_is_available) { + uint32_t outputs[kNumBlocks]; + uint32_t salted_outputs[kNumBlocks]; + + seq.generate(std::begin(outputs), std::end(outputs)); + salted_seq.generate(std::begin(salted_outputs), std::end(salted_outputs)); + + EXPECT_THAT(outputs, Pointwise(testing::Ne(), salted_outputs)); + } +} + +TEST(SaltedSeedSeq, GenerateAcceptsDifferentTypes) { + const size_t kNumBlocks = 4; + + SaltedSeedSeq<std::seed_seq> seq({1, 2, 3}); + + uint32_t expected[kNumBlocks]; + seq.generate(std::begin(expected), std::end(expected)); + + // 32-bit outputs + { + unsigned long seed_material[kNumBlocks]; // NOLINT(runtime/int) + seq.generate(std::begin(seed_material), std::end(seed_material)); + EXPECT_THAT(seed_material, Pointwise(Eq(), expected)); + } + { + unsigned int seed_material[kNumBlocks]; // NOLINT(runtime/int) + seq.generate(std::begin(seed_material), std::end(seed_material)); + EXPECT_THAT(seed_material, Pointwise(Eq(), expected)); + } + + // 64-bit outputs. + { + uint64_t seed_material[kNumBlocks]; + seq.generate(std::begin(seed_material), std::end(seed_material)); + EXPECT_THAT(seed_material, Pointwise(Eq(), expected)); + } + { + int64_t seed_material[kNumBlocks]; + seq.generate(std::begin(seed_material), std::end(seed_material)); + EXPECT_THAT(seed_material, Pointwise(Eq(), expected)); + } +} + +} // namespace diff --git a/absl/random/internal/seed_material.cc b/absl/random/internal/seed_material.cc new file mode 100644 index 00000000..dae7007f --- /dev/null +++ b/absl/random/internal/seed_material.cc @@ -0,0 +1,207 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/seed_material.h" + +#include <fcntl.h> + +#ifndef _WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif + +#include <algorithm> +#include <cerrno> +#include <cstdint> +#include <cstdlib> +#include <cstring> + +#include "absl/base/internal/raw_logging.h" +#include "absl/strings/ascii.h" +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" + +#if defined(__native_client__) + +#include <nacl/nacl_random.h> +#define ABSL_RANDOM_USE_NACL_SECURE_RANDOM 1 + +#elif defined(_WIN32) + +#include <windows.h> +#define ABSL_RANDOM_USE_BCRYPT 1 +#pragma comment(lib, "bcrypt.lib") + +#endif + +#if defined(ABSL_RANDOM_USE_BCRYPT) +#include <bcrypt.h> + +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#endif +// Also link bcrypt; this can be done via linker options or: +// #pragma comment(lib, "bcrypt.lib") +#endif + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { +namespace { + +// Read OS Entropy for random number seeds. +// TODO(absl-team): Possibly place a cap on how much entropy may be read at a +// time. + +#if defined(ABSL_RANDOM_USE_BCRYPT) + +// On Windows potentially use the BCRYPT CNG API to read available entropy. +bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { + BCRYPT_ALG_HANDLE hProvider; + NTSTATUS ret; + ret = BCryptOpenAlgorithmProvider(&hProvider, BCRYPT_RNG_ALGORITHM, + MS_PRIMITIVE_PROVIDER, 0); + if (!(BCRYPT_SUCCESS(ret))) { + ABSL_RAW_LOG(ERROR, "Failed to open crypto provider."); + return false; + } + ret = BCryptGenRandom( + hProvider, // provider + reinterpret_cast<UCHAR*>(values.data()), // buffer + static_cast<ULONG>(sizeof(uint32_t) * values.size()), // bytes + 0); // flags + BCryptCloseAlgorithmProvider(hProvider, 0); + return BCRYPT_SUCCESS(ret); +} + +#elif defined(ABSL_RANDOM_USE_NACL_SECURE_RANDOM) + +// On NaCL use nacl_secure_random to acquire bytes. +bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { + auto buffer = reinterpret_cast<uint8_t*>(values.data()); + size_t buffer_size = sizeof(uint32_t) * values.size(); + + uint8_t* output_ptr = buffer; + while (buffer_size > 0) { + size_t nread = 0; + const int error = nacl_secure_random(output_ptr, buffer_size, &nread); + if (error != 0 || nread > buffer_size) { + ABSL_RAW_LOG(ERROR, "Failed to read secure_random seed data: %d", error); + return false; + } + output_ptr += nread; + buffer_size -= nread; + } + return true; +} + +#else + +// On *nix, read entropy from /dev/urandom. +bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { + const char kEntropyFile[] = "/dev/urandom"; + + auto buffer = reinterpret_cast<uint8_t*>(values.data()); + size_t buffer_size = sizeof(uint32_t) * values.size(); + + int dev_urandom = open(kEntropyFile, O_RDONLY); + bool success = (-1 != dev_urandom); + if (!success) { + return false; + } + + while (success && buffer_size > 0) { + int bytes_read = read(dev_urandom, buffer, buffer_size); + int read_error = errno; + success = (bytes_read > 0); + if (success) { + buffer += bytes_read; + buffer_size -= bytes_read; + } else if (bytes_read == -1 && read_error == EINTR) { + success = true; // Need to try again. + } + } + close(dev_urandom); + return success; +} + +#endif + +} // namespace + +bool ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values) { + assert(values.data() != nullptr); + if (values.data() == nullptr) { + return false; + } + if (values.empty()) { + return true; + } + return ReadSeedMaterialFromOSEntropyImpl(values); +} + +void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence, + absl::Span<uint32_t> seed_material) { + // Algorithm is based on code available at + // https://gist.github.com/imneme/540829265469e673d045 + constexpr uint32_t kInitVal = 0x43b0d7e5; + constexpr uint32_t kHashMul = 0x931e8875; + constexpr uint32_t kMixMulL = 0xca01f9dd; + constexpr uint32_t kMixMulR = 0x4973f715; + constexpr uint32_t kShiftSize = sizeof(uint32_t) * 8 / 2; + + uint32_t hash_const = kInitVal; + auto hash = [&](uint32_t value) { + value ^= hash_const; + hash_const *= kHashMul; + value *= hash_const; + value ^= value >> kShiftSize; + return value; + }; + + auto mix = [&](uint32_t x, uint32_t y) { + uint32_t result = kMixMulL * x - kMixMulR * y; + result ^= result >> kShiftSize; + return result; + }; + + for (const auto& seq_val : sequence) { + for (auto& elem : seed_material) { + elem = mix(elem, hash(seq_val)); + } + } +} + +absl::optional<uint32_t> GetSaltMaterial() { + // Salt must be common for all generators within the same process so read it + // only once and store in static variable. + static const auto salt_material = []() -> absl::optional<uint32_t> { + uint32_t salt_value = 0; + + if (random_internal::ReadSeedMaterialFromOSEntropy( + MakeSpan(&salt_value, 1))) { + return salt_value; + } + + return absl::nullopt; + }(); + + return salt_material; +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/internal/seed_material.h b/absl/random/internal/seed_material.h new file mode 100644 index 00000000..41387fe3 --- /dev/null +++ b/absl/random/internal/seed_material.h @@ -0,0 +1,104 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_ +#define ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_ + +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <string> +#include <vector> + +#include "absl/base/attributes.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// Returns the number of 32-bit blocks needed to contain the given number of +// bits. +constexpr size_t SeedBitsToBlocks(size_t seed_size) { + return (seed_size + 31) / 32; +} + +// Amount of entropy (measured in bits) used to instantiate a Seed Sequence, +// with which to create a URBG. +constexpr size_t kEntropyBitsNeeded = 256; + +// Amount of entropy (measured in 32-bit blocks) used to instantiate a Seed +// Sequence, with which to create a URBG. +constexpr size_t kEntropyBlocksNeeded = + random_internal::SeedBitsToBlocks(kEntropyBitsNeeded); + +static_assert(kEntropyBlocksNeeded > 0, + "Entropy used to seed URBGs must be nonzero."); + +// Attempts to fill a span of uint32_t-values using an OS-provided source of +// true entropy (eg. /dev/urandom) into an array of uint32_t blocks of data. The +// resulting array may be used to initialize an instance of a class conforming +// to the C++ Standard "Seed Sequence" concept [rand.req.seedseq]. +// +// If values.data() == nullptr, the behavior is undefined. +ABSL_MUST_USE_RESULT +bool ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values); + +// Attempts to fill a span of uint32_t-values using variates generated by an +// existing instance of a class conforming to the C++ Standard "Uniform Random +// Bit Generator" concept [rand.req.urng]. The resulting data may be used to +// initialize an instance of a class conforming to the C++ Standard +// "Seed Sequence" concept [rand.req.seedseq]. +// +// If urbg == nullptr or values.data() == nullptr, the behavior is undefined. +template <typename URBG> +ABSL_MUST_USE_RESULT bool ReadSeedMaterialFromURBG( + URBG* urbg, absl::Span<uint32_t> values) { + random_internal::FastUniformBits<uint32_t> distr; + + assert(urbg != nullptr && values.data() != nullptr); + if (urbg == nullptr || values.data() == nullptr) { + return false; + } + + for (uint32_t& seed_value : values) { + seed_value = distr(*urbg); + } + return true; +} + +// Mixes given sequence of values with into given sequence of seed material. +// Time complexity of this function is O(sequence.size() * +// seed_material.size()). +// +// Algorithm is based on code available at +// https://gist.github.com/imneme/540829265469e673d045 +// by Melissa O'Neill. +void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence, + absl::Span<uint32_t> seed_material); + +// Returns salt value. +// +// Salt is obtained only once and stored in static variable. +// +// May return empty value if optaining the salt was not possible. +absl::optional<uint32_t> GetSaltMaterial(); + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_SEED_MATERIAL_H_ diff --git a/absl/random/internal/seed_material_test.cc b/absl/random/internal/seed_material_test.cc new file mode 100644 index 00000000..6db2820e --- /dev/null +++ b/absl/random/internal/seed_material_test.cc @@ -0,0 +1,202 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/seed_material.h" + +#include <bitset> +#include <cstdlib> +#include <cstring> +#include <random> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#ifdef __ANDROID__ +// Android assert messages only go to system log, so death tests cannot inspect +// the message for matching. +#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH_IF_SUPPORTED(statement, ".*") +#else +#define ABSL_EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH_IF_SUPPORTED(statement, regex) +#endif + +namespace { + +using testing::Each; +using testing::ElementsAre; +using testing::Eq; +using testing::Ne; +using testing::Pointwise; + +TEST(SeedBitsToBlocks, VerifyCases) { + EXPECT_EQ(0, absl::random_internal::SeedBitsToBlocks(0)); + EXPECT_EQ(1, absl::random_internal::SeedBitsToBlocks(1)); + EXPECT_EQ(1, absl::random_internal::SeedBitsToBlocks(31)); + EXPECT_EQ(1, absl::random_internal::SeedBitsToBlocks(32)); + EXPECT_EQ(2, absl::random_internal::SeedBitsToBlocks(33)); + EXPECT_EQ(4, absl::random_internal::SeedBitsToBlocks(127)); + EXPECT_EQ(4, absl::random_internal::SeedBitsToBlocks(128)); + EXPECT_EQ(5, absl::random_internal::SeedBitsToBlocks(129)); +} + +TEST(ReadSeedMaterialFromOSEntropy, SuccessiveReadsAreDistinct) { + constexpr size_t kSeedMaterialSize = 64; + uint32_t seed_material_1[kSeedMaterialSize] = {}; + uint32_t seed_material_2[kSeedMaterialSize] = {}; + + EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromOSEntropy( + absl::Span<uint32_t>(seed_material_1, kSeedMaterialSize))); + EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromOSEntropy( + absl::Span<uint32_t>(seed_material_2, kSeedMaterialSize))); + + EXPECT_THAT(seed_material_1, Pointwise(Ne(), seed_material_2)); +} + +TEST(ReadSeedMaterialFromOSEntropy, ReadZeroBytesIsNoOp) { + uint32_t seed_material[32] = {}; + std::memset(seed_material, 0xAA, sizeof(seed_material)); + EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromOSEntropy( + absl::Span<uint32_t>(seed_material, 0))); + + EXPECT_THAT(seed_material, Each(Eq(0xAAAAAAAA))); +} + +TEST(ReadSeedMaterialFromOSEntropy, NullPtrVectorArgument) { +#ifdef NDEBUG + EXPECT_FALSE(absl::random_internal::ReadSeedMaterialFromOSEntropy( + absl::Span<uint32_t>(nullptr, 32))); +#else + bool result; + ABSL_EXPECT_DEATH_IF_SUPPORTED( + result = absl::random_internal::ReadSeedMaterialFromOSEntropy( + absl::Span<uint32_t>(nullptr, 32)), + "!= nullptr"); + (void)result; // suppress unused-variable warning +#endif +} + +TEST(ReadSeedMaterialFromURBG, SeedMaterialEqualsVariateSequence) { + // Two default-constructed instances of std::mt19937_64 are guaranteed to + // produce equal variate-sequences. + std::mt19937 urbg_1; + std::mt19937 urbg_2; + constexpr size_t kSeedMaterialSize = 1024; + uint32_t seed_material[kSeedMaterialSize] = {}; + + EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromURBG( + &urbg_1, absl::Span<uint32_t>(seed_material, kSeedMaterialSize))); + for (uint32_t seed : seed_material) { + EXPECT_EQ(seed, urbg_2()); + } +} + +TEST(ReadSeedMaterialFromURBG, ReadZeroBytesIsNoOp) { + std::mt19937_64 urbg; + uint32_t seed_material[32]; + std::memset(seed_material, 0xAA, sizeof(seed_material)); + EXPECT_TRUE(absl::random_internal::ReadSeedMaterialFromURBG( + &urbg, absl::Span<uint32_t>(seed_material, 0))); + + EXPECT_THAT(seed_material, Each(Eq(0xAAAAAAAA))); +} + +TEST(ReadSeedMaterialFromURBG, NullUrbgArgument) { + constexpr size_t kSeedMaterialSize = 32; + uint32_t seed_material[kSeedMaterialSize]; +#ifdef NDEBUG + EXPECT_FALSE(absl::random_internal::ReadSeedMaterialFromURBG<std::mt19937_64>( + nullptr, absl::Span<uint32_t>(seed_material, kSeedMaterialSize))); +#else + bool result; + ABSL_EXPECT_DEATH_IF_SUPPORTED( + result = absl::random_internal::ReadSeedMaterialFromURBG<std::mt19937_64>( + nullptr, absl::Span<uint32_t>(seed_material, kSeedMaterialSize)), + "!= nullptr"); + (void)result; // suppress unused-variable warning +#endif +} + +TEST(ReadSeedMaterialFromURBG, NullPtrVectorArgument) { + std::mt19937_64 urbg; +#ifdef NDEBUG + EXPECT_FALSE(absl::random_internal::ReadSeedMaterialFromURBG( + &urbg, absl::Span<uint32_t>(nullptr, 32))); +#else + bool result; + ABSL_EXPECT_DEATH_IF_SUPPORTED( + result = absl::random_internal::ReadSeedMaterialFromURBG( + &urbg, absl::Span<uint32_t>(nullptr, 32)), + "!= nullptr"); + (void)result; // suppress unused-variable warning +#endif +} + +// The avalanche effect is a desirable cryptographic property of hashes in which +// changing a single bit in the input causes each bit of the output to be +// changed with probability near 50%. +// +// https://en.wikipedia.org/wiki/Avalanche_effect + +TEST(MixSequenceIntoSeedMaterial, AvalancheEffectTestOneBitLong) { + std::vector<uint32_t> seed_material = {1, 2, 3, 4, 5, 6, 7, 8}; + + // For every 32-bit number with exactly one bit set, verify the avalanche + // effect holds. In order to reduce flakiness of tests, accept values + // anywhere in the range of 30%-70%. + for (uint32_t v = 1; v != 0; v <<= 1) { + std::vector<uint32_t> seed_material_copy = seed_material; + absl::random_internal::MixIntoSeedMaterial( + absl::Span<uint32_t>(&v, 1), + absl::Span<uint32_t>(seed_material_copy.data(), + seed_material_copy.size())); + + uint32_t changed_bits = 0; + for (size_t i = 0; i < seed_material.size(); i++) { + std::bitset<sizeof(uint32_t) * 8> bitset(seed_material[i] ^ + seed_material_copy[i]); + changed_bits += bitset.count(); + } + + EXPECT_LE(changed_bits, 0.7 * sizeof(uint32_t) * 8 * seed_material.size()); + EXPECT_GE(changed_bits, 0.3 * sizeof(uint32_t) * 8 * seed_material.size()); + } +} + +TEST(MixSequenceIntoSeedMaterial, AvalancheEffectTestOneBitShort) { + std::vector<uint32_t> seed_material = {1}; + + // For every 32-bit number with exactly one bit set, verify the avalanche + // effect holds. In order to reduce flakiness of tests, accept values + // anywhere in the range of 30%-70%. + for (uint32_t v = 1; v != 0; v <<= 1) { + std::vector<uint32_t> seed_material_copy = seed_material; + absl::random_internal::MixIntoSeedMaterial( + absl::Span<uint32_t>(&v, 1), + absl::Span<uint32_t>(seed_material_copy.data(), + seed_material_copy.size())); + + uint32_t changed_bits = 0; + for (size_t i = 0; i < seed_material.size(); i++) { + std::bitset<sizeof(uint32_t) * 8> bitset(seed_material[i] ^ + seed_material_copy[i]); + changed_bits += bitset.count(); + } + + EXPECT_LE(changed_bits, 0.7 * sizeof(uint32_t) * 8 * seed_material.size()); + EXPECT_GE(changed_bits, 0.3 * sizeof(uint32_t) * 8 * seed_material.size()); + } +} + +} // namespace diff --git a/absl/random/internal/seed_salting_sequence_generator.cc b/absl/random/internal/seed_salting_sequence_generator.cc new file mode 100644 index 00000000..31fdcfe1 --- /dev/null +++ b/absl/random/internal/seed_salting_sequence_generator.cc @@ -0,0 +1,30 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <iostream> +#include <random> + +#include "absl/random/random.h" + +// This program is used in integration tests. + +int main() { + std::seed_seq seed_seq{1234}; + absl::BitGen rng(seed_seq); + constexpr size_t kSequenceLength = 8; + for (size_t i = 0; i < kSequenceLength; i++) { + std::cout << rng() << "\n"; + } + return 0; +} diff --git a/absl/random/internal/seed_salting_sequence_generator_empty_sequence.cc b/absl/random/internal/seed_salting_sequence_generator_empty_sequence.cc new file mode 100644 index 00000000..8797e2e7 --- /dev/null +++ b/absl/random/internal/seed_salting_sequence_generator_empty_sequence.cc @@ -0,0 +1,30 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <iostream> +#include <random> + +#include "absl/random/random.h" + +// This program is used in integration tests. + +int main() { + std::seed_seq seed_seq{}; + absl::BitGen rng(seed_seq); + constexpr size_t kSequenceLength = 8; + for (size_t i = 0; i < kSequenceLength; i++) { + std::cout << rng() << "\n"; + } + return 0; +} diff --git a/absl/random/internal/sequence_urbg.h b/absl/random/internal/sequence_urbg.h new file mode 100644 index 00000000..cec0bf9b --- /dev/null +++ b/absl/random/internal/sequence_urbg.h @@ -0,0 +1,58 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_ +#define ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_ + +#include <cstdint> +#include <cstring> +#include <limits> +#include <type_traits> +#include <vector> + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// `sequence_urbg` is a simple random number generator which meets the +// requirements of [rand.req.urbg], and is solely for testing absl +// distributions. +class sequence_urbg { + public: + using result_type = uint64_t; + + static constexpr result_type(min)() { + return (std::numeric_limits<result_type>::min)(); + } + static constexpr result_type(max)() { + return (std::numeric_limits<result_type>::max)(); + } + + sequence_urbg(std::initializer_list<result_type> data) : i_(0), data_(data) {} + void reset() { i_ = 0; } + + result_type operator()() { return data_[i_++ % data_.size()]; } + + size_t invocations() const { return i_; } + + private: + size_t i_; + std::vector<result_type> data_; +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_SEQUENCE_URBG_H_ diff --git a/absl/random/internal/traits.h b/absl/random/internal/traits.h new file mode 100644 index 00000000..9f7d126c --- /dev/null +++ b/absl/random/internal/traits.h @@ -0,0 +1,101 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_TRAITS_H_ +#define ABSL_RANDOM_INTERNAL_TRAITS_H_ + +#include <cstdint> +#include <limits> +#include <type_traits> + +#include "absl/base/config.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace random_internal { + +// random_internal::is_widening_convertible<A, B> +// +// Returns whether a type A is widening-convertible to a type B. +// +// A is widening-convertible to B means: +// A a = <any number>; +// B b = a; +// A c = b; +// EXPECT_EQ(a, c); +template <typename A, typename B> +class is_widening_convertible { + // As long as there are enough bits in the exact part of a number: + // - unsigned can fit in float, signed, unsigned + // - signed can fit in float, signed + // - float can fit in float + // So we define rank to be: + // - rank(float) -> 2 + // - rank(signed) -> 1 + // - rank(unsigned) -> 0 + template <class T> + static constexpr int rank() { + return !std::numeric_limits<T>::is_integer + + std::numeric_limits<T>::is_signed; + } + + public: + // If an arithmetic-type B can represent at least as many digits as a type A, + // and B belongs to a rank no lower than A, then A can be safely represented + // by B through a widening-conversion. + static constexpr bool value = + std::numeric_limits<A>::digits <= std::numeric_limits<B>::digits && + rank<A>() <= rank<B>(); +}; + +// unsigned_bits<N>::type returns the unsigned int type with the indicated +// number of bits. +template <size_t N> +struct unsigned_bits; + +template <> +struct unsigned_bits<8> { + using type = uint8_t; +}; +template <> +struct unsigned_bits<16> { + using type = uint16_t; +}; +template <> +struct unsigned_bits<32> { + using type = uint32_t; +}; +template <> +struct unsigned_bits<64> { + using type = uint64_t; +}; + +#ifdef ABSL_HAVE_INTRINSIC_INT128 +template <> +struct unsigned_bits<128> { + using type = __uint128_t; +}; +#endif + +template <typename IntType> +struct make_unsigned_bits { + using type = typename unsigned_bits<std::numeric_limits< + typename std::make_unsigned<IntType>::type>::digits>::type; +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_TRAITS_H_ diff --git a/absl/random/internal/traits_test.cc b/absl/random/internal/traits_test.cc new file mode 100644 index 00000000..a844887d --- /dev/null +++ b/absl/random/internal/traits_test.cc @@ -0,0 +1,126 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/traits.h" + +#include <cstdint> +#include <type_traits> + +#include "gtest/gtest.h" + +namespace { + +using absl::random_internal::is_widening_convertible; + +// CheckWideningConvertsToSelf<T1, T2, ...>() +// +// For each type T, checks: +// - T IS widening-convertible to itself. +// +template <typename T> +void CheckWideningConvertsToSelf() { + static_assert(is_widening_convertible<T, T>::value, + "Type is not convertible to self!"); +} + +template <typename T, typename Next, typename... Args> +void CheckWideningConvertsToSelf() { + CheckWideningConvertsToSelf<T>(); + CheckWideningConvertsToSelf<Next, Args...>(); +} + +// CheckNotWideningConvertibleWithSigned<T1, T2, ...>() +// +// For each unsigned-type T, checks that: +// - T is NOT widening-convertible to Signed(T) +// - Signed(T) is NOT widening-convertible to T +// +template <typename T> +void CheckNotWideningConvertibleWithSigned() { + using signed_t = typename std::make_signed<T>::type; + + static_assert(!is_widening_convertible<T, signed_t>::value, + "Unsigned type is convertible to same-sized signed-type!"); + static_assert(!is_widening_convertible<signed_t, T>::value, + "Signed type is convertible to same-sized unsigned-type!"); +} + +template <typename T, typename Next, typename... Args> +void CheckNotWideningConvertibleWithSigned() { + CheckNotWideningConvertibleWithSigned<T>(); + CheckWideningConvertsToSelf<Next, Args...>(); +} + +// CheckWideningConvertsToLargerType<T1, T2, ...>() +// +// For each successive unsigned-types {Ti, Ti+1}, checks that: +// - Ti IS widening-convertible to Ti+1 +// - Ti IS widening-convertible to Signed(Ti+1) +// - Signed(Ti) is NOT widening-convertible to Ti +// - Signed(Ti) IS widening-convertible to Ti+1 +template <typename T, typename Higher> +void CheckWideningConvertsToLargerTypes() { + using signed_t = typename std::make_signed<T>::type; + using higher_t = Higher; + using signed_higher_t = typename std::make_signed<Higher>::type; + + static_assert(is_widening_convertible<T, higher_t>::value, + "Type not embeddable into larger type!"); + static_assert(is_widening_convertible<T, signed_higher_t>::value, + "Type not embeddable into larger signed type!"); + static_assert(!is_widening_convertible<signed_t, higher_t>::value, + "Signed type is embeddable into larger unsigned type!"); + static_assert(is_widening_convertible<signed_t, signed_higher_t>::value, + "Signed type not embeddable into larger signed type!"); +} + +template <typename T, typename Higher, typename Next, typename... Args> +void CheckWideningConvertsToLargerTypes() { + CheckWideningConvertsToLargerTypes<T, Higher>(); + CheckWideningConvertsToLargerTypes<Higher, Next, Args...>(); +} + +// CheckWideningConvertsTo<T, U, [expect]> +// +// Checks that T DOES widening-convert to U. +// If "expect" is false, then asserts that T does NOT widening-convert to U. +template <typename T, typename U, bool expect = true> +void CheckWideningConvertsTo() { + static_assert(is_widening_convertible<T, U>::value == expect, + "Unexpected result for is_widening_convertible<T, U>!"); +} + +TEST(TraitsTest, IsWideningConvertibleTest) { + constexpr bool kInvalid = false; + + CheckWideningConvertsToSelf< + uint8_t, uint16_t, uint32_t, uint64_t, + int8_t, int16_t, int32_t, int64_t, + float, double>(); + CheckNotWideningConvertibleWithSigned< + uint8_t, uint16_t, uint32_t, uint64_t>(); + CheckWideningConvertsToLargerTypes< + uint8_t, uint16_t, uint32_t, uint64_t>(); + + CheckWideningConvertsTo<float, double>(); + CheckWideningConvertsTo<uint16_t, float>(); + CheckWideningConvertsTo<uint32_t, double>(); + CheckWideningConvertsTo<uint64_t, double, kInvalid>(); + CheckWideningConvertsTo<double, float, kInvalid>(); + + CheckWideningConvertsTo<bool, int>(); + CheckWideningConvertsTo<bool, float>(); +} + +} // namespace diff --git a/absl/random/internal/uniform_helper.h b/absl/random/internal/uniform_helper.h new file mode 100644 index 00000000..6af053ef --- /dev/null +++ b/absl/random/internal/uniform_helper.h @@ -0,0 +1,152 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_ +#define ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_ + +#include <cmath> +#include <limits> +#include <type_traits> + +#include "absl/meta/type_traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { +template <typename IntType> +class uniform_int_distribution; + +template <typename RealType> +class uniform_real_distribution; + +// Interval tag types which specify whether the interval is open or closed +// on either boundary. +namespace random_internal { +struct IntervalClosedClosedT {}; +struct IntervalClosedOpenT {}; +struct IntervalOpenClosedT {}; +struct IntervalOpenOpenT {}; +} // namespace random_internal + +namespace random_internal { + +// The functions +// uniform_lower_bound(tag, a, b) +// and +// uniform_upper_bound(tag, a, b) +// are used as implementation-details for absl::Uniform(). +// +// Conceptually, +// [a, b] == [uniform_lower_bound(IntervalClosedClosed, a, b), +// uniform_upper_bound(IntervalClosedClosed, a, b)] +// (a, b) == [uniform_lower_bound(IntervalOpenOpen, a, b), +// uniform_upper_bound(IntervalOpenOpen, a, b)] +// [a, b) == [uniform_lower_bound(IntervalClosedOpen, a, b), +// uniform_upper_bound(IntervalClosedOpen, a, b)] +// (a, b] == [uniform_lower_bound(IntervalOpenClosed, a, b), +// uniform_upper_bound(IntervalOpenClosed, a, b)] +// +template <typename IntType, typename Tag> +typename absl::enable_if_t< + absl::conjunction< + std::is_integral<IntType>, + absl::disjunction<std::is_same<Tag, IntervalOpenClosedT>, + std::is_same<Tag, IntervalOpenOpenT>>>::value, + IntType> +uniform_lower_bound(Tag, IntType a, IntType) { + return a + 1; +} + +template <typename FloatType, typename Tag> +typename absl::enable_if_t< + absl::conjunction< + std::is_floating_point<FloatType>, + absl::disjunction<std::is_same<Tag, IntervalOpenClosedT>, + std::is_same<Tag, IntervalOpenOpenT>>>::value, + FloatType> +uniform_lower_bound(Tag, FloatType a, FloatType b) { + return std::nextafter(a, b); +} + +template <typename NumType, typename Tag> +typename absl::enable_if_t< + absl::disjunction<std::is_same<Tag, IntervalClosedClosedT>, + std::is_same<Tag, IntervalClosedOpenT>>::value, + NumType> +uniform_lower_bound(Tag, NumType a, NumType) { + return a; +} + +template <typename IntType, typename Tag> +typename absl::enable_if_t< + absl::conjunction< + std::is_integral<IntType>, + absl::disjunction<std::is_same<Tag, IntervalClosedOpenT>, + std::is_same<Tag, IntervalOpenOpenT>>>::value, + IntType> +uniform_upper_bound(Tag, IntType, IntType b) { + return b - 1; +} + +template <typename FloatType, typename Tag> +typename absl::enable_if_t< + absl::conjunction< + std::is_floating_point<FloatType>, + absl::disjunction<std::is_same<Tag, IntervalClosedOpenT>, + std::is_same<Tag, IntervalOpenOpenT>>>::value, + FloatType> +uniform_upper_bound(Tag, FloatType, FloatType b) { + return b; +} + +template <typename IntType, typename Tag> +typename absl::enable_if_t< + absl::conjunction< + std::is_integral<IntType>, + absl::disjunction<std::is_same<Tag, IntervalClosedClosedT>, + std::is_same<Tag, IntervalOpenClosedT>>>::value, + IntType> +uniform_upper_bound(Tag, IntType, IntType b) { + return b; +} + +template <typename FloatType, typename Tag> +typename absl::enable_if_t< + absl::conjunction< + std::is_floating_point<FloatType>, + absl::disjunction<std::is_same<Tag, IntervalClosedClosedT>, + std::is_same<Tag, IntervalOpenClosedT>>>::value, + FloatType> +uniform_upper_bound(Tag, FloatType, FloatType b) { + return std::nextafter(b, (std::numeric_limits<FloatType>::max)()); +} + +template <typename NumType> +using UniformDistribution = + typename std::conditional<std::is_integral<NumType>::value, + absl::uniform_int_distribution<NumType>, + absl::uniform_real_distribution<NumType>>::type; + +template <typename TagType, typename NumType> +struct UniformDistributionWrapper : public UniformDistribution<NumType> { + explicit UniformDistributionWrapper(NumType lo, NumType hi) + : UniformDistribution<NumType>( + uniform_lower_bound<NumType>(TagType{}, lo, hi), + uniform_upper_bound<NumType>(TagType{}, lo, hi)) {} +}; + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_ diff --git a/absl/random/log_uniform_int_distribution.h b/absl/random/log_uniform_int_distribution.h new file mode 100644 index 00000000..a12fa4cb --- /dev/null +++ b/absl/random/log_uniform_int_distribution.h @@ -0,0 +1,252 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_ +#define ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_ + +#include <algorithm> +#include <cassert> +#include <cmath> +#include <istream> +#include <limits> +#include <ostream> +#include <type_traits> + +#include "absl/random/internal/distribution_impl.h" +#include "absl/random/internal/fastmath.h" +#include "absl/random/internal/iostream_state_saver.h" +#include "absl/random/internal/traits.h" +#include "absl/random/uniform_int_distribution.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// log_uniform_int_distribution: +// +// Returns a random variate R in range [min, max] such that +// floor(log(R-min, base)) is uniformly distributed. +// We ensure uniformity by discretization using the +// boundary sets [0, 1, base, base * base, ... min(base*n, max)] +// +template <typename IntType = int> +class log_uniform_int_distribution { + private: + using unsigned_type = + typename random_internal::make_unsigned_bits<IntType>::type; + + public: + using result_type = IntType; + + class param_type { + public: + using distribution_type = log_uniform_int_distribution; + + explicit param_type( + result_type min = 0, + result_type max = (std::numeric_limits<result_type>::max)(), + result_type base = 2) + : min_(min), + max_(max), + base_(base), + range_(static_cast<unsigned_type>(max_) - + static_cast<unsigned_type>(min_)), + log_range_(0) { + assert(max_ >= min_); + assert(base_ > 1); + + if (base_ == 2) { + // Determine where the first set bit is on range(), giving a log2(range) + // value which can be used to construct bounds. + log_range_ = (std::min)(random_internal::LeadingSetBit(range()), + std::numeric_limits<unsigned_type>::digits); + } else { + // NOTE: Computing the logN(x) introduces error from 2 sources: + // 1. Conversion of int to double loses precision for values >= + // 2^53, which may cause some log() computations to operate on + // different values. + // 2. The error introduced by the division will cause the result + // to differ from the expected value. + // + // Thus a result which should equal K may equal K +/- epsilon, + // which can eliminate some values depending on where the bounds fall. + const double inv_log_base = 1.0 / std::log(base_); + const double log_range = std::log(static_cast<double>(range()) + 0.5); + log_range_ = static_cast<int>(std::ceil(inv_log_base * log_range)); + } + } + + result_type(min)() const { return min_; } + result_type(max)() const { return max_; } + result_type base() const { return base_; } + + friend bool operator==(const param_type& a, const param_type& b) { + return a.min_ == b.min_ && a.max_ == b.max_ && a.base_ == b.base_; + } + + friend bool operator!=(const param_type& a, const param_type& b) { + return !(a == b); + } + + private: + friend class log_uniform_int_distribution; + + int log_range() const { return log_range_; } + unsigned_type range() const { return range_; } + + result_type min_; + result_type max_; + result_type base_; + unsigned_type range_; // max - min + int log_range_; // ceil(logN(range_)) + + static_assert(std::is_integral<IntType>::value, + "Class-template absl::log_uniform_int_distribution<> must be " + "parameterized using an integral type."); + }; + + log_uniform_int_distribution() : log_uniform_int_distribution(0) {} + + explicit log_uniform_int_distribution( + result_type min, + result_type max = (std::numeric_limits<result_type>::max)(), + result_type base = 2) + : param_(min, max, base) {} + + explicit log_uniform_int_distribution(const param_type& p) : param_(p) {} + + void reset() {} + + // generating functions + template <typename URBG> + result_type operator()(URBG& g) { // NOLINT(runtime/references) + return (*this)(g, param_); + } + + template <typename URBG> + result_type operator()(URBG& g, // NOLINT(runtime/references) + const param_type& p) { + return (p.min)() + Generate(g, p); + } + + result_type(min)() const { return (param_.min)(); } + result_type(max)() const { return (param_.max)(); } + result_type base() const { return param_.base(); } + + param_type param() const { return param_; } + void param(const param_type& p) { param_ = p; } + + friend bool operator==(const log_uniform_int_distribution& a, + const log_uniform_int_distribution& b) { + return a.param_ == b.param_; + } + friend bool operator!=(const log_uniform_int_distribution& a, + const log_uniform_int_distribution& b) { + return a.param_ != b.param_; + } + + private: + // Returns a log-uniform variate in the range [0, p.range()]. The caller + // should add min() to shift the result to the correct range. + template <typename URNG> + unsigned_type Generate(URNG& g, // NOLINT(runtime/references) + const param_type& p); + + param_type param_; +}; + +template <typename IntType> +template <typename URBG> +typename log_uniform_int_distribution<IntType>::unsigned_type +log_uniform_int_distribution<IntType>::Generate( + URBG& g, // NOLINT(runtime/references) + const param_type& p) { + // sample e over [0, log_range]. Map the results of e to this: + // 0 => 0 + // 1 => [1, b-1] + // 2 => [b, (b^2)-1] + // n => [b^(n-1)..(b^n)-1] + const int e = absl::uniform_int_distribution<int>(0, p.log_range())(g); + if (e == 0) { + return 0; + } + const int d = e - 1; + + unsigned_type base_e, top_e; + if (p.base() == 2) { + base_e = static_cast<unsigned_type>(1) << d; + + top_e = (e >= std::numeric_limits<unsigned_type>::digits) + ? (std::numeric_limits<unsigned_type>::max)() + : (static_cast<unsigned_type>(1) << e) - 1; + } else { + const double r = std::pow(p.base(), d); + const double s = (r * p.base()) - 1.0; + + base_e = (r > (std::numeric_limits<unsigned_type>::max)()) + ? (std::numeric_limits<unsigned_type>::max)() + : static_cast<unsigned_type>(r); + + top_e = (s > (std::numeric_limits<unsigned_type>::max)()) + ? (std::numeric_limits<unsigned_type>::max)() + : static_cast<unsigned_type>(s); + } + + const unsigned_type lo = (base_e >= p.range()) ? p.range() : base_e; + const unsigned_type hi = (top_e >= p.range()) ? p.range() : top_e; + + // choose uniformly over [lo, hi] + return absl::uniform_int_distribution<result_type>(lo, hi)(g); +} + +template <typename CharT, typename Traits, typename IntType> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const log_uniform_int_distribution<IntType>& x) { + using stream_type = + typename random_internal::stream_format_type<IntType>::type; + auto saver = random_internal::make_ostream_state_saver(os); + os << static_cast<stream_type>((x.min)()) << os.fill() + << static_cast<stream_type>((x.max)()) << os.fill() + << static_cast<stream_type>(x.base()); + return os; +} + +template <typename CharT, typename Traits, typename IntType> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + log_uniform_int_distribution<IntType>& x) { // NOLINT(runtime/references) + using param_type = typename log_uniform_int_distribution<IntType>::param_type; + using result_type = + typename log_uniform_int_distribution<IntType>::result_type; + using stream_type = + typename random_internal::stream_format_type<IntType>::type; + + stream_type min; + stream_type max; + stream_type base; + + auto saver = random_internal::make_istream_state_saver(is); + is >> min >> max >> base; + if (!is.fail()) { + x.param(param_type(static_cast<result_type>(min), + static_cast<result_type>(max), + static_cast<result_type>(base))); + } + return is; +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_LOG_UNIFORM_INT_DISTRIBUTION_H_ diff --git a/absl/random/log_uniform_int_distribution_test.cc b/absl/random/log_uniform_int_distribution_test.cc new file mode 100644 index 00000000..0ff4c32d --- /dev/null +++ b/absl/random/log_uniform_int_distribution_test.cc @@ -0,0 +1,277 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/log_uniform_int_distribution.h" + +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <random> +#include <sstream> +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/chi_square.h" +#include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/strip.h" + +namespace { + +template <typename IntType> +class LogUniformIntDistributionTypeTest : public ::testing::Test {}; + +using IntTypes = ::testing::Types<int8_t, int16_t, int32_t, int64_t, // + uint8_t, uint16_t, uint32_t, uint64_t>; +TYPED_TEST_CASE(LogUniformIntDistributionTypeTest, IntTypes); + +TYPED_TEST(LogUniformIntDistributionTypeTest, SerializeTest) { + using param_type = + typename absl::log_uniform_int_distribution<TypeParam>::param_type; + using Limits = std::numeric_limits<TypeParam>; + + constexpr int kCount = 1000; + absl::InsecureBitGen gen; + for (const auto& param : { + param_type(0, 1), // + param_type(0, 2), // + param_type(0, 2, 10), // + param_type(9, 32, 4), // + param_type(1, 101, 10), // + param_type(1, Limits::max() / 2), // + param_type(0, Limits::max() - 1), // + param_type(0, Limits::max(), 2), // + param_type(0, Limits::max(), 10), // + param_type(Limits::min(), 0), // + param_type(Limits::lowest(), Limits::max()), // + param_type(Limits::min(), Limits::max()), // + }) { + // Validate parameters. + const auto min = param.min(); + const auto max = param.max(); + const auto base = param.base(); + absl::log_uniform_int_distribution<TypeParam> before(min, max, base); + EXPECT_EQ(before.min(), param.min()); + EXPECT_EQ(before.max(), param.max()); + EXPECT_EQ(before.base(), param.base()); + + { + absl::log_uniform_int_distribution<TypeParam> via_param(param); + EXPECT_EQ(via_param, before); + } + + // Validate stream serialization. + std::stringstream ss; + ss << before; + + absl::log_uniform_int_distribution<TypeParam> after(3, 6, 17); + + EXPECT_NE(before.max(), after.max()); + EXPECT_NE(before.base(), after.base()); + EXPECT_NE(before.param(), after.param()); + EXPECT_NE(before, after); + + ss >> after; + + EXPECT_EQ(before.min(), after.min()); + EXPECT_EQ(before.max(), after.max()); + EXPECT_EQ(before.base(), after.base()); + EXPECT_EQ(before.param(), after.param()); + EXPECT_EQ(before, after); + + // Smoke test. + auto sample_min = after.max(); + auto sample_max = after.min(); + for (int i = 0; i < kCount; i++) { + auto sample = after(gen); + EXPECT_GE(sample, after.min()); + EXPECT_LE(sample, after.max()); + if (sample > sample_max) sample_max = sample; + if (sample < sample_min) sample_min = sample; + } + ABSL_INTERNAL_LOG(INFO, + absl::StrCat("Range: ", +sample_min, ", ", +sample_max)); + } +} + +using log_uniform_i32 = absl::log_uniform_int_distribution<int32_t>; + +class LogUniformIntChiSquaredTest + : public testing::TestWithParam<log_uniform_i32::param_type> { + public: + // The ChiSquaredTestImpl provides a chi-squared goodness of fit test for + // data generated by the log-uniform-int distribution. + double ChiSquaredTestImpl(); + + absl::InsecureBitGen rng_; +}; + +double LogUniformIntChiSquaredTest::ChiSquaredTestImpl() { + using absl::random_internal::kChiSquared; + + const auto& param = GetParam(); + + // Check the distribution of L=log(log_uniform_int_distribution, base), + // expecting that L is roughly uniformly distributed, that is: + // + // P[L=0] ~= P[L=1] ~= ... ~= P[L=log(max)] + // + // For a total of X entries, each bucket should contain some number of samples + // in the interval [X/k - a, X/k + a]. + // + // Where `a` is approximately sqrt(X/k). This is validated by bucketing + // according to the log function and using a chi-squared test for uniformity. + + const bool is_2 = (param.base() == 2); + const double base_log = 1.0 / std::log(param.base()); + const auto bucket_index = [base_log, is_2, ¶m](int32_t x) { + uint64_t y = static_cast<uint64_t>(x) - param.min(); + return (y == 0) ? 0 + : is_2 ? static_cast<int>(1 + std::log2(y)) + : static_cast<int>(1 + std::log(y) * base_log); + }; + const int max_bucket = bucket_index(param.max()); // inclusive + const size_t trials = 15 + (max_bucket + 1) * 10; + + log_uniform_i32 dist(param); + + std::vector<int64_t> buckets(max_bucket + 1); + for (size_t i = 0; i < trials; ++i) { + const auto sample = dist(rng_); + // Check the bounds. + ABSL_ASSERT(sample <= dist.max()); + ABSL_ASSERT(sample >= dist.min()); + // Convert the output of the generator to one of num_bucket buckets. + int bucket = bucket_index(sample); + ABSL_ASSERT(bucket <= max_bucket); + ++buckets[bucket]; + } + + // The null-hypothesis is that the distribution is uniform with respect to + // log-uniform-int bucketization. + const int dof = buckets.size() - 1; + const double expected = trials / static_cast<double>(buckets.size()); + + const double threshold = absl::random_internal::ChiSquareValue(dof, 0.98); + + double chi_square = absl::random_internal::ChiSquareWithExpected( + std::begin(buckets), std::end(buckets), expected); + + const double p = absl::random_internal::ChiSquarePValue(chi_square, dof); + + if (chi_square > threshold) { + ABSL_INTERNAL_LOG(INFO, "values"); + for (size_t i = 0; i < buckets.size(); i++) { + ABSL_INTERNAL_LOG(INFO, absl::StrCat(i, ": ", buckets[i])); + } + ABSL_INTERNAL_LOG(INFO, + absl::StrFormat("trials=%d\n" + "%s(data, %d) = %f (%f)\n" + "%s @ 0.98 = %f", + trials, kChiSquared, dof, chi_square, p, + kChiSquared, threshold)); + } + return p; +} + +TEST_P(LogUniformIntChiSquaredTest, MultiTest) { + const int kTrials = 5; + + int failures = 0; + for (int i = 0; i < kTrials; i++) { + double p_value = ChiSquaredTestImpl(); + if (p_value < 0.005) { + failures++; + } + } + + // There is a 0.10% chance of producing at least one failure, so raise the + // failure threshold high enough to allow for a flake rate < 10,000. + EXPECT_LE(failures, 4); +} + +// Generate the parameters for the test. +std::vector<log_uniform_i32::param_type> GenParams() { + using Param = log_uniform_i32::param_type; + using Limits = std::numeric_limits<int32_t>; + + return std::vector<Param>{ + Param{0, 1, 2}, + Param{1, 1, 2}, + Param{0, 2, 2}, + Param{0, 3, 2}, + Param{0, 4, 2}, + Param{0, 9, 10}, + Param{0, 10, 10}, + Param{0, 11, 10}, + Param{1, 10, 10}, + Param{0, (1 << 8) - 1, 2}, + Param{0, (1 << 8), 2}, + Param{0, (1 << 30) - 1, 2}, + Param{-1000, 1000, 10}, + Param{0, Limits::max(), 2}, + Param{0, Limits::max(), 3}, + Param{0, Limits::max(), 10}, + Param{Limits::min(), 0}, + Param{Limits::min(), Limits::max(), 2}, + }; +} + +std::string ParamName( + const ::testing::TestParamInfo<log_uniform_i32::param_type>& info) { + const auto& p = info.param; + std::string name = + absl::StrCat("min_", p.min(), "__max_", p.max(), "__base_", p.base()); + return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); +} + +INSTANTIATE_TEST_SUITE_P(, LogUniformIntChiSquaredTest, + ::testing::ValuesIn(GenParams()), ParamName); + +// NOTE: absl::log_uniform_int_distribution is not guaranteed to be stable. +TEST(LogUniformIntDistributionTest, StabilityTest) { + using testing::ElementsAre; + // absl::uniform_int_distribution stability relies on + // absl::random_internal::LeadingSetBit, std::log, std::pow. + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector<int> output(6); + + { + absl::log_uniform_int_distribution<int32_t> dist(0, 256); + std::generate(std::begin(output), std::end(output), + [&] { return dist(urbg); }); + EXPECT_THAT(output, ElementsAre(256, 66, 4, 6, 57, 103)); + } + urbg.reset(); + { + absl::log_uniform_int_distribution<int32_t> dist(0, 256, 10); + std::generate(std::begin(output), std::end(output), + [&] { return dist(urbg); }); + EXPECT_THAT(output, ElementsAre(8, 4, 0, 0, 0, 69)); + } +} + +} // namespace diff --git a/absl/random/poisson_distribution.h b/absl/random/poisson_distribution.h new file mode 100644 index 00000000..66c75091 --- /dev/null +++ b/absl/random/poisson_distribution.h @@ -0,0 +1,256 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_POISSON_DISTRIBUTION_H_ +#define ABSL_RANDOM_POISSON_DISTRIBUTION_H_ + +#include <cassert> +#include <cmath> +#include <istream> +#include <limits> +#include <ostream> +#include <type_traits> + +#include "absl/random/internal/distribution_impl.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/fastmath.h" +#include "absl/random/internal/iostream_state_saver.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// absl::poisson_distribution: +// Generates discrete variates conforming to a Poisson distribution. +// p(n) = (mean^n / n!) exp(-mean) +// +// Depending on the parameter, the distribution selects one of the following +// algorithms: +// * The standard algorithm, attributed to Knuth, extended using a split method +// for larger values +// * The "Ratio of Uniforms as a convenient method for sampling from classical +// discrete distributions", Stadlober, 1989. +// http://www.sciencedirect.com/science/article/pii/0377042790903495 +// +// NOTE: param_type.mean() is a double, which permits values larger than +// poisson_distribution<IntType>::max(), however this should be avoided and +// the distribution results are limited to the max() value. +// +// The goals of this implementation are to provide good performance while still +// beig thread-safe: This limits the implementation to not using lgamma provided +// by <math.h>. +// +template <typename IntType = int> +class poisson_distribution { + public: + using result_type = IntType; + + class param_type { + public: + using distribution_type = poisson_distribution; + explicit param_type(double mean = 1.0); + + double mean() const { return mean_; } + + friend bool operator==(const param_type& a, const param_type& b) { + return a.mean_ == b.mean_; + } + + friend bool operator!=(const param_type& a, const param_type& b) { + return !(a == b); + } + + private: + friend class poisson_distribution; + + double mean_; + double emu_; // e ^ -mean_ + double lmu_; // ln(mean_) + double s_; + double log_k_; + int split_; + + static_assert(std::is_integral<IntType>::value, + "Class-template absl::poisson_distribution<> must be " + "parameterized using an integral type."); + }; + + poisson_distribution() : poisson_distribution(1.0) {} + + explicit poisson_distribution(double mean) : param_(mean) {} + + explicit poisson_distribution(const param_type& p) : param_(p) {} + + void reset() {} + + // generating functions + template <typename URBG> + result_type operator()(URBG& g) { // NOLINT(runtime/references) + return (*this)(g, param_); + } + + template <typename URBG> + result_type operator()(URBG& g, // NOLINT(runtime/references) + const param_type& p); + + param_type param() const { return param_; } + void param(const param_type& p) { param_ = p; } + + result_type(min)() const { return 0; } + result_type(max)() const { return (std::numeric_limits<result_type>::max)(); } + + double mean() const { return param_.mean(); } + + friend bool operator==(const poisson_distribution& a, + const poisson_distribution& b) { + return a.param_ == b.param_; + } + friend bool operator!=(const poisson_distribution& a, + const poisson_distribution& b) { + return a.param_ != b.param_; + } + + private: + param_type param_; + random_internal::FastUniformBits<uint64_t> fast_u64_; +}; + +// ----------------------------------------------------------------------------- +// Implementation details follow +// ----------------------------------------------------------------------------- + +template <typename IntType> +poisson_distribution<IntType>::param_type::param_type(double mean) + : mean_(mean), split_(0) { + assert(mean >= 0); + assert(mean <= (std::numeric_limits<result_type>::max)()); + // As a defensive measure, avoid large values of the mean. The rejection + // algorithm used does not support very large values well. It my be worth + // changing algorithms to better deal with these cases. + assert(mean <= 1e10); + if (mean_ < 10) { + // For small lambda, use the knuth method. + split_ = 1; + emu_ = std::exp(-mean_); + } else if (mean_ <= 50) { + // Use split-knuth method. + split_ = 1 + static_cast<int>(mean_ / 10.0); + emu_ = std::exp(-mean_ / static_cast<double>(split_)); + } else { + // Use ratio of uniforms method. + constexpr double k2E = 0.7357588823428846; + constexpr double kSA = 0.4494580810294493; + + lmu_ = std::log(mean_); + double a = mean_ + 0.5; + s_ = kSA + std::sqrt(k2E * a); + const double mode = std::ceil(mean_) - 1; + log_k_ = lmu_ * mode - absl::random_internal::StirlingLogFactorial(mode); + } +} + +template <typename IntType> +template <typename URBG> +typename poisson_distribution<IntType>::result_type +poisson_distribution<IntType>::operator()( + URBG& g, // NOLINT(runtime/references) + const param_type& p) { + using random_internal::PositiveValueT; + using random_internal::RandU64ToDouble; + using random_internal::SignedValueT; + + if (p.split_ != 0) { + // Use Knuth's algorithm with range splitting to avoid floating-point + // errors. Knuth's algorithm is: Ui is a sequence of uniform variates on + // (0,1); return the number of variates required for product(Ui) < + // exp(-lambda). + // + // The expected number of variates required for Knuth's method can be + // computed as follows: + // The expected value of U is 0.5, so solving for 0.5^n < exp(-lambda) gives + // the expected number of uniform variates + // required for a given lambda, which is: + // lambda = [2, 5, 9, 10, 11, 12, 13, 14, 15, 16, 17] + // n = [3, 8, 13, 15, 16, 18, 19, 21, 22, 24, 25] + // + result_type n = 0; + for (int split = p.split_; split > 0; --split) { + double r = 1.0; + do { + r *= RandU64ToDouble<PositiveValueT, true>(fast_u64_(g)); + ++n; + } while (r > p.emu_); + --n; + } + return n; + } + + // Use ratio of uniforms method. + // + // Let u ~ Uniform(0, 1), v ~ Uniform(-1, 1), + // a = lambda + 1/2, + // s = 1.5 - sqrt(3/e) + sqrt(2(lambda + 1/2)/e), + // x = s * v/u + a. + // P(floor(x) = k | u^2 < f(floor(x))/k), where + // f(m) = lambda^m exp(-lambda)/ m!, for 0 <= m, and f(m) = 0 otherwise, + // and k = max(f). + const double a = p.mean_ + 0.5; + for (;;) { + const double u = + RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)); // (0, 1) + const double v = + RandU64ToDouble<SignedValueT, false>(fast_u64_(g)); // (-1, 1) + const double x = std::floor(p.s_ * v / u + a); + if (x < 0) continue; // f(negative) = 0 + const double rhs = x * p.lmu_; + // clang-format off + double s = (x <= 1.0) ? 0.0 + : (x == 2.0) ? 0.693147180559945 + : absl::random_internal::StirlingLogFactorial(x); + // clang-format on + const double lhs = 2.0 * std::log(u) + p.log_k_ + s; + if (lhs < rhs) { + return x > (max)() ? (max)() + : static_cast<result_type>(x); // f(x)/k >= u^2 + } + } +} + +template <typename CharT, typename Traits, typename IntType> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const poisson_distribution<IntType>& x) { + auto saver = random_internal::make_ostream_state_saver(os); + os.precision(random_internal::stream_precision_helper<double>::kPrecision); + os << x.mean(); + return os; +} + +template <typename CharT, typename Traits, typename IntType> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + poisson_distribution<IntType>& x) { // NOLINT(runtime/references) + using param_type = typename poisson_distribution<IntType>::param_type; + + auto saver = random_internal::make_istream_state_saver(is); + double mean = random_internal::read_floating_point<double>(is); + if (!is.fail()) { + x.param(param_type(mean)); + } + return is; +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_POISSON_DISTRIBUTION_H_ diff --git a/absl/random/poisson_distribution_test.cc b/absl/random/poisson_distribution_test.cc new file mode 100644 index 00000000..6d68999a --- /dev/null +++ b/absl/random/poisson_distribution_test.cc @@ -0,0 +1,565 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/poisson_distribution.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <random> +#include <sstream> +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" +#include "absl/container/flat_hash_map.h" +#include "absl/random/internal/chi_square.h" +#include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/strip.h" + +// Notes about generating poisson variates: +// +// It is unlikely that any implementation of std::poisson_distribution +// will be stable over time and across library implementations. For instance +// the three different poisson variate generators listed below all differ: +// +// https://github.com/ampl/gsl/tree/master/randist/poisson.c +// * GSL uses a gamma + binomial + knuth method to compute poisson variates. +// +// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/random.tcc +// * GCC uses the Devroye rejection algorithm, based on +// Devroye, L. Non-Uniform Random Variates Generation. Springer-Verlag, +// New York, 1986, Ch. X, Sects. 3.3 & 3.4 (+ Errata!), ~p.511 +// http://www.nrbook.com/devroye/ +// +// https://github.com/llvm-mirror/libcxx/blob/master/include/random +// * CLANG uses a different rejection method, which appears to include a +// normal-distribution approximation and an exponential distribution to +// compute the threshold, including a similar factorial approximation to this +// one, but it is unclear where the algorithm comes from, exactly. +// + +namespace { + +using absl::random_internal::kChiSquared; + +// The PoissonDistributionInterfaceTest provides a basic test that +// absl::poisson_distribution conforms to the interface and serialization +// requirements imposed by [rand.req.dist] for the common integer types. + +template <typename IntType> +class PoissonDistributionInterfaceTest : public ::testing::Test {}; + +using IntTypes = ::testing::Types<int, int8_t, int16_t, int32_t, int64_t, + uint8_t, uint16_t, uint32_t, uint64_t>; +TYPED_TEST_CASE(PoissonDistributionInterfaceTest, IntTypes); + +TYPED_TEST(PoissonDistributionInterfaceTest, SerializeTest) { + using param_type = typename absl::poisson_distribution<TypeParam>::param_type; + const double kMax = + std::min(1e10 /* assertion limit */, + static_cast<double>(std::numeric_limits<TypeParam>::max())); + + const double kParams[] = { + // Cases around 1. + 1, // + std::nextafter(1.0, 0.0), // 1 - epsilon + std::nextafter(1.0, 2.0), // 1 + epsilon + // Arbitrary values. + 1e-8, 1e-4, + 0.0000005, // ~7.2e-7 + 0.2, // ~0.2x + 0.5, // 0.72 + 2, // ~2.8 + 20, // 3x ~9.6 + 100, 1e4, 1e8, 1.5e9, 1e20, + // Boundary cases. + std::numeric_limits<double>::max(), + std::numeric_limits<double>::epsilon(), + std::nextafter(std::numeric_limits<double>::min(), + 1.0), // min + epsilon + std::numeric_limits<double>::min(), // smallest normal + std::numeric_limits<double>::denorm_min(), // smallest denorm + std::numeric_limits<double>::min() / 2, // denorm + std::nextafter(std::numeric_limits<double>::min(), + 0.0), // denorm_max + }; + + + constexpr int kCount = 1000; + absl::InsecureBitGen gen; + for (const double m : kParams) { + const double mean = std::min(kMax, m); + const param_type param(mean); + + // Validate parameters. + absl::poisson_distribution<TypeParam> before(mean); + EXPECT_EQ(before.mean(), param.mean()); + + { + absl::poisson_distribution<TypeParam> via_param(param); + EXPECT_EQ(via_param, before); + EXPECT_EQ(via_param.param(), before.param()); + } + + // Smoke test. + auto sample_min = before.max(); + auto sample_max = before.min(); + for (int i = 0; i < kCount; i++) { + auto sample = before(gen); + EXPECT_GE(sample, before.min()); + EXPECT_LE(sample, before.max()); + if (sample > sample_max) sample_max = sample; + if (sample < sample_min) sample_min = sample; + } + + ABSL_INTERNAL_LOG(INFO, absl::StrCat("Range {", param.mean(), "}: ", + +sample_min, ", ", +sample_max)); + + // Validate stream serialization. + std::stringstream ss; + ss << before; + + absl::poisson_distribution<TypeParam> after(3.8); + + EXPECT_NE(before.mean(), after.mean()); + EXPECT_NE(before.param(), after.param()); + EXPECT_NE(before, after); + + ss >> after; + + EXPECT_EQ(before.mean(), after.mean()) // + << ss.str() << " " // + << (ss.good() ? "good " : "") // + << (ss.bad() ? "bad " : "") // + << (ss.eof() ? "eof " : "") // + << (ss.fail() ? "fail " : ""); + } +} + +// See http://www.itl.nist.gov/div898/handbook/eda/section3/eda366j.htm + +class PoissonModel { + public: + explicit PoissonModel(double mean) : mean_(mean) {} + + double mean() const { return mean_; } + double variance() const { return mean_; } + double stddev() const { return std::sqrt(variance()); } + double skew() const { return 1.0 / mean_; } + double kurtosis() const { return 3.0 + 1.0 / mean_; } + + // InitCDF() initializes the CDF for the distribution parameters. + void InitCDF(); + + // The InverseCDF, or the Percent-point function returns x, P(x) < v. + struct CDF { + size_t index; + double pmf; + double cdf; + }; + CDF InverseCDF(double p) { + CDF target{0, 0, p}; + auto it = std::upper_bound( + std::begin(cdf_), std::end(cdf_), target, + [](const CDF& a, const CDF& b) { return a.cdf < b.cdf; }); + return *it; + } + + void LogCDF() { + ABSL_INTERNAL_LOG(INFO, absl::StrCat("CDF (mean = ", mean_, ")")); + for (const auto c : cdf_) { + ABSL_INTERNAL_LOG(INFO, + absl::StrCat(c.index, ": pmf=", c.pmf, " cdf=", c.cdf)); + } + } + + private: + const double mean_; + + std::vector<CDF> cdf_; +}; + +// The goal is to compute an InverseCDF function, or percent point function for +// the poisson distribution, and use that to partition our output into equal +// range buckets. However there is no closed form solution for the inverse cdf +// for poisson distributions (the closest is the incomplete gamma function). +// Instead, `InitCDF` iteratively computes the PMF and the CDF. This enables +// searching for the bucket points. +void PoissonModel::InitCDF() { + if (!cdf_.empty()) { + // State already initialized. + return; + } + ABSL_ASSERT(mean_ < 201.0); + + const size_t max_i = 50 * stddev() + mean(); + const double e_neg_mean = std::exp(-mean()); + ABSL_ASSERT(e_neg_mean > 0); + + double d = 1; + double last_result = e_neg_mean; + double cumulative = e_neg_mean; + if (e_neg_mean > 1e-10) { + cdf_.push_back({0, e_neg_mean, cumulative}); + } + for (size_t i = 1; i < max_i; i++) { + d *= (mean() / i); + double result = e_neg_mean * d; + cumulative += result; + if (result < 1e-10 && result < last_result && cumulative > 0.999999) { + break; + } + if (result > 1e-7) { + cdf_.push_back({i, result, cumulative}); + } + last_result = result; + } + ABSL_ASSERT(!cdf_.empty()); +} + +// PoissonDistributionZTest implements a z-test for the poisson distribution. + +struct ZParam { + double mean; + double p_fail; // Z-Test probability of failure. + int trials; // Z-Test trials. + size_t samples; // Z-Test samples. +}; + +class PoissonDistributionZTest : public testing::TestWithParam<ZParam>, + public PoissonModel { + public: + PoissonDistributionZTest() : PoissonModel(GetParam().mean) {} + + // ZTestImpl provides a basic z-squared test of the mean vs. expected + // mean for data generated by the poisson distribution. + template <typename D> + bool SingleZTest(const double p, const size_t samples); + + absl::InsecureBitGen rng_; +}; + +template <typename D> +bool PoissonDistributionZTest::SingleZTest(const double p, + const size_t samples) { + D dis(mean()); + + absl::flat_hash_map<int32_t, int> buckets; + std::vector<double> data; + data.reserve(samples); + for (int j = 0; j < samples; j++) { + const auto x = dis(rng_); + buckets[x]++; + data.push_back(x); + } + + // The null-hypothesis is that the distribution is a poisson distribution with + // the provided mean (not estimated from the data). + const auto m = absl::random_internal::ComputeDistributionMoments(data); + const double max_err = absl::random_internal::MaxErrorTolerance(p); + const double z = absl::random_internal::ZScore(mean(), m); + const bool pass = absl::random_internal::Near("z", z, 0.0, max_err); + + if (!pass) { + ABSL_INTERNAL_LOG( + INFO, absl::StrFormat("p=%f max_err=%f\n" + " mean=%f vs. %f\n" + " stddev=%f vs. %f\n" + " skewness=%f vs. %f\n" + " kurtosis=%f vs. %f\n" + " z=%f", + p, max_err, m.mean, mean(), std::sqrt(m.variance), + stddev(), m.skewness, skew(), m.kurtosis, + kurtosis(), z)); + } + return pass; +} + +TEST_P(PoissonDistributionZTest, AbslPoissonDistribution) { + const auto& param = GetParam(); + const int expected_failures = + std::max(1, static_cast<int>(std::ceil(param.trials * param.p_fail))); + const double p = absl::random_internal::RequiredSuccessProbability( + param.p_fail, param.trials); + + int failures = 0; + for (int i = 0; i < param.trials; i++) { + failures += + SingleZTest<absl::poisson_distribution<int32_t>>(p, param.samples) ? 0 + : 1; + } + EXPECT_LE(failures, expected_failures); +} + +std::vector<ZParam> GetZParams() { + // These values have been adjusted from the "exact" computed values to reduce + // failure rates. + // + // It turns out that the actual values are not as close to the expected values + // as would be ideal. + return std::vector<ZParam>({ + // Knuth method. + ZParam{0.5, 0.01, 100, 1000}, + ZParam{1.0, 0.01, 100, 1000}, + ZParam{10.0, 0.01, 100, 5000}, + // Split-knuth method. + ZParam{20.0, 0.01, 100, 10000}, + ZParam{50.0, 0.01, 100, 10000}, + // Ratio of gaussians method. + ZParam{51.0, 0.01, 100, 10000}, + ZParam{200.0, 0.05, 10, 100000}, + ZParam{100000.0, 0.05, 10, 1000000}, + }); +} + +std::string ZParamName(const ::testing::TestParamInfo<ZParam>& info) { + const auto& p = info.param; + std::string name = absl::StrCat("mean_", absl::SixDigits(p.mean)); + return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); +} + +INSTANTIATE_TEST_SUITE_P(, PoissonDistributionZTest, + ::testing::ValuesIn(GetZParams()), ZParamName); + +// The PoissonDistributionChiSquaredTest class provides a basic test framework +// for variates generated by a conforming poisson_distribution. +class PoissonDistributionChiSquaredTest : public testing::TestWithParam<double>, + public PoissonModel { + public: + PoissonDistributionChiSquaredTest() : PoissonModel(GetParam()) {} + + // The ChiSquaredTestImpl provides a chi-squared goodness of fit test for data + // generated by the poisson distribution. + template <typename D> + double ChiSquaredTestImpl(); + + private: + void InitChiSquaredTest(const double buckets); + + absl::InsecureBitGen rng_; + std::vector<size_t> cutoffs_; + std::vector<double> expected_; +}; + +void PoissonDistributionChiSquaredTest::InitChiSquaredTest( + const double buckets) { + if (!cutoffs_.empty() && !expected_.empty()) { + return; + } + InitCDF(); + + // The code below finds cuttoffs that yield approximately equally-sized + // buckets to the extent that it is possible. However for poisson + // distributions this is particularly challenging for small mean parameters. + // Track the expected proportion of items in each bucket. + double last_cdf = 0; + const double inc = 1.0 / buckets; + for (double p = inc; p <= 1.0; p += inc) { + auto result = InverseCDF(p); + if (!cutoffs_.empty() && cutoffs_.back() == result.index) { + continue; + } + double d = result.cdf - last_cdf; + cutoffs_.push_back(result.index); + expected_.push_back(d); + last_cdf = result.cdf; + } + cutoffs_.push_back(std::numeric_limits<size_t>::max()); + expected_.push_back(std::max(0.0, 1.0 - last_cdf)); +} + +template <typename D> +double PoissonDistributionChiSquaredTest::ChiSquaredTestImpl() { + const int kSamples = 2000; + const int kBuckets = 50; + + // The poisson CDF fails for large mean values, since e^-mean exceeds the + // machine precision. For these cases, using a normal approximation would be + // appropriate. + ABSL_ASSERT(mean() <= 200); + InitChiSquaredTest(kBuckets); + + D dis(mean()); + + std::vector<int32_t> counts(cutoffs_.size(), 0); + for (int j = 0; j < kSamples; j++) { + const size_t x = dis(rng_); + auto it = std::lower_bound(std::begin(cutoffs_), std::end(cutoffs_), x); + counts[std::distance(cutoffs_.begin(), it)]++; + } + + // Normalize the counts. + std::vector<int32_t> e(expected_.size(), 0); + for (int i = 0; i < e.size(); i++) { + e[i] = kSamples * expected_[i]; + } + + // The null-hypothesis is that the distribution is a poisson distribution with + // the provided mean (not estimated from the data). + const int dof = static_cast<int>(counts.size()) - 1; + + // The threshold for logging is 1-in-50. + const double threshold = absl::random_internal::ChiSquareValue(dof, 0.98); + + const double chi_square = absl::random_internal::ChiSquare( + std::begin(counts), std::end(counts), std::begin(e), std::end(e)); + + const double p = absl::random_internal::ChiSquarePValue(chi_square, dof); + + // Log if the chi_squared value is above the threshold. + if (chi_square > threshold) { + LogCDF(); + + ABSL_INTERNAL_LOG(INFO, absl::StrCat("VALUES buckets=", counts.size(), + " samples=", kSamples)); + for (size_t i = 0; i < counts.size(); i++) { + ABSL_INTERNAL_LOG( + INFO, absl::StrCat(cutoffs_[i], ": ", counts[i], " vs. E=", e[i])); + } + + ABSL_INTERNAL_LOG( + INFO, + absl::StrCat(kChiSquared, "(data, dof=", dof, ") = ", chi_square, " (", + p, ")\n", " vs.\n", kChiSquared, " @ 0.98 = ", threshold)); + } + return p; +} + +TEST_P(PoissonDistributionChiSquaredTest, AbslPoissonDistribution) { + const int kTrials = 20; + + // Large values are not yet supported -- this requires estimating the cdf + // using the normal distribution instead of the poisson in this case. + ASSERT_LE(mean(), 200.0); + if (mean() > 200.0) { + return; + } + + int failures = 0; + for (int i = 0; i < kTrials; i++) { + double p_value = ChiSquaredTestImpl<absl::poisson_distribution<int32_t>>(); + if (p_value < 0.005) { + failures++; + } + } + // There is a 0.10% chance of producing at least one failure, so raise the + // failure threshold high enough to allow for a flake rate < 10,000. + EXPECT_LE(failures, 4); +} + +INSTANTIATE_TEST_SUITE_P(, PoissonDistributionChiSquaredTest, + ::testing::Values(0.5, 1.0, 2.0, 10.0, 50.0, 51.0, + 200.0)); + +// NOTE: absl::poisson_distribution is not guaranteed to be stable. +TEST(PoissonDistributionTest, StabilityTest) { + using testing::ElementsAre; + // absl::poisson_distribution stability relies on stability of + // std::exp, std::log, std::sqrt, std::ceil, std::floor, and + // absl::FastUniformBits, absl::StirlingLogFactorial, absl::RandU64ToDouble. + absl::random_internal::sequence_urbg urbg({ + 0x035b0dc7e0a18acfull, 0x06cebe0d2653682eull, 0x0061e9b23861596bull, + 0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull, + 0x4864f22c059bf29eull, 0x247856d8b862665cull, 0xe46e86e9a1337e10ull, + 0xd8c8541f3519b133ull, 0xe75b5162c567b9e4ull, 0xf732e5ded7009c5bull, + 0xb170b98353121eacull, 0x1ec2e8986d2362caull, 0x814c8e35fe9a961aull, + 0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull, 0x1224e62c978bbc7full, + 0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull, 0x1bbc23cfa8fac721ull, + 0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull, 0x836d794457c08849ull, + 0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull, 0xb12d74fdd718c8c5ull, + 0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull, 0x5738341045ba0d85ull, + 0xf3fd722dc65ad09eull, 0xfa14fd21ea2a5705ull, 0xffe6ea4d6edb0c73ull, + 0xD07E9EFE2BF11FB4ull, 0x95DBDA4DAE909198ull, 0xEAAD8E716B93D5A0ull, + 0xD08ED1D0AFC725E0ull, 0x8E3C5B2F8E7594B7ull, 0x8FF6E2FBF2122B64ull, + 0x8888B812900DF01Cull, 0x4FAD5EA0688FC31Cull, 0xD1CFF191B3A8C1ADull, + 0x2F2F2218BE0E1777ull, 0xEA752DFE8B021FA1ull, 0xE5A0CC0FB56F74E8ull, + 0x18ACF3D6CE89E299ull, 0xB4A84FE0FD13E0B7ull, 0x7CC43B81D2ADA8D9ull, + 0x165FA26680957705ull, 0x93CC7314211A1477ull, 0xE6AD206577B5FA86ull, + 0xC75442F5FB9D35CFull, 0xEBCDAF0C7B3E89A0ull, 0xD6411BD3AE1E7E49ull, + 0x00250E2D2071B35Eull, 0x226800BB57B8E0AFull, 0x2464369BF009B91Eull, + 0x5563911D59DFA6AAull, 0x78C14389D95A537Full, 0x207D5BA202E5B9C5ull, + 0x832603766295CFA9ull, 0x11C819684E734A41ull, 0xB3472DCA7B14A94Aull, + }); + + std::vector<int> output(10); + + // Method 1. + { + absl::poisson_distribution<int> dist(5); + std::generate(std::begin(output), std::end(output), + [&] { return dist(urbg); }); + } + EXPECT_THAT(output, // mean = 4.2 + ElementsAre(1, 0, 0, 4, 2, 10, 3, 3, 7, 12)); + + // Method 2. + { + urbg.reset(); + absl::poisson_distribution<int> dist(25); + std::generate(std::begin(output), std::end(output), + [&] { return dist(urbg); }); + } + EXPECT_THAT(output, // mean = 19.8 + ElementsAre(9, 35, 18, 10, 35, 18, 10, 35, 18, 10)); + + // Method 3. + { + urbg.reset(); + absl::poisson_distribution<int> dist(121); + std::generate(std::begin(output), std::end(output), + [&] { return dist(urbg); }); + } + EXPECT_THAT(output, // mean = 124.1 + ElementsAre(161, 122, 129, 124, 112, 112, 117, 120, 130, 114)); +} + +TEST(PoissonDistributionTest, AlgorithmExpectedValue_1) { + // This tests small values of the Knuth method. + // The underlying uniform distribution will generate exactly 0.5. + absl::random_internal::sequence_urbg urbg({0x8000000000000001ull}); + absl::poisson_distribution<int> dist(5); + EXPECT_EQ(7, dist(urbg)); +} + +TEST(PoissonDistributionTest, AlgorithmExpectedValue_2) { + // This tests larger values of the Knuth method. + // The underlying uniform distribution will generate exactly 0.5. + absl::random_internal::sequence_urbg urbg({0x8000000000000001ull}); + absl::poisson_distribution<int> dist(25); + EXPECT_EQ(36, dist(urbg)); +} + +TEST(PoissonDistributionTest, AlgorithmExpectedValue_3) { + // This variant uses the ratio of uniforms method. + absl::random_internal::sequence_urbg urbg( + {0x7fffffffffffffffull, 0x8000000000000000ull}); + + absl::poisson_distribution<int> dist(121); + EXPECT_EQ(121, dist(urbg)); +} + +} // namespace diff --git a/absl/random/random.h b/absl/random/random.h new file mode 100644 index 00000000..72a4cf5b --- /dev/null +++ b/absl/random/random.h @@ -0,0 +1,189 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: random.h +// ----------------------------------------------------------------------------- +// +// This header defines the recommended Uniform Random Bit Generator (URBG) +// types for use within the Abseil Random library. These types are not +// suitable for security-related use-cases, but should suffice for most other +// uses of generating random values. +// +// The Abseil random library provides the following URBG types: +// +// * BitGen, a good general-purpose bit generator, optimized for generating +// random (but not cryptographically secure) values +// * InsecureBitGen, a slightly faster, though less random, bit generator, for +// cases where the existing BitGen is a drag on performance. + +#ifndef ABSL_RANDOM_RANDOM_H_ +#define ABSL_RANDOM_RANDOM_H_ + +#include <random> + +#include "absl/random/distributions.h" // IWYU pragma: export +#include "absl/random/internal/nonsecure_base.h" // IWYU pragma: export +#include "absl/random/internal/pcg_engine.h" // IWYU pragma: export +#include "absl/random/internal/pool_urbg.h" +#include "absl/random/internal/randen_engine.h" +#include "absl/random/seed_sequences.h" // IWYU pragma: export + +namespace absl { +inline namespace lts_2019_08_08 { + +// ----------------------------------------------------------------------------- +// absl::BitGen +// ----------------------------------------------------------------------------- +// +// `absl::BitGen` is a general-purpose random bit generator for generating +// random values for use within the Abseil random library. Typically, you use a +// bit generator in combination with a distribution to provide random values. +// +// Example: +// +// // Create an absl::BitGen. There is no need to seed this bit generator. +// absl::BitGen gen; +// +// // Generate an integer value in the closed interval [1,6] +// int die_roll = absl::uniform_int_distribution<int>(1, 6)(gen); +// +// `absl::BitGen` is seeded by default with non-deterministic data to produce +// different sequences of random values across different instances, including +// different binary invocations. This behavior is different than the standard +// library bit generators, which use golden values as their seeds. Default +// construction intentionally provides no stability guarantees, to avoid +// accidental dependence on such a property. +// +// `absl::BitGen` may be constructed with an optional seed sequence type, +// conforming to [rand.req.seed_seq], which will be mixed with additional +// non-deterministic data. +// +// Example: +// +// // Create an absl::BitGen using an std::seed_seq seed sequence +// std::seed_seq seq{1,2,3}; +// absl::BitGen gen_with_seed(seq); +// +// // Generate an integer value in the closed interval [1,6] +// int die_roll2 = absl::uniform_int_distribution<int>(1, 6)(gen_with_seed); +// +// `absl::BitGen` meets the requirements of the Uniform Random Bit Generator +// (URBG) concept as per the C++17 standard [rand.req.urng] though differs +// slightly with [rand.req.eng]. Like its standard library equivalents (e.g. +// `std::mersenne_twister_engine`) `absl::BitGen` is not cryptographically +// secure. +// +// Constructing two `absl::BitGen`s with the same seed sequence in the same +// binary will produce the same sequence of variates within the same binary, but +// need not do so across multiple binary invocations. +// +// This type has been optimized to perform better than Mersenne Twister +// (https://en.wikipedia.org/wiki/Mersenne_Twister) and many other complex URBG +// types on modern x86, ARM, and PPC architectures. +// +// This type is thread-compatible, but not thread-safe. + +// --------------------------------------------------------------------------- +// absl::BitGen member functions +// --------------------------------------------------------------------------- + +// absl::BitGen::operator()() +// +// Calls the BitGen, returning a generated value. + +// absl::BitGen::min() +// +// Returns the smallest possible value from this bit generator. + +// absl::BitGen::max() +// +// Returns the largest possible value from this bit generator., and + +// absl::BitGen::discard(num) +// +// Advances the internal state of this bit generator by `num` times, and +// discards the intermediate results. +// --------------------------------------------------------------------------- + +using BitGen = random_internal::NonsecureURBGBase< + random_internal::randen_engine<uint64_t>>; + +// ----------------------------------------------------------------------------- +// absl::InsecureBitGen +// ----------------------------------------------------------------------------- +// +// `absl::InsecureBitGen` is an efficient random bit generator for generating +// random values, recommended only for performance-sensitive use cases where +// `absl::BitGen` is not satisfactory when compute-bounded by bit generation +// costs. +// +// Example: +// +// // Create an absl::InsecureBitGen +// absl::InsecureBitGen gen; +// for (size_t i = 0; i < 1000000; i++) { +// +// // Generate a bunch of random values from some complex distribution +// auto my_rnd = some_distribution(gen, 1, 1000); +// } +// +// Like `absl::BitGen`, `absl::InsecureBitGen` is seeded by default with +// non-deterministic data to produce different sequences of random values across +// different instances, including different binary invocations. (This behavior +// is different than the standard library bit generators, which use golden +// values as their seeds.) +// +// `absl::InsecureBitGen` may be constructed with an optional seed sequence +// type, conforming to [rand.req.seed_seq], which will be mixed with additional +// non-deterministic data. (See std_seed_seq.h for more information.) +// +// `absl::InsecureBitGen` meets the requirements of the Uniform Random Bit +// Generator (URBG) concept as per the C++17 standard [rand.req.urng] though +// its implementation differs slightly with [rand.req.eng]. Like its standard +// library equivalents (e.g. `std::mersenne_twister_engine`) +// `absl::InsecureBitGen` is not cryptographically secure. +// +// Prefer `absl::BitGen` over `absl::InsecureBitGen` as the general type is +// often fast enough for the vast majority of applications. + +using InsecureBitGen = + random_internal::NonsecureURBGBase<random_internal::pcg64_2018_engine>; + +// --------------------------------------------------------------------------- +// absl::InsecureBitGen member functions +// --------------------------------------------------------------------------- + +// absl::InsecureBitGen::operator()() +// +// Calls the InsecureBitGen, returning a generated value. + +// absl::InsecureBitGen::min() +// +// Returns the smallest possible value from this bit generator. + +// absl::InsecureBitGen::max() +// +// Returns the largest possible value from this bit generator. + +// absl::InsecureBitGen::discard(num) +// +// Advances the internal state of this bit generator by `num` times, and +// discards the intermediate results. +// --------------------------------------------------------------------------- + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_RANDOM_H_ diff --git a/absl/random/seed_gen_exception.cc b/absl/random/seed_gen_exception.cc new file mode 100644 index 00000000..5f01a30c --- /dev/null +++ b/absl/random/seed_gen_exception.cc @@ -0,0 +1,46 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/seed_gen_exception.h" + +#include <iostream> + +#include "absl/base/config.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +static constexpr const char kExceptionMessage[] = + "Failed generating seed-material for URBG."; + +SeedGenException::~SeedGenException() = default; + +const char* SeedGenException::what() const noexcept { + return kExceptionMessage; +} + +namespace random_internal { + +void ThrowSeedGenException() { +#ifdef ABSL_HAVE_EXCEPTIONS + throw absl::SeedGenException(); +#else + std::cerr << kExceptionMessage << std::endl; + std::terminate(); +#endif +} + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/seed_gen_exception.h b/absl/random/seed_gen_exception.h new file mode 100644 index 00000000..52afe6cc --- /dev/null +++ b/absl/random/seed_gen_exception.h @@ -0,0 +1,53 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: seed_gen_exception.h +// ----------------------------------------------------------------------------- +// +// This header defines an exception class which may be thrown if unpredictable +// events prevent the derivation of suitable seed-material for constructing a +// bit generator conforming to [rand.req.urng] (eg. entropy cannot be read from +// /dev/urandom on a Unix-based system). +// +// Note: if exceptions are disabled, `std::terminate()` is called instead. + +#ifndef ABSL_RANDOM_SEED_GEN_EXCEPTION_H_ +#define ABSL_RANDOM_SEED_GEN_EXCEPTION_H_ + +#include <exception> + +namespace absl { +inline namespace lts_2019_08_08 { + +//------------------------------------------------------------------------------ +// SeedGenException +//------------------------------------------------------------------------------ +class SeedGenException : public std::exception { + public: + SeedGenException() = default; + ~SeedGenException() override; + const char* what() const noexcept override; +}; + +namespace random_internal { + +// throw delegator +[[noreturn]] void ThrowSeedGenException(); + +} // namespace random_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_SEED_GEN_EXCEPTION_H_ diff --git a/absl/random/seed_sequences.cc b/absl/random/seed_sequences.cc new file mode 100644 index 00000000..fb7eb8d1 --- /dev/null +++ b/absl/random/seed_sequences.cc @@ -0,0 +1,29 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/seed_sequences.h" + +#include "absl/random/internal/pool_urbg.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +SeedSeq MakeSeedSeq() { + SeedSeq::result_type seed_material[8]; + random_internal::RandenPool<uint32_t>::Fill(absl::MakeSpan(seed_material)); + return SeedSeq(std::begin(seed_material), std::end(seed_material)); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/random/seed_sequences.h b/absl/random/seed_sequences.h new file mode 100644 index 00000000..73d075c0 --- /dev/null +++ b/absl/random/seed_sequences.h @@ -0,0 +1,110 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: seed_sequences.h +// ----------------------------------------------------------------------------- +// +// This header contains utilities for creating and working with seed sequences +// conforming to [rand.req.seedseq]. In general, direct construction of seed +// sequences is discouraged, but use-cases for construction of identical bit +// generators (using the same seed sequence) may be helpful (e.g. replaying a +// simulation whose state is derived from variates of a bit generator). + +#ifndef ABSL_RANDOM_SEED_SEQUENCES_H_ +#define ABSL_RANDOM_SEED_SEQUENCES_H_ + +#include <iterator> +#include <random> + +#include "absl/random/internal/salted_seed_seq.h" +#include "absl/random/internal/seed_material.h" +#include "absl/random/seed_gen_exception.h" +#include "absl/types/span.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// ----------------------------------------------------------------------------- +// absl::SeedSeq +// ----------------------------------------------------------------------------- +// +// `absl::SeedSeq` constructs a seed sequence according to [rand.req.seedseq] +// for use within bit generators. `absl::SeedSeq`, unlike `std::seed_seq` +// additionally salts the generated seeds with extra implementation-defined +// entropy. For that reason, you can use `absl::SeedSeq` in combination with +// standard library bit generators (e.g. `std::mt19937`) to introduce +// non-determinism in your seeds. +// +// Example: +// +// absl::SeedSeq my_seed_seq({a, b, c}); +// std::mt19937 my_bitgen(my_seed_seq); +// +using SeedSeq = random_internal::SaltedSeedSeq<std::seed_seq>; + +// ----------------------------------------------------------------------------- +// absl::CreateSeedSeqFrom(bitgen*) +// ----------------------------------------------------------------------------- +// +// Constructs a seed sequence conforming to [rand.req.seedseq] using variates +// produced by a provided bit generator. +// +// You should generally avoid direct construction of seed sequences, but +// use-cases for reuse of a seed sequence to construct identical bit generators +// may be helpful (eg. replaying a simulation whose state is derived from bit +// generator values). +// +// If bitgen == nullptr, then behavior is undefined. +// +// Example: +// +// absl::BitGen my_bitgen; +// auto seed_seq = absl::CreateSeedSeqFrom(&my_bitgen); +// absl::BitGen new_engine(seed_seq); // derived from my_bitgen, but not +// // correlated. +// +template <typename URBG> +SeedSeq CreateSeedSeqFrom(URBG* urbg) { + SeedSeq::result_type + seed_material[random_internal::kEntropyBlocksNeeded]; + + if (!random_internal::ReadSeedMaterialFromURBG( + urbg, absl::MakeSpan(seed_material))) { + random_internal::ThrowSeedGenException(); + } + return SeedSeq(std::begin(seed_material), std::end(seed_material)); +} + +// ----------------------------------------------------------------------------- +// absl::MakeSeedSeq() +// ----------------------------------------------------------------------------- +// +// Constructs an `absl::SeedSeq` salting the generated values using +// implementation-defined entropy. The returned sequence can be used to create +// equivalent bit generators correlated using this sequence. +// +// Example: +// +// auto my_seed_seq = absl::MakeSeedSeq(); +// std::mt19937 rng1(my_seed_seq); +// std::mt19937 rng2(my_seed_seq); +// EXPECT_EQ(rng1(), rng2()); +// +SeedSeq MakeSeedSeq(); + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_SEED_SEQUENCES_H_ diff --git a/absl/random/seed_sequences_test.cc b/absl/random/seed_sequences_test.cc new file mode 100644 index 00000000..2cc8b0e6 --- /dev/null +++ b/absl/random/seed_sequences_test.cc @@ -0,0 +1,127 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/seed_sequences.h" + +#include <iterator> +#include <random> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/internal/nonsecure_base.h" +#include "absl/random/random.h" +namespace { + +TEST(SeedSequences, Examples) { + { + absl::SeedSeq seed_seq({1, 2, 3}); + absl::BitGen bitgen(seed_seq); + + EXPECT_NE(0, bitgen()); + } + { + absl::BitGen engine; + auto seed_seq = absl::CreateSeedSeqFrom(&engine); + absl::BitGen bitgen(seed_seq); + + EXPECT_NE(engine(), bitgen()); + } + { + auto seed_seq = absl::MakeSeedSeq(); + std::mt19937 random(seed_seq); + + EXPECT_NE(0, random()); + } +} + +TEST(CreateSeedSeqFrom, CompatibleWithStdTypes) { + using ExampleNonsecureURBG = + absl::random_internal::NonsecureURBGBase<std::minstd_rand0>; + + // Construct a URBG instance. + ExampleNonsecureURBG rng; + + // Construct a Seed Sequence from its variates. + auto seq_from_rng = absl::CreateSeedSeqFrom(&rng); + + // Ensure that another URBG can be validly constructed from the Seed Sequence. + std::mt19937_64{seq_from_rng}; +} + +TEST(CreateSeedSeqFrom, CompatibleWithBitGenerator) { + // Construct a URBG instance. + absl::BitGen rng; + + // Construct a Seed Sequence from its variates. + auto seq_from_rng = absl::CreateSeedSeqFrom(&rng); + + // Ensure that another URBG can be validly constructed from the Seed Sequence. + std::mt19937_64{seq_from_rng}; +} + +TEST(CreateSeedSeqFrom, CompatibleWithInsecureBitGen) { + // Construct a URBG instance. + absl::InsecureBitGen rng; + + // Construct a Seed Sequence from its variates. + auto seq_from_rng = absl::CreateSeedSeqFrom(&rng); + + // Ensure that another URBG can be validly constructed from the Seed Sequence. + std::mt19937_64{seq_from_rng}; +} + +TEST(CreateSeedSeqFrom, CompatibleWithRawURBG) { + // Construct a URBG instance. + std::random_device urandom; + + // Construct a Seed Sequence from its variates, using 64b of seed-material. + auto seq_from_rng = absl::CreateSeedSeqFrom(&urandom); + + // Ensure that another URBG can be validly constructed from the Seed Sequence. + std::mt19937_64{seq_from_rng}; +} + +template <typename URBG> +void TestReproducibleVariateSequencesForNonsecureURBG() { + const size_t kNumVariates = 1000; + + // Master RNG instance. + URBG rng; + // Reused for both RNG instances. + auto reusable_seed = absl::CreateSeedSeqFrom(&rng); + + typename URBG::result_type variates[kNumVariates]; + { + URBG child(reusable_seed); + for (auto& variate : variates) { + variate = child(); + } + } + // Ensure that variate-sequence can be "replayed" by identical RNG. + { + URBG child(reusable_seed); + for (auto& variate : variates) { + ASSERT_EQ(variate, child()); + } + } +} + +TEST(CreateSeedSeqFrom, ReproducesVariateSequencesForInsecureBitGen) { + TestReproducibleVariateSequencesForNonsecureURBG<absl::InsecureBitGen>(); +} + +TEST(CreateSeedSeqFrom, ReproducesVariateSequencesForBitGenerator) { + TestReproducibleVariateSequencesForNonsecureURBG<absl::BitGen>(); +} +} // namespace diff --git a/absl/random/uniform_int_distribution.h b/absl/random/uniform_int_distribution.h new file mode 100644 index 00000000..95eb04a4 --- /dev/null +++ b/absl/random/uniform_int_distribution.h @@ -0,0 +1,275 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: uniform_int_distribution.h +// ----------------------------------------------------------------------------- +// +// This header defines a class for representing a uniform integer distribution +// over the closed (inclusive) interval [a,b]. You use this distribution in +// combination with an Abseil random bit generator to produce random values +// according to the rules of the distribution. +// +// `absl::uniform_int_distribution` is a drop-in replacement for the C++11 +// `std::uniform_int_distribution` [rand.dist.uni.int] but is considerably +// faster than the libstdc++ implementation. + +#ifndef ABSL_RANDOM_UNIFORM_INT_DISTRIBUTION_H_ +#define ABSL_RANDOM_UNIFORM_INT_DISTRIBUTION_H_ + +#include <cassert> +#include <istream> +#include <limits> +#include <type_traits> + +#include "absl/base/optimization.h" +#include "absl/random/internal/distribution_impl.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/iostream_state_saver.h" +#include "absl/random/internal/traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// absl::uniform_int_distribution<T> +// +// This distribution produces random integer values uniformly distributed in the +// closed (inclusive) interval [a, b]. +// +// Example: +// +// absl::BitGen gen; +// +// // Use the distribution to produce a value between 1 and 6, inclusive. +// int die_roll = absl::uniform_int_distribution<int>(1, 6)(gen); +// +template <typename IntType = int> +class uniform_int_distribution { + private: + using unsigned_type = + typename random_internal::make_unsigned_bits<IntType>::type; + + public: + using result_type = IntType; + + class param_type { + public: + using distribution_type = uniform_int_distribution; + + explicit param_type( + result_type lo = 0, + result_type hi = (std::numeric_limits<result_type>::max)()) + : lo_(lo), + range_(static_cast<unsigned_type>(hi) - + static_cast<unsigned_type>(lo)) { + // [rand.dist.uni.int] precondition 2 + assert(lo <= hi); + } + + result_type a() const { return lo_; } + result_type b() const { + return static_cast<result_type>(static_cast<unsigned_type>(lo_) + range_); + } + + friend bool operator==(const param_type& a, const param_type& b) { + return a.lo_ == b.lo_ && a.range_ == b.range_; + } + + friend bool operator!=(const param_type& a, const param_type& b) { + return !(a == b); + } + + private: + friend class uniform_int_distribution; + unsigned_type range() const { return range_; } + + result_type lo_; + unsigned_type range_; + + static_assert(std::is_integral<result_type>::value, + "Class-template absl::uniform_int_distribution<> must be " + "parameterized using an integral type."); + }; // param_type + + uniform_int_distribution() : uniform_int_distribution(0) {} + + explicit uniform_int_distribution( + result_type lo, + result_type hi = (std::numeric_limits<result_type>::max)()) + : param_(lo, hi) {} + + explicit uniform_int_distribution(const param_type& param) : param_(param) {} + + // uniform_int_distribution<T>::reset() + // + // Resets the uniform int distribution. Note that this function has no effect + // because the distribution already produces independent values. + void reset() {} + + template <typename URBG> + result_type operator()(URBG& gen) { // NOLINT(runtime/references) + return (*this)(gen, param()); + } + + template <typename URBG> + result_type operator()( + URBG& gen, const param_type& param) { // NOLINT(runtime/references) + return param.a() + Generate(gen, param.range()); + } + + result_type a() const { return param_.a(); } + result_type b() const { return param_.b(); } + + param_type param() const { return param_; } + void param(const param_type& params) { param_ = params; } + + result_type(min)() const { return a(); } + result_type(max)() const { return b(); } + + friend bool operator==(const uniform_int_distribution& a, + const uniform_int_distribution& b) { + return a.param_ == b.param_; + } + friend bool operator!=(const uniform_int_distribution& a, + const uniform_int_distribution& b) { + return !(a == b); + } + + private: + // Generates a value in the *closed* interval [0, R] + template <typename URBG> + unsigned_type Generate(URBG& g, // NOLINT(runtime/references) + unsigned_type R); + param_type param_; +}; + +// ----------------------------------------------------------------------------- +// Implementation details follow +// ----------------------------------------------------------------------------- +template <typename CharT, typename Traits, typename IntType> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, + const uniform_int_distribution<IntType>& x) { + using stream_type = + typename random_internal::stream_format_type<IntType>::type; + auto saver = random_internal::make_ostream_state_saver(os); + os << static_cast<stream_type>(x.a()) << os.fill() + << static_cast<stream_type>(x.b()); + return os; +} + +template <typename CharT, typename Traits, typename IntType> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, + uniform_int_distribution<IntType>& x) { + using param_type = typename uniform_int_distribution<IntType>::param_type; + using result_type = typename uniform_int_distribution<IntType>::result_type; + using stream_type = + typename random_internal::stream_format_type<IntType>::type; + + stream_type a; + stream_type b; + + auto saver = random_internal::make_istream_state_saver(is); + is >> a >> b; + if (!is.fail()) { + x.param( + param_type(static_cast<result_type>(a), static_cast<result_type>(b))); + } + return is; +} + +template <typename IntType> +template <typename URBG> +typename random_internal::make_unsigned_bits<IntType>::type +uniform_int_distribution<IntType>::Generate( + URBG& g, // NOLINT(runtime/references) + typename random_internal::make_unsigned_bits<IntType>::type R) { + random_internal::FastUniformBits<unsigned_type> fast_bits; + unsigned_type bits = fast_bits(g); + const unsigned_type Lim = R + 1; + if ((R & Lim) == 0) { + // If the interval's length is a power of two range, just take the low bits. + return bits & R; + } + + // Generates a uniform variate on [0, Lim) using fixed-point multiplication. + // The above fast-path guarantees that Lim is representable in unsigned_type. + // + // Algorithm adapted from + // http://lemire.me/blog/2016/06/30/fast-random-shuffling/, with added + // explanation. + // + // The algorithm creates a uniform variate `bits` in the interval [0, 2^N), + // and treats it as the fractional part of a fixed-point real value in [0, 1), + // multiplied by 2^N. For example, 0.25 would be represented as 2^(N - 2), + // because 2^N * 0.25 == 2^(N - 2). + // + // Next, `bits` and `Lim` are multiplied with a wide-multiply to bring the + // value into the range [0, Lim). The integral part (the high word of the + // multiplication result) is then very nearly the desired result. However, + // this is not quite accurate; viewing the multiplication result as one + // double-width integer, the resulting values for the sample are mapped as + // follows: + // + // If the result lies in this interval: Return this value: + // [0, 2^N) 0 + // [2^N, 2 * 2^N) 1 + // ... ... + // [K * 2^N, (K + 1) * 2^N) K + // ... ... + // [(Lim - 1) * 2^N, Lim * 2^N) Lim - 1 + // + // While all of these intervals have the same size, the result of `bits * Lim` + // must be a multiple of `Lim`, and not all of these intervals contain the + // same number of multiples of `Lim`. In particular, some contain + // `F = floor(2^N / Lim)` and some contain `F + 1 = ceil(2^N / Lim)`. This + // difference produces a small nonuniformity, which is corrected by applying + // rejection sampling to one of the values in the "larger intervals" (i.e., + // the intervals containing `F + 1` multiples of `Lim`. + // + // An interval contains `F + 1` multiples of `Lim` if and only if its smallest + // value modulo 2^N is less than `2^N % Lim`. The unique value satisfying + // this property is used as the one for rejection. That is, a value of + // `bits * Lim` is rejected if `(bit * Lim) % 2^N < (2^N % Lim)`. + + using helper = random_internal::wide_multiply<unsigned_type>; + auto product = helper::multiply(bits, Lim); + + // Two optimizations here: + // * Rejection occurs with some probability less than 1/2, and for reasonable + // ranges considerably less (in particular, less than 1/(F+1)), so + // ABSL_PREDICT_FALSE is apt. + // * `Lim` is an overestimate of `threshold`, and doesn't require a divide. + if (ABSL_PREDICT_FALSE(helper::lo(product) < Lim)) { + // This quantity is exactly equal to `2^N % Lim`, but does not require high + // precision calculations: `2^N % Lim` is congruent to `(2^N - Lim) % Lim`. + // Ideally this could be expressed simply as `-X` rather than `2^N - X`, but + // for types smaller than int, this calculation is incorrect due to integer + // promotion rules. + const unsigned_type threshold = + ((std::numeric_limits<unsigned_type>::max)() - Lim + 1) % Lim; + while (helper::lo(product) < threshold) { + bits = fast_bits(g); + product = helper::multiply(bits, Lim); + } + } + + return helper::hi(product); +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_UNIFORM_INT_DISTRIBUTION_H_ diff --git a/absl/random/uniform_int_distribution_test.cc b/absl/random/uniform_int_distribution_test.cc new file mode 100644 index 00000000..aacff88d --- /dev/null +++ b/absl/random/uniform_int_distribution_test.cc @@ -0,0 +1,250 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/uniform_int_distribution.h" + +#include <cmath> +#include <cstdint> +#include <iterator> +#include <random> +#include <sstream> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/chi_square.h" +#include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" + +namespace { + +template <typename IntType> +class UniformIntDistributionTest : public ::testing::Test {}; + +using IntTypes = ::testing::Types<int8_t, uint8_t, int16_t, uint16_t, int32_t, + uint32_t, int64_t, uint64_t>; +TYPED_TEST_SUITE(UniformIntDistributionTest, IntTypes); + +TYPED_TEST(UniformIntDistributionTest, ParamSerializeTest) { + // This test essentially ensures that the parameters serialize, + // not that the values generated cover the full range. + using Limits = std::numeric_limits<TypeParam>; + using param_type = + typename absl::uniform_int_distribution<TypeParam>::param_type; + const TypeParam kMin = std::is_unsigned<TypeParam>::value ? 37 : -105; + const TypeParam kNegOneOrZero = std::is_unsigned<TypeParam>::value ? 0 : -1; + + constexpr int kCount = 1000; + absl::InsecureBitGen gen; + for (const auto& param : { + param_type(), + param_type(2, 2), // Same + param_type(9, 32), + param_type(kMin, 115), + param_type(kNegOneOrZero, Limits::max()), + param_type(Limits::min(), Limits::max()), + param_type(Limits::lowest(), Limits::max()), + param_type(Limits::min() + 1, Limits::max() - 1), + }) { + const auto a = param.a(); + const auto b = param.b(); + absl::uniform_int_distribution<TypeParam> before(a, b); + EXPECT_EQ(before.a(), param.a()); + EXPECT_EQ(before.b(), param.b()); + + { + // Initialize via param_type + absl::uniform_int_distribution<TypeParam> via_param(param); + EXPECT_EQ(via_param, before); + } + + // Initialize via iostreams + std::stringstream ss; + ss << before; + + absl::uniform_int_distribution<TypeParam> after(Limits::min() + 3, + Limits::max() - 5); + + EXPECT_NE(before.a(), after.a()); + EXPECT_NE(before.b(), after.b()); + EXPECT_NE(before.param(), after.param()); + EXPECT_NE(before, after); + + ss >> after; + + EXPECT_EQ(before.a(), after.a()); + EXPECT_EQ(before.b(), after.b()); + EXPECT_EQ(before.param(), after.param()); + EXPECT_EQ(before, after); + + // Smoke test. + auto sample_min = after.max(); + auto sample_max = after.min(); + for (int i = 0; i < kCount; i++) { + auto sample = after(gen); + EXPECT_GE(sample, after.min()); + EXPECT_LE(sample, after.max()); + if (sample > sample_max) { + sample_max = sample; + } + if (sample < sample_min) { + sample_min = sample; + } + } + std::string msg = absl::StrCat("Range: ", +sample_min, ", ", +sample_max); + ABSL_RAW_LOG(INFO, "%s", msg.c_str()); + } +} + +TYPED_TEST(UniformIntDistributionTest, ViolatesPreconditionsDeathTest) { +#if GTEST_HAS_DEATH_TEST + // Hi < Lo + EXPECT_DEBUG_DEATH({ absl::uniform_int_distribution<TypeParam> dist(10, 1); }, + ""); +#endif // GTEST_HAS_DEATH_TEST +#if defined(NDEBUG) + // opt-mode, for invalid parameters, will generate a garbage value, + // but should not enter an infinite loop. + absl::InsecureBitGen gen; + absl::uniform_int_distribution<TypeParam> dist(10, 1); + auto x = dist(gen); + + // Any value will generate a non-empty std::string. + EXPECT_FALSE(absl::StrCat(+x).empty()) << x; +#endif // NDEBUG +} + +TYPED_TEST(UniformIntDistributionTest, TestMoments) { + constexpr int kSize = 100000; + using Limits = std::numeric_limits<TypeParam>; + using param_type = + typename absl::uniform_int_distribution<TypeParam>::param_type; + + absl::InsecureBitGen rng; + std::vector<double> values(kSize); + for (const auto& param : + {param_type(0, Limits::max()), param_type(13, 127)}) { + absl::uniform_int_distribution<TypeParam> dist(param); + for (int i = 0; i < kSize; i++) { + const auto sample = dist(rng); + ASSERT_LE(dist.param().a(), sample); + ASSERT_GE(dist.param().b(), sample); + values[i] = sample; + } + + auto moments = absl::random_internal::ComputeDistributionMoments(values); + const double a = dist.param().a(); + const double b = dist.param().b(); + const double n = (b - a + 1); + const double mean = (a + b) / 2; + const double var = ((b - a + 1) * (b - a + 1) - 1) / 12; + const double kurtosis = 3 - 6 * (n * n + 1) / (5 * (n * n - 1)); + + // TODO(ahh): this is not the right bound + // empirically validated with --runs_per_test=10000. + EXPECT_NEAR(mean, moments.mean, 0.01 * var); + EXPECT_NEAR(var, moments.variance, 0.015 * var); + EXPECT_NEAR(0.0, moments.skewness, 0.025); + EXPECT_NEAR(kurtosis, moments.kurtosis, 0.02 * kurtosis); + } +} + +TYPED_TEST(UniformIntDistributionTest, ChiSquaredTest50) { + using absl::random_internal::kChiSquared; + + constexpr size_t kTrials = 1000; + constexpr int kBuckets = 50; // inclusive, so actally +1 + constexpr double kExpected = + static_cast<double>(kTrials) / static_cast<double>(kBuckets); + + // Empirically validated with --runs_per_test=10000. + const int kThreshold = + absl::random_internal::ChiSquareValue(kBuckets, 0.999999); + + const TypeParam min = std::is_unsigned<TypeParam>::value ? 37 : -37; + const TypeParam max = min + kBuckets; + + absl::InsecureBitGen rng; + absl::uniform_int_distribution<TypeParam> dist(min, max); + + std::vector<int32_t> counts(kBuckets + 1, 0); + for (size_t i = 0; i < kTrials; i++) { + auto x = dist(rng); + counts[x - min]++; + } + double chi_square = absl::random_internal::ChiSquareWithExpected( + std::begin(counts), std::end(counts), kExpected); + if (chi_square > kThreshold) { + double p_value = + absl::random_internal::ChiSquarePValue(chi_square, kBuckets); + + // Chi-squared test failed. Output does not appear to be uniform. + std::string msg; + for (const auto& a : counts) { + absl::StrAppend(&msg, a, "\n"); + } + absl::StrAppend(&msg, kChiSquared, " p-value ", p_value, "\n"); + absl::StrAppend(&msg, "High ", kChiSquared, " value: ", chi_square, " > ", + kThreshold); + ABSL_RAW_LOG(INFO, "%s", msg.c_str()); + FAIL() << msg; + } +} + +TEST(UniformIntDistributionTest, StabilityTest) { + // absl::uniform_int_distribution stability relies only on integer operations. + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector<int> output(12); + + { + absl::uniform_int_distribution<int32_t> dist(0, 4); + for (auto& v : output) { + v = dist(urbg); + } + } + EXPECT_EQ(12, urbg.invocations()); + EXPECT_THAT(output, testing::ElementsAre(4, 4, 3, 2, 1, 0, 1, 4, 3, 1, 3, 1)); + + { + urbg.reset(); + absl::uniform_int_distribution<int32_t> dist(0, 100); + for (auto& v : output) { + v = dist(urbg); + } + } + EXPECT_EQ(12, urbg.invocations()); + EXPECT_THAT(output, testing::ElementsAre(97, 86, 75, 41, 36, 16, 38, 92, 67, + 30, 80, 38)); + + { + urbg.reset(); + absl::uniform_int_distribution<int32_t> dist(0, 10000); + for (auto& v : output) { + v = dist(urbg); + } + } + EXPECT_EQ(12, urbg.invocations()); + EXPECT_THAT(output, testing::ElementsAre(9648, 8562, 7439, 4089, 3571, 1602, + 3813, 9195, 6641, 2986, 7956, 3765)); +} + +} // namespace diff --git a/absl/random/uniform_real_distribution.h b/absl/random/uniform_real_distribution.h new file mode 100644 index 00000000..0ea3163a --- /dev/null +++ b/absl/random/uniform_real_distribution.h @@ -0,0 +1,195 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: uniform_real_distribution.h +// ----------------------------------------------------------------------------- +// +// This header defines a class for representing a uniform floating-point +// distribution over a half-open interval [a,b). You use this distribution in +// combination with an Abseil random bit generator to produce random values +// according to the rules of the distribution. +// +// `absl::uniform_real_distribution` is a drop-in replacement for the C++11 +// `std::uniform_real_distribution` [rand.dist.uni.real] but is considerably +// faster than the libstdc++ implementation. +// +// Note: the standard-library version may occasionally return `1.0` when +// default-initialized. See https://bugs.llvm.org//show_bug.cgi?id=18767 +// `absl::uniform_real_distribution` does not exhibit this behavior. + +#ifndef ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_ +#define ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_ + +#include <cassert> +#include <cmath> +#include <cstdint> +#include <istream> +#include <limits> +#include <type_traits> + +#include "absl/random/internal/distribution_impl.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/iostream_state_saver.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// absl::uniform_real_distribution<T> +// +// This distribution produces random floating-point values uniformly distributed +// over the half-open interval [a, b). +// +// Example: +// +// absl::BitGen gen; +// +// // Use the distribution to produce a value between 0.0 (inclusive) +// // and 1.0 (exclusive). +// int value = absl::uniform_real_distribution<double>(0, 1)(gen); +// +template <typename RealType = double> +class uniform_real_distribution { + public: + using result_type = RealType; + + class param_type { + public: + using distribution_type = uniform_real_distribution; + + explicit param_type(result_type lo = 0, result_type hi = 1) + : lo_(lo), hi_(hi), range_(hi - lo) { + // [rand.dist.uni.real] preconditions 2 & 3 + assert(lo <= hi); + // NOTE: For integral types, we can promote the range to an unsigned type, + // which gives full width of the range. However for real (fp) types, this + // is not possible, so value generation cannot use the full range of the + // real type. + assert(range_ <= (std::numeric_limits<result_type>::max)()); + } + + result_type a() const { return lo_; } + result_type b() const { return hi_; } + + friend bool operator==(const param_type& a, const param_type& b) { + return a.lo_ == b.lo_ && a.hi_ == b.hi_; + } + + friend bool operator!=(const param_type& a, const param_type& b) { + return !(a == b); + } + + private: + friend class uniform_real_distribution; + result_type lo_, hi_, range_; + + static_assert(std::is_floating_point<RealType>::value, + "Class-template absl::uniform_real_distribution<> must be " + "parameterized using a floating-point type."); + }; + + uniform_real_distribution() : uniform_real_distribution(0) {} + + explicit uniform_real_distribution(result_type lo, result_type hi = 1) + : param_(lo, hi) {} + + explicit uniform_real_distribution(const param_type& param) : param_(param) {} + + // uniform_real_distribution<T>::reset() + // + // Resets the uniform real distribution. Note that this function has no effect + // because the distribution already produces independent values. + void reset() {} + + template <typename URBG> + result_type operator()(URBG& gen) { // NOLINT(runtime/references) + return operator()(gen, param_); + } + + template <typename URBG> + result_type operator()(URBG& gen, // NOLINT(runtime/references) + const param_type& p); + + result_type a() const { return param_.a(); } + result_type b() const { return param_.b(); } + + param_type param() const { return param_; } + void param(const param_type& params) { param_ = params; } + + result_type(min)() const { return a(); } + result_type(max)() const { return b(); } + + friend bool operator==(const uniform_real_distribution& a, + const uniform_real_distribution& b) { + return a.param_ == b.param_; + } + friend bool operator!=(const uniform_real_distribution& a, + const uniform_real_distribution& b) { + return a.param_ != b.param_; + } + + private: + param_type param_; + random_internal::FastUniformBits<uint64_t> fast_u64_; +}; + +// ----------------------------------------------------------------------------- +// Implementation details follow +// ----------------------------------------------------------------------------- +template <typename RealType> +template <typename URBG> +typename uniform_real_distribution<RealType>::result_type +uniform_real_distribution<RealType>::operator()( + URBG& gen, const param_type& p) { // NOLINT(runtime/references) + using random_internal::PositiveValueT; + while (true) { + const result_type sample = random_internal::RandU64ToReal< + result_type>::template Value<PositiveValueT, true>(fast_u64_(gen)); + const result_type res = p.a() + (sample * p.range_); + if (res < p.b() || p.range_ <= 0 || !std::isfinite(p.range_)) { + return res; + } + // else sample rejected, try again. + } +} + +template <typename CharT, typename Traits, typename RealType> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const uniform_real_distribution<RealType>& x) { + auto saver = random_internal::make_ostream_state_saver(os); + os.precision(random_internal::stream_precision_helper<RealType>::kPrecision); + os << x.a() << os.fill() << x.b(); + return os; +} + +template <typename CharT, typename Traits, typename RealType> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + uniform_real_distribution<RealType>& x) { // NOLINT(runtime/references) + using param_type = typename uniform_real_distribution<RealType>::param_type; + using result_type = typename uniform_real_distribution<RealType>::result_type; + auto saver = random_internal::make_istream_state_saver(is); + auto a = random_internal::read_floating_point<result_type>(is); + if (is.fail()) return is; + auto b = random_internal::read_floating_point<result_type>(is); + if (!is.fail()) { + x.param(param_type(a, b)); + } + return is; +} +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_ diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc new file mode 100644 index 00000000..597f0ee5 --- /dev/null +++ b/absl/random/uniform_real_distribution_test.cc @@ -0,0 +1,322 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/uniform_real_distribution.h" + +#include <cmath> +#include <cstdint> +#include <iterator> +#include <random> +#include <sstream> +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/chi_square.h" +#include "absl/random/internal/distribution_test_util.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" + +// NOTES: +// * Some documentation on generating random real values suggests that +// it is possible to use std::nextafter(b, DBL_MAX) to generate a value on +// the closed range [a, b]. Unfortunately, that technique is not universally +// reliable due to floating point quantization. +// +// * absl::uniform_real_distribution<float> generates between 2^28 and 2^29 +// distinct floating point values in the range [0, 1). +// +// * absl::uniform_real_distribution<float> generates at least 2^23 distinct +// floating point values in the range [1, 2). This should be the same as +// any other range covered by a single exponent in IEEE 754. +// +// * absl::uniform_real_distribution<double> generates more than 2^52 distinct +// values in the range [0, 1), and should generate at least 2^52 distinct +// values in the range of [1, 2). +// + +namespace { + +template <typename RealType> +class UniformRealDistributionTest : public ::testing::Test {}; + +using RealTypes = ::testing::Types<float, double, long double>; +TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes); + +TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { + using param_type = + typename absl::uniform_real_distribution<TypeParam>::param_type; + + constexpr const TypeParam a{1152921504606846976}; + + constexpr int kCount = 1000; + absl::InsecureBitGen gen; + for (const auto& param : { + param_type(), + param_type(TypeParam(2.0), TypeParam(2.0)), // Same + param_type(TypeParam(-0.1), TypeParam(0.1)), + param_type(TypeParam(0.05), TypeParam(0.12)), + param_type(TypeParam(-0.05), TypeParam(0.13)), + param_type(TypeParam(-0.05), TypeParam(-0.02)), + // double range = 0 + // 2^60 , 2^60 + 2^6 + param_type(a, TypeParam(1152921504606847040)), + // 2^60 , 2^60 + 2^7 + param_type(a, TypeParam(1152921504606847104)), + // double range = 2^8 + // 2^60 , 2^60 + 2^8 + param_type(a, TypeParam(1152921504606847232)), + // float range = 0 + // 2^60 , 2^60 + 2^36 + param_type(a, TypeParam(1152921573326323712)), + // 2^60 , 2^60 + 2^37 + param_type(a, TypeParam(1152921642045800448)), + // float range = 2^38 + // 2^60 , 2^60 + 2^38 + param_type(a, TypeParam(1152921779484753920)), + // Limits + param_type(0, std::numeric_limits<TypeParam>::max()), + param_type(std::numeric_limits<TypeParam>::lowest(), 0), + param_type(0, std::numeric_limits<TypeParam>::epsilon()), + param_type(-std::numeric_limits<TypeParam>::epsilon(), + std::numeric_limits<TypeParam>::epsilon()), + param_type(std::numeric_limits<TypeParam>::epsilon(), + 2 * std::numeric_limits<TypeParam>::epsilon()), + }) { + // Validate parameters. + const auto a = param.a(); + const auto b = param.b(); + absl::uniform_real_distribution<TypeParam> before(a, b); + EXPECT_EQ(before.a(), param.a()); + EXPECT_EQ(before.b(), param.b()); + + { + absl::uniform_real_distribution<TypeParam> via_param(param); + EXPECT_EQ(via_param, before); + } + + std::stringstream ss; + ss << before; + absl::uniform_real_distribution<TypeParam> after(TypeParam(1.0), + TypeParam(3.1)); + + EXPECT_NE(before.a(), after.a()); + EXPECT_NE(before.b(), after.b()); + EXPECT_NE(before.param(), after.param()); + EXPECT_NE(before, after); + + ss >> after; + + EXPECT_EQ(before.a(), after.a()); + EXPECT_EQ(before.b(), after.b()); + EXPECT_EQ(before.param(), after.param()); + EXPECT_EQ(before, after); + + // Smoke test. + auto sample_min = after.max(); + auto sample_max = after.min(); + for (int i = 0; i < kCount; i++) { + auto sample = after(gen); + // Failure here indicates a bug in uniform_real_distribution::operator(), + // or bad parameters--range too large, etc. + if (after.min() == after.max()) { + EXPECT_EQ(sample, after.min()); + } else { + EXPECT_GE(sample, after.min()); + EXPECT_LT(sample, after.max()); + } + if (sample > sample_max) { + sample_max = sample; + } + if (sample < sample_min) { + sample_min = sample; + } + } + + if (!std::is_same<TypeParam, long double>::value) { + // static_cast<double>(long double) can overflow. + std::string msg = absl::StrCat("Range: ", static_cast<double>(sample_min), + ", ", static_cast<double>(sample_max)); + ABSL_RAW_LOG(INFO, "%s", msg.c_str()); + } + } +} + +TYPED_TEST(UniformRealDistributionTest, ViolatesPreconditionsDeathTest) { +#if GTEST_HAS_DEATH_TEST + // Hi < Lo + EXPECT_DEBUG_DEATH( + { absl::uniform_real_distribution<TypeParam> dist(10.0, 1.0); }, ""); + + // Hi - Lo > numeric_limits<>::max() + EXPECT_DEBUG_DEATH( + { + absl::uniform_real_distribution<TypeParam> dist( + std::numeric_limits<TypeParam>::lowest(), + std::numeric_limits<TypeParam>::max()); + }, + ""); +#endif // GTEST_HAS_DEATH_TEST +#if defined(NDEBUG) + // opt-mode, for invalid parameters, will generate a garbage value, + // but should not enter an infinite loop. + absl::InsecureBitGen gen; + { + absl::uniform_real_distribution<TypeParam> dist(10.0, 1.0); + auto x = dist(gen); + EXPECT_FALSE(std::isnan(x)) << x; + } + { + absl::uniform_real_distribution<TypeParam> dist( + std::numeric_limits<TypeParam>::lowest(), + std::numeric_limits<TypeParam>::max()); + auto x = dist(gen); + // Infinite result. + EXPECT_FALSE(std::isfinite(x)) << x; + } +#endif // NDEBUG +} + +TYPED_TEST(UniformRealDistributionTest, TestMoments) { + constexpr int kSize = 1000000; + std::vector<double> values(kSize); + + absl::InsecureBitGen rng; + absl::uniform_real_distribution<TypeParam> dist; + for (int i = 0; i < kSize; i++) { + values[i] = dist(rng); + } + + const auto moments = + absl::random_internal::ComputeDistributionMoments(values); + EXPECT_NEAR(0.5, moments.mean, 0.01); + EXPECT_NEAR(1 / 12.0, moments.variance, 0.015); + EXPECT_NEAR(0.0, moments.skewness, 0.02); + EXPECT_NEAR(9 / 5.0, moments.kurtosis, 0.015); +} + +TYPED_TEST(UniformRealDistributionTest, ChiSquaredTest50) { + using absl::random_internal::kChiSquared; + using param_type = + typename absl::uniform_real_distribution<TypeParam>::param_type; + + constexpr size_t kTrials = 100000; + constexpr int kBuckets = 50; + constexpr double kExpected = + static_cast<double>(kTrials) / static_cast<double>(kBuckets); + + // 1-in-100000 threshold, but remember, there are about 8 tests + // in this file. And the test could fail for other reasons. + // Empirically validated with --runs_per_test=10000. + const int kThreshold = + absl::random_internal::ChiSquareValue(kBuckets - 1, 0.999999); + + absl::InsecureBitGen rng; + for (const auto& param : {param_type(0, 1), param_type(5, 12), + param_type(-5, 13), param_type(-5, -2)}) { + const double min_val = param.a(); + const double max_val = param.b(); + const double factor = kBuckets / (max_val - min_val); + + std::vector<int32_t> counts(kBuckets, 0); + absl::uniform_real_distribution<TypeParam> dist(param); + for (size_t i = 0; i < kTrials; i++) { + auto x = dist(rng); + auto bucket = static_cast<size_t>((x - min_val) * factor); + counts[bucket]++; + } + + double chi_square = absl::random_internal::ChiSquareWithExpected( + std::begin(counts), std::end(counts), kExpected); + if (chi_square > kThreshold) { + double p_value = + absl::random_internal::ChiSquarePValue(chi_square, kBuckets); + + // Chi-squared test failed. Output does not appear to be uniform. + std::string msg; + for (const auto& a : counts) { + absl::StrAppend(&msg, a, "\n"); + } + absl::StrAppend(&msg, kChiSquared, " p-value ", p_value, "\n"); + absl::StrAppend(&msg, "High ", kChiSquared, " value: ", chi_square, " > ", + kThreshold); + ABSL_RAW_LOG(INFO, "%s", msg.c_str()); + FAIL() << msg; + } + } +} + +TYPED_TEST(UniformRealDistributionTest, StabilityTest) { + // absl::uniform_real_distribution stability relies only on + // random_internal::RandU64ToDouble and random_internal::RandU64ToFloat. + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector<int> output(12); + + absl::uniform_real_distribution<TypeParam> dist; + std::generate(std::begin(output), std::end(output), [&] { + return static_cast<int>(TypeParam(1000000) * dist(urbg)); + }); + + EXPECT_THAT( + output, // + testing::ElementsAre(59, 999246, 762494, 395876, 167716, 82545, 925251, + 77341, 12527, 708791, 834451, 932808)); +} + +TEST(UniformRealDistributionTest, AlgorithmBounds) { + absl::uniform_real_distribution<double> dist; + + { + // This returns the smallest value >0 from absl::uniform_real_distribution. + absl::random_internal::sequence_urbg urbg({0x0000000000000001ull}); + double a = dist(urbg); + EXPECT_EQ(a, 5.42101086242752217004e-20); + } + + { + // This returns a value very near 0.5 from absl::uniform_real_distribution. + absl::random_internal::sequence_urbg urbg({0x7fffffffffffffefull}); + double a = dist(urbg); + EXPECT_EQ(a, 0.499999999999999944489); + } + { + // This returns a value very near 0.5 from absl::uniform_real_distribution. + absl::random_internal::sequence_urbg urbg({0x8000000000000000ull}); + double a = dist(urbg); + EXPECT_EQ(a, 0.5); + } + + { + // This returns the largest value <1 from absl::uniform_real_distribution. + absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFEFull}); + double a = dist(urbg); + EXPECT_EQ(a, 0.999999999999999888978); + } + { + // This *ALSO* returns the largest value <1. + absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFFFull}); + double a = dist(urbg); + EXPECT_EQ(a, 0.999999999999999888978); + } +} + +} // namespace diff --git a/absl/random/zipf_distribution.h b/absl/random/zipf_distribution.h new file mode 100644 index 00000000..bba98e88 --- /dev/null +++ b/absl/random/zipf_distribution.h @@ -0,0 +1,271 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_ZIPF_DISTRIBUTION_H_ +#define ABSL_RANDOM_ZIPF_DISTRIBUTION_H_ + +#include <cassert> +#include <cmath> +#include <istream> +#include <limits> +#include <ostream> +#include <type_traits> + +#include "absl/random/internal/iostream_state_saver.h" +#include "absl/random/uniform_real_distribution.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +// absl::zipf_distribution produces random integer-values in the range [0, k], +// distributed according to the discrete probability function: +// +// P(x) = (v + x) ^ -q +// +// The parameter `v` must be greater than 0 and the parameter `q` must be +// greater than 1. If either of these parameters take invalid values then the +// behavior is undefined. +// +// IntType is the result_type generated by the generator. It must be of integral +// type; a static_assert ensures this is the case. +// +// The implementation is based on W.Hormann, G.Derflinger: +// +// "Rejection-Inversion to Generate Variates from Monotone Discrete +// Distributions" +// +// http://eeyore.wu-wien.ac.at/papers/96-04-04.wh-der.ps.gz +// +template <typename IntType = int> +class zipf_distribution { + public: + using result_type = IntType; + + class param_type { + public: + using distribution_type = zipf_distribution; + + // Preconditions: k > 0, v > 0, q > 1 + // The precondidtions are validated when NDEBUG is not defined via + // a pair of assert() directives. + // If NDEBUG is defined and either or both of these parameters take invalid + // values, the behavior of the class is undefined. + explicit param_type(result_type k = (std::numeric_limits<IntType>::max)(), + double q = 2.0, double v = 1.0); + + result_type k() const { return k_; } + double q() const { return q_; } + double v() const { return v_; } + + friend bool operator==(const param_type& a, const param_type& b) { + return a.k_ == b.k_ && a.q_ == b.q_ && a.v_ == b.v_; + } + friend bool operator!=(const param_type& a, const param_type& b) { + return !(a == b); + } + + private: + friend class zipf_distribution; + inline double h(double x) const; + inline double hinv(double x) const; + inline double compute_s() const; + inline double pow_negative_q(double x) const; + + // Parameters here are exactly the same as the parameters of Algorithm ZRI + // in the paper. + IntType k_; + double q_; + double v_; + + double one_minus_q_; // 1-q + double s_; + double one_minus_q_inv_; // 1 / 1-q + double hxm_; // h(k + 0.5) + double hx0_minus_hxm_; // h(x0) - h(k + 0.5) + + static_assert(std::is_integral<IntType>::value, + "Class-template absl::zipf_distribution<> must be " + "parameterized using an integral type."); + }; + + zipf_distribution() + : zipf_distribution((std::numeric_limits<IntType>::max)()) {} + + explicit zipf_distribution(result_type k, double q = 2.0, double v = 1.0) + : param_(k, q, v) {} + + explicit zipf_distribution(const param_type& p) : param_(p) {} + + void reset() {} + + template <typename URBG> + result_type operator()(URBG& g) { // NOLINT(runtime/references) + return (*this)(g, param_); + } + + template <typename URBG> + result_type operator()(URBG& g, // NOLINT(runtime/references) + const param_type& p); + + result_type k() const { return param_.k(); } + double q() const { return param_.q(); } + double v() const { return param_.v(); } + + param_type param() const { return param_; } + void param(const param_type& p) { param_ = p; } + + result_type(min)() const { return 0; } + result_type(max)() const { return k(); } + + friend bool operator==(const zipf_distribution& a, + const zipf_distribution& b) { + return a.param_ == b.param_; + } + friend bool operator!=(const zipf_distribution& a, + const zipf_distribution& b) { + return a.param_ != b.param_; + } + + private: + param_type param_; +}; + +// -------------------------------------------------------------------------- +// Implementation details follow +// -------------------------------------------------------------------------- + +template <typename IntType> +zipf_distribution<IntType>::param_type::param_type( + typename zipf_distribution<IntType>::result_type k, double q, double v) + : k_(k), q_(q), v_(v), one_minus_q_(1 - q) { + assert(q > 1); + assert(v > 0); + assert(k > 0); + one_minus_q_inv_ = 1 / one_minus_q_; + + // Setup for the ZRI algorithm (pg 17 of the paper). + // Compute: h(i max) => h(k + 0.5) + constexpr double kMax = 18446744073709549568.0; + double kd = static_cast<double>(k); + // TODO(absl-team): Determine if this check is needed, and if so, add a test + // that fails for k > kMax + if (kd > kMax) { + // Ensure that our maximum value is capped to a value which will + // round-trip back through double. + kd = kMax; + } + hxm_ = h(kd + 0.5); + + // Compute: h(0) + const bool use_precomputed = (v == 1.0 && q == 2.0); + const double h0x5 = use_precomputed ? (-1.0 / 1.5) // exp(-log(1.5)) + : h(0.5); + const double elogv_q = (v_ == 1.0) ? 1 : pow_negative_q(v_); + + // h(0) = h(0.5) - exp(log(v) * -q) + hx0_minus_hxm_ = (h0x5 - elogv_q) - hxm_; + + // And s + s_ = use_precomputed ? 0.46153846153846123 : compute_s(); +} + +template <typename IntType> +double zipf_distribution<IntType>::param_type::h(double x) const { + // std::exp(one_minus_q_ * std::log(v_ + x)) * one_minus_q_inv_; + x += v_; + return (one_minus_q_ == -1.0) + ? (-1.0 / x) // -exp(-log(x)) + : (std::exp(std::log(x) * one_minus_q_) * one_minus_q_inv_); +} + +template <typename IntType> +double zipf_distribution<IntType>::param_type::hinv(double x) const { + // std::exp(one_minus_q_inv_ * std::log(one_minus_q_ * x)) - v_; + return -v_ + ((one_minus_q_ == -1.0) + ? (-1.0 / x) // exp(-log(-x)) + : std::exp(one_minus_q_inv_ * std::log(one_minus_q_ * x))); +} + +template <typename IntType> +double zipf_distribution<IntType>::param_type::compute_s() const { + // 1 - hinv(h(1.5) - std::exp(std::log(v_ + 1) * -q_)); + return 1.0 - hinv(h(1.5) - pow_negative_q(v_ + 1.0)); +} + +template <typename IntType> +double zipf_distribution<IntType>::param_type::pow_negative_q(double x) const { + // std::exp(std::log(x) * -q_); + return q_ == 2.0 ? (1.0 / (x * x)) : std::exp(std::log(x) * -q_); +} + +template <typename IntType> +template <typename URBG> +typename zipf_distribution<IntType>::result_type +zipf_distribution<IntType>::operator()( + URBG& g, const param_type& p) { // NOLINT(runtime/references) + absl::uniform_real_distribution<double> uniform_double; + double k; + for (;;) { + const double v = uniform_double(g); + const double u = p.hxm_ + v * p.hx0_minus_hxm_; + const double x = p.hinv(u); + k = rint(x); // std::floor(x + 0.5); + if (k > p.k()) continue; // reject k > max_k + if (k - x <= p.s_) break; + const double h = p.h(k + 0.5); + const double r = p.pow_negative_q(p.v_ + k); + if (u >= h - r) break; + } + IntType ki = static_cast<IntType>(k); + assert(ki <= p.k_); + return ki; +} + +template <typename CharT, typename Traits, typename IntType> +std::basic_ostream<CharT, Traits>& operator<<( + std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) + const zipf_distribution<IntType>& x) { + using stream_type = + typename random_internal::stream_format_type<IntType>::type; + auto saver = random_internal::make_ostream_state_saver(os); + os.precision(random_internal::stream_precision_helper<double>::kPrecision); + os << static_cast<stream_type>(x.k()) << os.fill() << x.q() << os.fill() + << x.v(); + return os; +} + +template <typename CharT, typename Traits, typename IntType> +std::basic_istream<CharT, Traits>& operator>>( + std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) + zipf_distribution<IntType>& x) { // NOLINT(runtime/references) + using result_type = typename zipf_distribution<IntType>::result_type; + using param_type = typename zipf_distribution<IntType>::param_type; + using stream_type = + typename random_internal::stream_format_type<IntType>::type; + stream_type k; + double q; + double v; + + auto saver = random_internal::make_istream_state_saver(is); + is >> k >> q >> v; + if (!is.fail()) { + x.param(param_type(static_cast<result_type>(k), q, v)); + } + return is; +} + +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_RANDOM_ZIPF_DISTRIBUTION_H_ diff --git a/absl/random/zipf_distribution_test.cc b/absl/random/zipf_distribution_test.cc new file mode 100644 index 00000000..4d4a0fcf --- /dev/null +++ b/absl/random/zipf_distribution_test.cc @@ -0,0 +1,423 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/zipf_distribution.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <random> +#include <string> +#include <utility> +#include <vector> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/random/internal/chi_square.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/strip.h" + +namespace { + +using ::absl::random_internal::kChiSquared; +using ::testing::ElementsAre; + +template <typename IntType> +class ZipfDistributionTypedTest : public ::testing::Test {}; + +using IntTypes = ::testing::Types<int, int8_t, int16_t, int32_t, int64_t, + uint8_t, uint16_t, uint32_t, uint64_t>; +TYPED_TEST_CASE(ZipfDistributionTypedTest, IntTypes); + +TYPED_TEST(ZipfDistributionTypedTest, SerializeTest) { + using param_type = typename absl::zipf_distribution<TypeParam>::param_type; + + constexpr int kCount = 1000; + absl::InsecureBitGen gen; + for (const auto& param : { + param_type(), + param_type(32), + param_type(100, 3, 2), + param_type(std::numeric_limits<TypeParam>::max(), 4, 3), + param_type(std::numeric_limits<TypeParam>::max() / 2), + }) { + // Validate parameters. + const auto k = param.k(); + const auto q = param.q(); + const auto v = param.v(); + + absl::zipf_distribution<TypeParam> before(k, q, v); + EXPECT_EQ(before.k(), param.k()); + EXPECT_EQ(before.q(), param.q()); + EXPECT_EQ(before.v(), param.v()); + + { + absl::zipf_distribution<TypeParam> via_param(param); + EXPECT_EQ(via_param, before); + } + + // Validate stream serialization. + std::stringstream ss; + ss << before; + absl::zipf_distribution<TypeParam> after(4, 5.5, 4.4); + + EXPECT_NE(before.k(), after.k()); + EXPECT_NE(before.q(), after.q()); + EXPECT_NE(before.v(), after.v()); + EXPECT_NE(before.param(), after.param()); + EXPECT_NE(before, after); + + ss >> after; + + EXPECT_EQ(before.k(), after.k()); + EXPECT_EQ(before.q(), after.q()); + EXPECT_EQ(before.v(), after.v()); + EXPECT_EQ(before.param(), after.param()); + EXPECT_EQ(before, after); + + // Smoke test. + auto sample_min = after.max(); + auto sample_max = after.min(); + for (int i = 0; i < kCount; i++) { + auto sample = after(gen); + EXPECT_GE(sample, after.min()); + EXPECT_LE(sample, after.max()); + if (sample > sample_max) sample_max = sample; + if (sample < sample_min) sample_min = sample; + } + ABSL_INTERNAL_LOG(INFO, + absl::StrCat("Range: ", +sample_min, ", ", +sample_max)); + } +} + +class ZipfModel { + public: + ZipfModel(size_t k, double q, double v) : k_(k), q_(q), v_(v) {} + + double mean() const { return mean_; } + + // For the other moments of the Zipf distribution, see, for example, + // http://mathworld.wolfram.com/ZipfDistribution.html + + // PMF(k) = (1 / k^s) / H(N,s) + // Returns the probability that any single invocation returns k. + double PMF(size_t i) { return i >= hnq_.size() ? 0.0 : hnq_[i] / sum_hnq_; } + + // CDF = H(k, s) / H(N,s) + double CDF(size_t i) { + if (i >= hnq_.size()) { + return 1.0; + } + auto it = std::begin(hnq_); + double h = 0.0; + for (const auto end = it; it != end; it++) { + h += *it; + } + return h / sum_hnq_; + } + + // The InverseCDF returns the k values which bound p on the upper and lower + // bound. Since there is no closed-form solution, this is implemented as a + // bisction of the cdf. + std::pair<size_t, size_t> InverseCDF(double p) { + size_t min = 0; + size_t max = hnq_.size(); + while (max > min + 1) { + size_t target = (max + min) >> 1; + double x = CDF(target); + if (x > p) { + max = target; + } else { + min = target; + } + } + return {min, max}; + } + + // Compute the probability totals, which are based on the generalized harmonic + // number, H(N,s). + // H(N,s) == SUM(k=1..N, 1 / k^s) + // + // In the limit, H(N,s) == zetac(s) + 1. + // + // NOTE: The mean of a zipf distribution could be computed here as well. + // Mean := H(N, s-1) / H(N,s). + // Given the parameter v = 1, this gives the following function: + // (Hn(100, 1) - Hn(1,1)) / (Hn(100,2) - Hn(1,2)) = 6.5944 + // + void Init() { + if (!hnq_.empty()) { + return; + } + hnq_.clear(); + hnq_.reserve(std::min(k_, size_t{1000})); + + sum_hnq_ = 0; + double qm1 = q_ - 1.0; + double sum_hnq_m1 = 0; + for (size_t i = 0; i < k_; i++) { + // Partial n-th generalized harmonic number + const double x = v_ + i; + + // H(n, q-1) + const double hnqm1 = + (q_ == 2.0) ? (1.0 / x) + : (q_ == 3.0) ? (1.0 / (x * x)) : std::pow(x, -qm1); + sum_hnq_m1 += hnqm1; + + // H(n, q) + const double hnq = + (q_ == 2.0) ? (1.0 / (x * x)) + : (q_ == 3.0) ? (1.0 / (x * x * x)) : std::pow(x, -q_); + sum_hnq_ += hnq; + hnq_.push_back(hnq); + if (i > 1000 && hnq <= 1e-10) { + // The harmonic number is too small. + break; + } + } + assert(sum_hnq_ > 0); + mean_ = sum_hnq_m1 / sum_hnq_; + } + + private: + const size_t k_; + const double q_; + const double v_; + + double mean_; + std::vector<double> hnq_; + double sum_hnq_; +}; + +using zipf_u64 = absl::zipf_distribution<uint64_t>; + +class ZipfTest : public testing::TestWithParam<zipf_u64::param_type>, + public ZipfModel { + public: + ZipfTest() : ZipfModel(GetParam().k(), GetParam().q(), GetParam().v()) {} + + absl::InsecureBitGen rng_; +}; + +TEST_P(ZipfTest, ChiSquaredTest) { + const auto& param = GetParam(); + Init(); + + size_t trials = 10000; + + // Find the split-points for the buckets. + std::vector<size_t> points; + std::vector<double> expected; + { + double last_cdf = 0.0; + double min_p = 1.0; + for (double p = 0.01; p < 1.0; p += 0.01) { + auto x = InverseCDF(p); + if (points.empty() || points.back() < x.second) { + const double p = CDF(x.second); + points.push_back(x.second); + double q = p - last_cdf; + expected.push_back(q); + last_cdf = p; + if (q < min_p) { + min_p = q; + } + } + } + if (last_cdf < 0.999) { + points.push_back(std::numeric_limits<size_t>::max()); + double q = 1.0 - last_cdf; + expected.push_back(q); + if (q < min_p) { + min_p = q; + } + } else { + points.back() = std::numeric_limits<size_t>::max(); + expected.back() += (1.0 - last_cdf); + } + // The Chi-Squared score is not completely scale-invariant; it works best + // when the small values are in the small digits. + trials = static_cast<size_t>(8.0 / min_p); + } + ASSERT_GT(points.size(), 0); + + // Generate n variates and fill the counts vector with the count of their + // occurrences. + std::vector<int64_t> buckets(points.size(), 0); + double avg = 0; + { + zipf_u64 dis(param); + for (size_t i = 0; i < trials; i++) { + uint64_t x = dis(rng_); + ASSERT_LE(x, dis.max()); + ASSERT_GE(x, dis.min()); + avg += static_cast<double>(x); + auto it = std::upper_bound(std::begin(points), std::end(points), + static_cast<size_t>(x)); + buckets[std::distance(std::begin(points), it)]++; + } + avg = avg / static_cast<double>(trials); + } + + // Validate the output using the Chi-Squared test. + for (auto& e : expected) { + e *= trials; + } + + // The null-hypothesis is that the distribution is a poisson distribution with + // the provided mean (not estimated from the data). + const int dof = static_cast<int>(expected.size()) - 1; + + // NOTE: This test runs about 15x per invocation, so a value of 0.9995 is + // approximately correct for a test suite failure rate of 1 in 100. In + // practice we see failures slightly higher than that. + const double threshold = absl::random_internal::ChiSquareValue(dof, 0.9999); + + const double chi_square = absl::random_internal::ChiSquare( + std::begin(buckets), std::end(buckets), std::begin(expected), + std::end(expected)); + + const double p_actual = + absl::random_internal::ChiSquarePValue(chi_square, dof); + + // Log if the chi_squared value is above the threshold. + if (chi_square > threshold) { + ABSL_INTERNAL_LOG(INFO, "values"); + for (size_t i = 0; i < expected.size(); i++) { + ABSL_INTERNAL_LOG(INFO, absl::StrCat(points[i], ": ", buckets[i], + " vs. E=", expected[i])); + } + ABSL_INTERNAL_LOG(INFO, absl::StrCat("trials ", trials)); + ABSL_INTERNAL_LOG(INFO, + absl::StrCat("mean ", avg, " vs. expected ", mean())); + ABSL_INTERNAL_LOG(INFO, absl::StrCat(kChiSquared, "(data, ", dof, ") = ", + chi_square, " (", p_actual, ")")); + ABSL_INTERNAL_LOG(INFO, + absl::StrCat(kChiSquared, " @ 0.9995 = ", threshold)); + FAIL() << kChiSquared << " value of " << chi_square + << " is above the threshold."; + } +} + +std::vector<zipf_u64::param_type> GenParams() { + using param = zipf_u64::param_type; + const auto k = param().k(); + const auto q = param().q(); + const auto v = param().v(); + const uint64_t k2 = 1 << 10; + return std::vector<zipf_u64::param_type>{ + // Default + param(k, q, v), + // vary K + param(4, q, v), param(1 << 4, q, v), param(k2, q, v), + // vary V + param(k2, q, 0.5), param(k2, q, 1.5), param(k2, q, 2.5), param(k2, q, 10), + // vary Q + param(k2, 1.5, v), param(k2, 3, v), param(k2, 5, v), param(k2, 10, v), + // Vary V & Q + param(k2, 1.5, 0.5), param(k2, 3, 1.5), param(k, 10, 10)}; +} + +std::string ParamName( + const ::testing::TestParamInfo<zipf_u64::param_type>& info) { + const auto& p = info.param; + std::string name = absl::StrCat("k_", p.k(), "__q_", absl::SixDigits(p.q()), + "__v_", absl::SixDigits(p.v())); + return absl::StrReplaceAll(name, {{"+", "_"}, {"-", "_"}, {".", "_"}}); +} + +INSTANTIATE_TEST_SUITE_P(All, ZipfTest, ::testing::ValuesIn(GenParams()), + ParamName); + +// NOTE: absl::zipf_distribution is not guaranteed to be stable. +TEST(ZipfDistributionTest, StabilityTest) { + // absl::zipf_distribution stability relies on + // absl::uniform_real_distribution, std::log, std::exp, std::log1p + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector<int> output(10); + + { + absl::zipf_distribution<int32_t> dist; + std::generate(std::begin(output), std::end(output), + [&] { return dist(urbg); }); + EXPECT_THAT(output, ElementsAre(10031, 0, 0, 3, 6, 0, 7, 47, 0, 0)); + } + urbg.reset(); + { + absl::zipf_distribution<int32_t> dist(std::numeric_limits<int32_t>::max(), + 3.3); + std::generate(std::begin(output), std::end(output), + [&] { return dist(urbg); }); + EXPECT_THAT(output, ElementsAre(44, 0, 0, 0, 0, 1, 0, 1, 3, 0)); + } +} + +TEST(ZipfDistributionTest, AlgorithmBounds) { + absl::zipf_distribution<int32_t> dist; + + // Small values from absl::uniform_real_distribution map to larger Zipf + // distribution values. + const std::pair<uint64_t, int32_t> kInputs[] = { + {0xffffffffffffffff, 0x0}, {0x7fffffffffffffff, 0x0}, + {0x3ffffffffffffffb, 0x1}, {0x1ffffffffffffffd, 0x4}, + {0xffffffffffffffe, 0x9}, {0x7ffffffffffffff, 0x12}, + {0x3ffffffffffffff, 0x25}, {0x1ffffffffffffff, 0x4c}, + {0xffffffffffffff, 0x99}, {0x7fffffffffffff, 0x132}, + {0x3fffffffffffff, 0x265}, {0x1fffffffffffff, 0x4cc}, + {0xfffffffffffff, 0x999}, {0x7ffffffffffff, 0x1332}, + {0x3ffffffffffff, 0x2665}, {0x1ffffffffffff, 0x4ccc}, + {0xffffffffffff, 0x9998}, {0x7fffffffffff, 0x1332f}, + {0x3fffffffffff, 0x2665a}, {0x1fffffffffff, 0x4cc9e}, + {0xfffffffffff, 0x998e0}, {0x7ffffffffff, 0x133051}, + {0x3ffffffffff, 0x265ae4}, {0x1ffffffffff, 0x4c9ed3}, + {0xffffffffff, 0x98e223}, {0x7fffffffff, 0x13058c4}, + {0x3fffffffff, 0x25b178e}, {0x1fffffffff, 0x4a062b2}, + {0xfffffffff, 0x8ee23b8}, {0x7ffffffff, 0x10b21642}, + {0x3ffffffff, 0x1d89d89d}, {0x1ffffffff, 0x2fffffff}, + {0xffffffff, 0x45d1745d}, {0x7fffffff, 0x5a5a5a5a}, + {0x3fffffff, 0x69ee5846}, {0x1fffffff, 0x73ecade3}, + {0xfffffff, 0x79a9d260}, {0x7ffffff, 0x7cc0532b}, + {0x3ffffff, 0x7e5ad146}, {0x1ffffff, 0x7f2c0bec}, + {0xffffff, 0x7f95adef}, {0x7fffff, 0x7fcac0da}, + {0x3fffff, 0x7fe55ae2}, {0x1fffff, 0x7ff2ac0e}, + {0xfffff, 0x7ff955ae}, {0x7ffff, 0x7ffcaac1}, + {0x3ffff, 0x7ffe555b}, {0x1ffff, 0x7fff2aac}, + {0xffff, 0x7fff9556}, {0x7fff, 0x7fffcaab}, + {0x3fff, 0x7fffe555}, {0x1fff, 0x7ffff2ab}, + {0xfff, 0x7ffff955}, {0x7ff, 0x7ffffcab}, + {0x3ff, 0x7ffffe55}, {0x1ff, 0x7fffff2b}, + {0xff, 0x7fffff95}, {0x7f, 0x7fffffcb}, + {0x3f, 0x7fffffe5}, {0x1f, 0x7ffffff3}, + {0xf, 0x7ffffff9}, {0x7, 0x7ffffffd}, + {0x3, 0x7ffffffe}, {0x1, 0x7fffffff}, + }; + + for (const auto& instance : kInputs) { + absl::random_internal::sequence_urbg urbg({instance.first}); + EXPECT_EQ(instance.second, dist(urbg)); + } +} + +} // namespace diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 3b85f1b4..20511a35 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -5,21 +5,20 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", - "ABSL_TEST_COPTS", "ABSL_EXCEPTIONS_FLAG", "ABSL_EXCEPTIONS_FLAG_LINKOPTS", + "ABSL_TEST_COPTS", ) package( @@ -405,7 +404,7 @@ cc_test( cc_test( name = "numbers_test", - size = "small", + size = "medium", srcs = [ "internal/numbers_test_common.h", "numbers_test.cc", @@ -558,8 +557,8 @@ cc_library( visibility = ["//visibility:private"], deps = [ ":strings", + "//absl/base:config", "//absl/base:core_headers", - "//absl/container:inlined_vector", "//absl/meta:type_traits", "//absl/numeric:int128", "//absl/types:span", @@ -629,7 +628,7 @@ cc_test( cc_test( name = "str_format_convert_test", - size = "small", + size = "medium", srcs = ["internal/str_format/convert_test.cc"], copts = ABSL_TEST_COPTS, visibility = ["//visibility:private"], diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 5b877ad1..e63eec19 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,468 +14,507 @@ # limitations under the License. # - -list(APPEND STRINGS_PUBLIC_HEADERS - "ascii.h" - "charconv.h" - "escaping.h" - "match.h" - "numbers.h" - "str_cat.h" - "string_view.h" - "strip.h" - "str_join.h" - "str_replace.h" - "str_split.h" - "substitute.h" -) - - -list(APPEND STRINGS_INTERNAL_HEADERS - "internal/char_map.h" - "internal/charconv_bigint.h" - "internal/charconv_parse.h" - "internal/memutil.h" - "internal/ostringstream.h" - "internal/resize_uninitialized.h" - "internal/stl_type_traits.h" - "internal/str_join_internal.h" - "internal/str_split_internal.h" - "internal/utf8.h" -) - - - -# 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" - "internal/ostringstream.cc" - "match.cc" - "numbers.cc" - "str_cat.cc" - "str_replace.cc" - "str_split.cc" - "string_view.cc" - "substitute.cc" - ${STRINGS_PUBLIC_HEADERS} - ${STRINGS_INTERNAL_HEADERS} -) -set(STRINGS_PUBLIC_LIBRARIES absl::base absl_internal_throw_delegate) - -absl_library( - TARGET - absl_strings - SOURCES - ${STRINGS_SRC} - PUBLIC_LIBRARIES - ${STRINGS_PUBLIC_LIBRARIES} - EXPORT_NAME +absl_cc_library( + NAME strings -) - -# add str_format library -absl_header_library( - TARGET - absl_str_format - PUBLIC_LIBRARIES - str_format_internal - EXPORT_NAME - str_format -) - -# str_format_internal -absl_library( - TARGET - str_format_internal - SOURCES - "internal/str_format/arg.cc" - "internal/str_format/bind.cc" - "internal/str_format/extension.cc" - "internal/str_format/float_conversion.cc" - "internal/str_format/output.cc" - "internal/str_format/parser.cc" - "internal/str_format/arg.h" - "internal/str_format/bind.h" - "internal/str_format/checker.h" - "internal/str_format/extension.h" - "internal/str_format/float_conversion.h" - "internal/str_format/output.h" - "internal/str_format/parser.h" - PUBLIC_LIBRARIES - str_format_extension_internal - absl::strings + HDRS + "ascii.h" + "charconv.h" + "escaping.h" + "match.h" + "numbers.h" + "str_cat.h" + "str_join.h" + "str_replace.h" + "str_split.h" + "string_view.h" + "strip.h" + "substitute.h" + 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" + "internal/str_join_internal.h" + "internal/str_split_internal.h" + "match.cc" + "numbers.cc" + "str_cat.cc" + "str_replace.cc" + "str_split.cc" + "string_view.cc" + "substitute.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::strings_internal absl::base - absl::numeric - absl::inlined_vector - absl::span -) - -# str_format_extension_internal -absl_library( - TARGET - str_format_extension_internal - SOURCES - "internal/str_format/extension.cc" - "internal/str_format/extension.h" - "internal/str_format/output.cc" - "internal/str_format/output.h" - PUBLIC_LIBRARIES - absl::base - absl::strings -) - -# pow10_helper -absl_library( - TARGET - pow10_helper - SOURCES - "internal/pow10_helper.cc" - "internal/pow10_helper.h" -) - -# -## TESTS -# - -# test match_test -set(MATCH_TEST_SRC "match_test.cc") -set(MATCH_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET + absl::bits + absl::config + absl::core_headers + absl::endian + absl::throw_delegate + absl::memory + absl::type_traits + absl::int128 + PUBLIC +) + +absl_cc_library( + NAME + strings_internal + HDRS + "internal/char_map.h" + "internal/ostringstream.h" + "internal/resize_uninitialized.h" + "internal/utf8.h" + SRCS + "internal/ostringstream.cc" + "internal/utf8.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + absl::endian + absl::type_traits +) + +absl_cc_test( + NAME match_test - SOURCES - ${MATCH_TEST_SRC} - PUBLIC_LIBRARIES - ${MATCH_TEST_PUBLIC_LIBRARIES} + SRCS + "match_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::base + gmock_main ) - -# test escaping_test -set(ESCAPING_TEST_SRC "escaping_test.cc") -set(ESCAPING_TEST_PUBLIC_LIBRARIES absl::strings absl::base) - -absl_test( - TARGET +absl_cc_test( + NAME escaping_test - SOURCES - ${ESCAPING_TEST_SRC} - PUBLIC_LIBRARIES - ${ESCAPING_TEST_PUBLIC_LIBRARIES} + SRCS + "escaping_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::core_headers + absl::fixed_array + gmock_main ) - -# test ascii_test -set(ASCII_TEST_SRC "ascii_test.cc") -set(ASCII_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET +absl_cc_test( + NAME ascii_test - SOURCES - ${ASCII_TEST_SRC} - PUBLIC_LIBRARIES - ${ASCII_TEST_PUBLIC_LIBRARIES} + SRCS + "ascii_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::core_headers + gmock_main ) - -# test memutil_test -set(MEMUTIL_TEST_SRC "internal/memutil_test.cc") -set(MEMUTIL_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET +absl_cc_test( + NAME memutil_test - SOURCES - ${MEMUTIL_TEST_SRC} - PUBLIC_LIBRARIES - ${MEMUTIL_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/memutil.h" + "internal/memutil_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::core_headers + gmock_main ) - -# test utf8_test -set(UTF8_TEST_SRC "internal/utf8_test.cc") -set(UTF8_TEST_PUBLIC_LIBRARIES absl::strings absl::base) - -absl_test( - TARGET +absl_cc_test( + NAME utf8_test - SOURCES - ${UTF8_TEST_SRC} - PUBLIC_LIBRARIES - ${UTF8_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/utf8_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings_internal + absl::base + absl::core_headers + gmock_main ) - -# test string_view_test -set(STRING_VIEW_TEST_SRC "string_view_test.cc") -set(STRING_VIEW_TEST_PUBLIC_LIBRARIES absl::strings absl_internal_throw_delegate absl::base) - -absl_test( - TARGET +absl_cc_test( + NAME string_view_test - SOURCES - ${STRING_VIEW_TEST_SRC} - PUBLIC_LIBRARIES - ${STRING_VIEW_TEST_PUBLIC_LIBRARIES} + SRCS + "string_view_test.cc" + COPTS + ${ABSL_TEST_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::strings + absl::config + absl::core_headers + absl::dynamic_annotations + gmock_main ) - -# test substitute_test -set(SUBSTITUTE_TEST_SRC "substitute_test.cc") -set(SUBSTITUTE_TEST_PUBLIC_LIBRARIES absl::strings absl::base) - -absl_test( - TARGET +absl_cc_test( + NAME substitute_test - SOURCES - ${SUBSTITUTE_TEST_SRC} - PUBLIC_LIBRARIES - ${SUBSTITUTE_TEST_PUBLIC_LIBRARIES} + SRCS + "substitute_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::core_headers + gmock_main ) - -# test str_replace_test -set(STR_REPLACE_TEST_SRC "str_replace_test.cc") -set(STR_REPLACE_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_internal_throw_delegate) - -absl_test( - TARGET +absl_cc_test( + NAME str_replace_test - SOURCES - ${STR_REPLACE_TEST_SRC} - PUBLIC_LIBRARIES - ${STR_REPLACE_TEST_PUBLIC_LIBRARIES} + SRCS + "str_replace_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + gmock_main ) - -# test str_split_test -set(STR_SPLIT_TEST_SRC "str_split_test.cc") -set(STR_SPLIT_TEST_PUBLIC_LIBRARIES absl::strings absl::base absl_internal_throw_delegate) - -absl_test( - TARGET +absl_cc_test( + NAME str_split_test - SOURCES - ${STR_SPLIT_TEST_SRC} - PUBLIC_LIBRARIES - ${STR_SPLIT_TEST_PUBLIC_LIBRARIES} + SRCS + "str_split_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::base + absl::core_headers + absl::dynamic_annotations + gmock_main ) - -# test ostringstream_test -set(OSTRINGSTREAM_TEST_SRC "internal/ostringstream_test.cc") -set(OSTRINGSTREAM_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET +absl_cc_test( + NAME ostringstream_test - SOURCES - ${OSTRINGSTREAM_TEST_SRC} - PUBLIC_LIBRARIES - ${OSTRINGSTREAM_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/ostringstream_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings_internal + gmock_main ) - -# test resize_uninitialized_test -set(RESIZE_UNINITIALIZED_TEST_SRC "internal/resize_uninitialized_test.cc") - -absl_test( - TARGET +absl_cc_test( + NAME resize_uninitialized_test - SOURCES - ${RESIZE_UNINITIALIZED_TEST_SRC} + SRCS + "internal/resize_uninitialized.h" + "internal/resize_uninitialized_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::core_headers + absl::type_traits + gmock_main ) - -# test str_join_test -set(STR_JOIN_TEST_SRC "str_join_test.cc") -set(STR_JOIN_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET +absl_cc_test( + NAME str_join_test - SOURCES - ${STR_JOIN_TEST_SRC} - PUBLIC_LIBRARIES - ${STR_JOIN_TEST_PUBLIC_LIBRARIES} + SRCS + "str_join_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::base + absl::core_headers + absl::memory + gmock_main ) - -# test str_cat_test -set(STR_CAT_TEST_SRC "str_cat_test.cc") -set(STR_CAT_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET +absl_cc_test( + NAME str_cat_test - SOURCES - ${STR_CAT_TEST_SRC} - PUBLIC_LIBRARIES - ${STR_CAT_TEST_PUBLIC_LIBRARIES} + SRCS + "str_cat_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::core_headers + gmock_main ) - -# test numbers_test -set(NUMBERS_TEST_SRC "numbers_test.cc") -set(NUMBERS_TEST_PUBLIC_LIBRARIES absl::strings pow10_helper) - -absl_test( - TARGET +absl_cc_test( + NAME numbers_test - SOURCES - ${NUMBERS_TEST_SRC} - PUBLIC_LIBRARIES - ${NUMBERS_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/numbers_test_common.h" + "numbers_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::base + absl::core_headers + absl::pow10_helper + gmock_main ) - -# test strip_test -set(STRIP_TEST_SRC "strip_test.cc") -set(STRIP_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET +absl_cc_test( + NAME strip_test - SOURCES - ${STRIP_TEST_SRC} - PUBLIC_LIBRARIES - ${STRIP_TEST_PUBLIC_LIBRARIES} + SRCS + "strip_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::base + gmock_main ) - -# test char_map_test -set(CHAR_MAP_TEST_SRC "internal/char_map_test.cc") -set(CHAR_MAP_TEST_PUBLIC_LIBRARIES absl::strings) - -absl_test( - TARGET +absl_cc_test( + NAME char_map_test - SOURCES - ${CHAR_MAP_TEST_SRC} - PUBLIC_LIBRARIES - ${CHAR_MAP_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/char_map_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings_internal + gmock_main ) - -# test charconv_test -set(CHARCONV_TEST_SRC "charconv_test.cc") -set(CHARCONV_TEST_PUBLIC_LIBRARIES absl::strings absl::str_format pow10_helper) - -absl_test( - TARGET +absl_cc_test( + NAME charconv_test - SOURCES - ${CHARCONV_TEST_SRC} - PUBLIC_LIBRARIES - ${CHARCONV_TEST_PUBLIC_LIBRARIES} + SRCS + "charconv_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::str_format + absl::base + absl::pow10_helper + gmock_main ) - -# 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 +absl_cc_test( + NAME charconv_parse_test - SOURCES - ${CHARCONV_PARSE_TEST_SRC} - PUBLIC_LIBRARIES - ${CHARCONV_PARSE_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/charconv_parse.h" + "internal/charconv_parse_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::strings + absl::base + gmock_main ) +absl_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 + absl::strings + absl::base + gmock_main +) -# test charconv_bigint_test -set(CHARCONV_BIGINT_TEST_SRC "internal/charconv_bigint_test.cc") -set(CHARCONV_BIGINT_TEST_PUBLIC_LIBRARIES absl::strings) +absl_cc_library( + NAME + str_format + HDRS + "str_format.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::str_format_internal + PUBLIC +) -absl_test( - TARGET - charconv_bigint_test - SOURCES - ${CHARCONV_BIGINT_TEST_SRC} - PUBLIC_LIBRARIES - ${CHARCONV_BIGINT_TEST_PUBLIC_LIBRARIES} -) -# test str_format_test -absl_test( - TARGET +absl_cc_library( + NAME + str_format_internal + HDRS + "internal/str_format/arg.h" + "internal/str_format/bind.h" + "internal/str_format/checker.h" + "internal/str_format/extension.h" + "internal/str_format/float_conversion.h" + "internal/str_format/output.h" + "internal/str_format/parser.h" + SRCS + "internal/str_format/arg.cc" + "internal/str_format/bind.cc" + "internal/str_format/extension.cc" + "internal/str_format/float_conversion.cc" + "internal/str_format/output.cc" + "internal/str_format/parser.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::strings + absl::config + absl::core_headers + absl::type_traits + absl::int128 + absl::span +) + +absl_cc_test( + NAME str_format_test - SOURCES + SRCS "str_format_test.cc" - PUBLIC_LIBRARIES - absl::base + COPTS + ${ABSL_TEST_COPTS} + DEPS absl::str_format absl::strings + absl::core_headers + gmock_main +) + +absl_cc_test( + NAME + str_format_extension_test + SRCS + "internal/str_format/extension_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::str_format + absl::str_format_internal + gmock_main +) + +absl_cc_test( + NAME + str_format_arg_test + SRCS + "internal/str_format/arg_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::str_format + absl::str_format_internal + gmock_main ) -# test str_format_bind_test -absl_test( - TARGET +absl_cc_test( + NAME str_format_bind_test - SOURCES + SRCS "internal/str_format/bind_test.cc" - PUBLIC_LIBRARIES - str_format_internal + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::str_format_internal + gmock_main ) -# test str_format_checker_test -absl_test( - TARGET +absl_cc_test( + NAME str_format_checker_test - SOURCES + SRCS "internal/str_format/checker_test.cc" - PUBLIC_LIBRARIES + COPTS + ${ABSL_TEST_COPTS} + DEPS absl::str_format + gmock_main ) -# test str_format_convert_test -absl_test( - TARGET +absl_cc_test( + NAME str_format_convert_test - SOURCES + SRCS "internal/str_format/convert_test.cc" - PUBLIC_LIBRARIES - str_format_internal - absl::numeric + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::str_format_internal + absl::int128 + gmock_main ) -# test str_format_output_test -absl_test( - TARGET +absl_cc_test( + NAME str_format_output_test - SOURCES + SRCS "internal/str_format/output_test.cc" - PUBLIC_LIBRARIES - str_format_extension_internal + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::str_format_internal + gmock_main ) -# test str_format_parser_test -absl_test( - TARGET +absl_cc_test( + NAME str_format_parser_test - SOURCES + SRCS "internal/str_format/parser_test.cc" - PUBLIC_LIBRARIES - str_format_internal - absl::base + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::str_format_internal + absl::core_headers + gmock_main +) + +absl_cc_library( + NAME + pow10_helper + HDRS + "internal/pow10_helper.h" + SRCS + "internal/pow10_helper.cc" + COPTS + ${ABSL_TEST_COPTS} + TESTONLY ) -# test pow10_helper_test -absl_test( - TARGET +absl_cc_test( + NAME pow10_helper_test - SOURCES + SRCS "internal/pow10_helper_test.cc" - PUBLIC_LIBRARIES - pow10_helper + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::pow10_helper absl::str_format + gmock_main ) diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc index 5d08e816..045a5e21 100644 --- a/absl/strings/ascii.cc +++ b/absl/strings/ascii.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,7 @@ #include "absl/strings/ascii.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace ascii_internal { // # Table generated by this Python code (bit 0x02 is currently unused): @@ -196,5 +196,5 @@ void RemoveExtraAsciiWhitespace(std::string* str) { str->erase(output_it - &(*str)[0]); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h index 98418fd2..ebcbb11a 100644 --- a/absl/strings/ascii.h +++ b/absl/strings/ascii.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -59,7 +59,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace ascii_internal { // Declaration for an array of bitfields holding character information. @@ -235,7 +235,7 @@ inline void StripAsciiWhitespace(std::string* str) { // Removes leading, trailing, and consecutive internal whitespace. void RemoveExtraAsciiWhitespace(std::string*); -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_ASCII_H_ diff --git a/absl/strings/ascii_benchmark.cc b/absl/strings/ascii_benchmark.cc index 8dea4b8c..aca458c8 100644 --- a/absl/strings/ascii_benchmark.cc +++ b/absl/strings/ascii_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/ascii_test.cc b/absl/strings/ascii_test.cc index 9903b049..5ecd23f8 100644 --- a/absl/strings/ascii_test.cc +++ b/absl/strings/ascii_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc index 21ea17b1..866a163e 100644 --- a/absl/strings/charconv.cc +++ b/absl/strings/charconv.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -57,7 +57,7 @@ // narrower mantissas. namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { template <typename FloatType> @@ -552,9 +552,10 @@ CalculatedFloat CalculateFromParsedDecimal( 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. + // `mantissa_width` constants below are justified in + // https://abseil.io/about/design/charconv. 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) { @@ -980,5 +981,5 @@ const int16_t kPower10ExponentTable[] = { }; } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h index 160306e6..0b84ccac 100644 --- a/absl/strings/charconv.h +++ b/absl/strings/charconv.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include <system_error> // NOLINT(build/c++11) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Workalike compatibilty version of std::chars_format from C++17. // @@ -50,9 +50,9 @@ struct from_chars_result { // 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, +// DR 3080 and DR 3081. 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 +// likely to change is for DR 3081, 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.) @@ -111,7 +111,7 @@ inline chars_format& operator^=(chars_format& lhs, chars_format rhs) { return lhs; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_CHARCONV_H_ diff --git a/absl/strings/charconv_benchmark.cc b/absl/strings/charconv_benchmark.cc index fd83f44e..644b2abd 100644 --- a/absl/strings/charconv_benchmark.cc +++ b/absl/strings/charconv_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc index d07537eb..b58fad26 100644 --- a/absl/strings/charconv_test.cc +++ b/absl/strings/charconv_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -279,7 +279,8 @@ void TestHalfwayValue(const std::string& mantissa, int exponent, 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); + 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); diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc index 69053c19..eb0974dc 100644 --- a/absl/strings/escaping.cc +++ b/absl/strings/escaping.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,7 +33,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { // Digit conversion. @@ -180,7 +180,8 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped, ch = (ch << 4) + hex_digit_to_int(*++p); if (ch > 0xFF) { if (error) { - *error = "Value of \\" + std::string(hex_start, p + 1 - hex_start) + + *error = "Value of \\" + + std::string(hex_start, p + 1 - hex_start) + " exceeds 0xff"; } return false; @@ -295,7 +296,7 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped, // ---------------------------------------------------------------------- // CUnescapeInternal() // -// Same as above but uses a C++ string for output. 'source' and 'dest' +// Same as above but uses a std::string for output. 'source' and 'dest' // may be the same. // ---------------------------------------------------------------------- bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped, @@ -325,7 +326,8 @@ bool CUnescapeInternal(absl::string_view source, bool leave_nulls_escaped, // // Escaped chars: \n, \r, \t, ", ', \, and !absl::ascii_isprint(). // ---------------------------------------------------------------------- -std::string CEscapeInternal(absl::string_view src, bool use_hex, bool utf8_safe) { +std::string CEscapeInternal(absl::string_view src, bool use_hex, + bool utf8_safe) { std::string dest; bool last_hex_escape = false; // true if last output char was \xNN. @@ -787,7 +789,7 @@ size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) { // Base64 encodes three bytes of input at a time. If the input is not // divisible by three, we pad as appropriate. // - // (from http://tools.ietf.org/html/rfc3548) + // (from https://tools.ietf.org/html/rfc3548) // Special processing is performed if fewer than 24 bits are available // at the end of the data being encoded. A full encoding quantum is // always completed at the end of a quantity. When fewer than 24 input @@ -801,12 +803,12 @@ size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) { size_t len = (input_len / 3) * 4; if (input_len % 3 == 0) { - // (from http://tools.ietf.org/html/rfc3548) + // (from https://tools.ietf.org/html/rfc3548) // (1) the final quantum of encoding input is an integral multiple of 24 // bits; here, the final unit of encoded output will be an integral // multiple of 4 characters with no "=" padding, } else if (input_len % 3 == 1) { - // (from http://tools.ietf.org/html/rfc3548) + // (from https://tools.ietf.org/html/rfc3548) // (2) the final quantum of encoding input is exactly 8 bits; here, the // final unit of encoded output will be two characters followed by two // "=" padding characters, or @@ -815,7 +817,7 @@ size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) { len += 2; } } else { // (input_len % 3 == 2) - // (from http://tools.ietf.org/html/rfc3548) + // (from https://tools.ietf.org/html/rfc3548) // (3) the final quantum of encoding input is exactly 16 bits; here, the // final unit of encoded output will be three characters followed by one // "=" padding character. @@ -844,8 +846,8 @@ size_t Base64EscapeInternal(const unsigned char* src, size_t szsrc, char* dest, // Three bytes of data encodes to four characters of cyphertext. // So we can pump through three-byte chunks atomically. - if (szsrc >= 3) { // "limit_src - 3" is UB if szsrc < 3 - while (cur_src < limit_src - 3) { // as long as we have >= 32 bits + if (szsrc >= 3) { // "limit_src - 3" is UB if szsrc < 3. + while (cur_src < limit_src - 3) { // While we have >= 32 bits. uint32_t in = absl::big_endian::Load32(cur_src) >> 8; cur_dest[0] = base64[in >> 18]; @@ -1012,7 +1014,8 @@ void HexStringToBytesInternal(const char* from, T to, ptrdiff_t num) { } } -// This is a templated function so that T can be either a char* or a string. +// This is a templated function so that T can be either a char* or a +// std::string. template <typename T> void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) { auto dest_ptr = &dest[0]; @@ -1029,7 +1032,8 @@ void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) { // // See CUnescapeInternal() for implementation details. // ---------------------------------------------------------------------- -bool CUnescape(absl::string_view source, std::string* dest, std::string* error) { +bool CUnescape(absl::string_view source, std::string* dest, + std::string* error) { return CUnescapeInternal(source, kUnescapeNulls, dest, error); } @@ -1052,10 +1056,10 @@ std::string Utf8SafeCHexEscape(absl::string_view src) { } // ---------------------------------------------------------------------- -// ptrdiff_t Base64Unescape() - base64 decoder -// ptrdiff_t Base64Escape() - base64 encoder -// ptrdiff_t WebSafeBase64Unescape() - Google's variation of base64 decoder -// ptrdiff_t WebSafeBase64Escape() - Google's variation of base64 encoder +// Base64Unescape() - base64 decoder +// Base64Escape() - base64 encoder +// WebSafeBase64Unescape() - Google's variation of base64 decoder +// WebSafeBase64Escape() - Google's variation of base64 encoder // // Check out // http://tools.ietf.org/html/rfc2045 for formal description, but what we @@ -1093,6 +1097,20 @@ void WebSafeBase64Escape(absl::string_view src, std::string* dest) { src.size(), dest, false, kWebSafeBase64Chars); } +std::string Base64Escape(absl::string_view src) { + std::string dest; + Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()), + src.size(), &dest, true, kBase64Chars); + return dest; +} + +std::string WebSafeBase64Escape(absl::string_view src) { + std::string dest; + Base64EscapeInternal(reinterpret_cast<const unsigned char*>(src.data()), + src.size(), &dest, false, kWebSafeBase64Chars); + return dest; +} + std::string HexStringToBytes(absl::string_view from) { std::string result; const auto num = from.size() / 2; @@ -1109,5 +1127,5 @@ std::string BytesToHexString(absl::string_view from) { return result; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/escaping.h b/absl/strings/escaping.h index a31fb374..7f1c5d46 100644 --- a/absl/strings/escaping.h +++ b/absl/strings/escaping.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,7 +19,6 @@ // // This header file contains string utilities involved in escaping and // unescaping strings in various ways. -// #ifndef ABSL_STRINGS_ESCAPING_H_ #define ABSL_STRINGS_ESCAPING_H_ @@ -34,12 +33,12 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // CUnescape() // // Unescapes a `source` string and copies it into `dest`, rewriting C-style -// escape sequences (http://en.cppreference.com/w/cpp/language/escape) into +// escape sequences (https://en.cppreference.com/w/cpp/language/escape) into // their proper code point equivalents, returning `true` if successful. // // The following unescape sequences can be handled: @@ -57,7 +56,6 @@ inline namespace lts_2018_12_18 { // UTF-8. (E.g., `\u2019` unescapes to the three bytes 0xE2, 0x80, and // 0x99). // -// // If any errors are encountered, this function returns `false`, leaving the // `dest` output parameter in an unspecified state, and stores the first // encountered error in `error`. To disable error reporting, set `error` to @@ -81,7 +79,7 @@ inline bool CUnescape(absl::string_view source, std::string* dest) { // CEscape() // // Escapes a 'src' string using C-style escapes sequences -// (http://en.cppreference.com/w/cpp/language/escape), escaping other +// (https://en.cppreference.com/w/cpp/language/escape), escaping other // non-printable/non-whitespace bytes as octal sequences (e.g. "\377"). // // Example: @@ -135,16 +133,18 @@ bool WebSafeBase64Unescape(absl::string_view src, std::string* dest); // Base64Escape() // -// Encodes a `src` string into a `dest` buffer using base64 encoding, with -// padding characters. This function conforms with RFC 4648 section 4 (base64). +// Encodes a `src` string into a base64-encoded string, with padding characters. +// This function conforms with RFC 4648 section 4 (base64). void Base64Escape(absl::string_view src, std::string* dest); +std::string Base64Escape(absl::string_view src); // WebSafeBase64Escape() // -// Encodes a `src` string into a `dest` buffer using '-' instead of '+' and -// '_' instead of '/', and without padding. This function conforms with RFC 4648 -// section 5 (base64url). +// Encodes a `src` string into a base64-like string, using '-' instead of '+' +// and '_' instead of '/', and without padding. This function conforms with RFC +// 4648 section 5 (base64url). void WebSafeBase64Escape(absl::string_view src, std::string* dest); +std::string WebSafeBase64Escape(absl::string_view src); // HexStringToBytes() // @@ -158,7 +158,7 @@ std::string HexStringToBytes(absl::string_view from); // `2*from.size()`. std::string BytesToHexString(absl::string_view from); -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_ESCAPING_H_ diff --git a/absl/strings/escaping_benchmark.cc b/absl/strings/escaping_benchmark.cc index 0f791f4e..10d5b033 100644 --- a/absl/strings/escaping_benchmark.cc +++ b/absl/strings/escaping_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/escaping_test.cc b/absl/strings/escaping_test.cc index 9dc27f3f..1967975b 100644 --- a/absl/strings/escaping_test.cc +++ b/absl/strings/escaping_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -36,18 +36,19 @@ struct epair { TEST(CEscape, EscapeAndUnescape) { const std::string inputs[] = { - std::string("foo\nxx\r\b\0023"), - std::string(""), - std::string("abc"), - std::string("\1chad_rules"), - std::string("\1arnar_drools"), - std::string("xxxx\r\t'\"\\"), - std::string("\0xx\0", 4), - std::string("\x01\x31"), - std::string("abc\xb\x42\141bc"), - std::string("123\1\x31\x32\x33"), - std::string("\xc1\xca\x1b\x62\x19o\xcc\x04"), - std::string("\\\"\xe8\xb0\xb7\xe6\xad\x8c\\\" is Google\\\'s Chinese name"), + std::string("foo\nxx\r\b\0023"), + std::string(""), + std::string("abc"), + std::string("\1chad_rules"), + std::string("\1arnar_drools"), + std::string("xxxx\r\t'\"\\"), + std::string("\0xx\0", 4), + std::string("\x01\x31"), + std::string("abc\xb\x42\141bc"), + std::string("123\1\x31\x32\x33"), + std::string("\xc1\xca\x1b\x62\x19o\xcc\x04"), + std::string( + "\\\"\xe8\xb0\xb7\xe6\xad\x8c\\\" is Google\\\'s Chinese name"), }; // Do this twice, once for octal escapes and once for hex escapes. for (int kind = 0; kind < 4; kind++) { @@ -71,6 +72,11 @@ TEST(CEscape, EscapeAndUnescape) { EXPECT_TRUE(absl::CUnescape(escaped, &unescaped_str)); EXPECT_EQ(unescaped_str, original); + unescaped_str.erase(); + std::string error; + EXPECT_TRUE(absl::CUnescape(escaped, &unescaped_str, &error)); + EXPECT_EQ(error, ""); + // Check in-place unescaping std::string s = escaped; EXPECT_TRUE(absl::CUnescape(s, &s)); @@ -149,7 +155,8 @@ TEST(CEscape, BasicEscaping) { TEST(Unescape, BasicFunction) { epair tests[] = - {{"\\u0030", "0"}, + {{"", ""}, + {"\\u0030", "0"}, {"\\u00A3", "\xC2\xA3"}, {"\\u22FD", "\xE2\x8B\xBD"}, {"\\U00010000", "\xF0\x90\x80\x80"}, @@ -159,20 +166,22 @@ TEST(Unescape, BasicFunction) { EXPECT_TRUE(absl::CUnescape(val.escaped, &out)); EXPECT_EQ(out, val.unescaped); } - std::string bad[] = - {"\\u1", // too short - "\\U1", // too short - "\\Uffffff", // exceeds 0x10ffff (largest Unicode) - "\\U00110000", // exceeds 0x10ffff (largest Unicode) - "\\uD835", // surrogate character (D800-DFFF) - "\\U0000DD04", // surrogate character (D800-DFFF) - "\\777", // exceeds 0xff - "\\xABCD"}; // exceeds 0xff + std::string bad[] = {"\\u1", // too short + "\\U1", // too short + "\\Uffffff", // exceeds 0x10ffff (largest Unicode) + "\\U00110000", // exceeds 0x10ffff (largest Unicode) + "\\uD835", // surrogate character (D800-DFFF) + "\\U0000DD04", // surrogate character (D800-DFFF) + "\\777", // exceeds 0xff + "\\xABCD"}; // exceeds 0xff for (const std::string& e : bad) { std::string error; std::string out; EXPECT_FALSE(absl::CUnescape(e, &out, &error)); EXPECT_FALSE(error.empty()); + + out.erase(); + EXPECT_FALSE(absl::CUnescape(e, &out)); } } @@ -258,9 +267,11 @@ TEST_F(CUnescapeTest, UnescapesMultipleOctalNulls) { // All escapes, including newlines and null escapes, should have been // converted to the equivalent characters. EXPECT_EQ(std::string("\0\n" - "0\n" - "\0\n" - "\0", 7), result_string_); + "0\n" + "\0\n" + "\0", + 7), + result_string_); } @@ -268,17 +279,21 @@ TEST_F(CUnescapeTest, UnescapesMultipleHexNulls) { std::string original_string(kStringWithMultipleHexNulls); EXPECT_TRUE(absl::CUnescape(original_string, &result_string_)); EXPECT_EQ(std::string("\0\n" - "0\n" - "\0\n" - "\0", 7), result_string_); + "0\n" + "\0\n" + "\0", + 7), + result_string_); } TEST_F(CUnescapeTest, UnescapesMultipleUnicodeNulls) { std::string original_string(kStringWithMultipleUnicodeNulls); EXPECT_TRUE(absl::CUnescape(original_string, &result_string_)); EXPECT_EQ(std::string("\0\n" - "0\n" - "\0", 5), result_string_); + "0\n" + "\0", + 5), + result_string_); } static struct { @@ -550,6 +565,7 @@ void TestEscapeAndUnescape() { StringType encoded("this junk should be ignored"); absl::Base64Escape(tc.plaintext, &encoded); EXPECT_EQ(encoded, tc.cyphertext); + EXPECT_EQ(absl::Base64Escape(tc.plaintext), tc.cyphertext); StringType decoded("this junk should be ignored"); EXPECT_TRUE(absl::Base64Unescape(encoded, &decoded)); @@ -568,6 +584,7 @@ void TestEscapeAndUnescape() { encoded = "this junk should be ignored"; absl::WebSafeBase64Escape(tc.plaintext, &encoded); EXPECT_EQ(encoded, websafe); + EXPECT_EQ(absl::WebSafeBase64Escape(tc.plaintext), websafe); // Let's try the std::string version of the decoder decoded = "this junk should be ignored"; @@ -580,6 +597,7 @@ void TestEscapeAndUnescape() { StringType buffer; absl::WebSafeBase64Escape(tc.plaintext, &buffer); EXPECT_EQ(tc.cyphertext, buffer); + EXPECT_EQ(absl::WebSafeBase64Escape(tc.plaintext), tc.cyphertext); } // Verify the behavior when decoding bad data diff --git a/absl/strings/internal/char_map.h b/absl/strings/internal/char_map.h index 10b7d007..772ae869 100644 --- a/absl/strings/internal/char_map.h +++ b/absl/strings/internal/char_map.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { class Charmap { @@ -150,7 +150,7 @@ constexpr Charmap GraphCharmap() { return PrintCharmap() & ~SpaceCharmap(); } constexpr Charmap PunctCharmap() { return GraphCharmap() & ~AlnumCharmap(); } } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_CHAR_MAP_H_ diff --git a/absl/strings/internal/char_map_benchmark.cc b/absl/strings/internal/char_map_benchmark.cc index c45f3157..5cef967b 100644 --- a/absl/strings/internal/char_map_benchmark.cc +++ b/absl/strings/internal/char_map_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/internal/char_map_test.cc b/absl/strings/internal/char_map_test.cc index c3601e10..d3306241 100644 --- a/absl/strings/internal/char_map_test.cc +++ b/absl/strings/internal/char_map_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/internal/charconv_bigint.cc b/absl/strings/internal/charconv_bigint.cc index dac907e2..58c909f4 100644 --- a/absl/strings/internal/charconv_bigint.cc +++ b/absl/strings/internal/charconv_bigint.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,7 +19,7 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { namespace { @@ -355,5 +355,5 @@ template class BigUnsigned<4>; template class BigUnsigned<84>; } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/charconv_bigint.h b/absl/strings/internal/charconv_bigint.h index ffafc11c..5aef416b 100644 --- a/absl/strings/internal/charconv_bigint.h +++ b/absl/strings/internal/charconv_bigint.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { // The largest power that 5 that can be raised to, and still fit in a uint32_t. @@ -104,12 +104,12 @@ class BigUnsigned { SetToZero(); return; } - size_ = std::min(size_ + word_shift, max_words); + 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) { + 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)); } @@ -268,7 +268,7 @@ class BigUnsigned { 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); + (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); } @@ -287,7 +287,7 @@ class BigUnsigned { value = 0; } } - size_ = std::min(max_words, std::max(index + 1, size_)); + size_ = (std::min)(max_words, (std::max)(index + 1, size_)); } } @@ -310,7 +310,7 @@ class BigUnsigned { } 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_)); + size_ = (std::min)(max_words, (std::max)(index + 1, size_)); } } } @@ -349,7 +349,7 @@ class BigUnsigned { // Returns -1 if lhs < rhs, 0 if lhs == rhs, and 1 if lhs > rhs. template <int N, int M> int Compare(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) { - int limit = std::max(lhs.size(), rhs.size()); + 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); @@ -364,7 +364,7 @@ int Compare(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) { template <int N, int M> bool operator==(const BigUnsigned<N>& lhs, const BigUnsigned<M>& rhs) { - int limit = std::max(lhs.size(), rhs.size()); + int limit = (std::max)(lhs.size(), rhs.size()); for (int i = 0; i < limit; ++i) { if (lhs.GetWord(i) != rhs.GetWord(i)) { return false; @@ -415,7 +415,7 @@ extern template class BigUnsigned<4>; extern template class BigUnsigned<84>; } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_CHARCONV_BIGINT_H_ diff --git a/absl/strings/internal/charconv_bigint_test.cc b/absl/strings/internal/charconv_bigint_test.cc index dbab3208..590511d0 100644 --- a/absl/strings/internal/charconv_bigint_test.cc +++ b/absl/strings/internal/charconv_bigint_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,7 +19,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { TEST(BigUnsigned, ShiftLeft) { @@ -201,5 +201,5 @@ TEST(BigUnsigned, TenToTheNth) { } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc index 68d65a8a..4dd4ecb3 100644 --- a/absl/strings/internal/charconv_parse.cc +++ b/absl/strings/internal/charconv_parse.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/strings/internal/memutil.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { // ParseFloat<10> will read the first 19 significant digits of the mantissa. @@ -494,5 +494,5 @@ template ParsedFloat ParseFloat<16>(const char* begin, const char* end, chars_format format_flags); } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/charconv_parse.h b/absl/strings/internal/charconv_parse.h index 17d5a8f8..ddfc87f8 100644 --- a/absl/strings/internal/charconv_parse.h +++ b/absl/strings/internal/charconv_parse.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "absl/strings/charconv.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { // Enum indicating whether a parsed float is a number or special value. @@ -93,6 +93,6 @@ extern template ParsedFloat ParseFloat<16>(const char* begin, const char* end, absl::chars_format format_flags); } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 index f48b9aee..9511c987 100644 --- a/absl/strings/internal/charconv_parse_test.cc +++ b/absl/strings/internal/charconv_parse_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/internal/escaping_test_common.h b/absl/strings/internal/escaping_test_common.h index 50ef595f..ecd3aa35 100644 --- a/absl/strings/internal/escaping_test_common.h +++ b/absl/strings/internal/escaping_test_common.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { struct base64_testcase { @@ -127,7 +127,7 @@ inline const std::array<base64_testcase, 5>& base64_strings() { } } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 1d6cfa36..05251377 100644 --- a/absl/strings/internal/memutil.cc +++ b/absl/strings/internal/memutil.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include <cstdlib> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { int memcasecmp(const char* s1, const char* s2, size_t len) { @@ -108,5 +108,5 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, } } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/memutil.h b/absl/strings/internal/memutil.h index dcc5c9a3..4efac989 100644 --- a/absl/strings/internal/memutil.h +++ b/absl/strings/internal/memutil.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -69,7 +69,7 @@ #include "absl/strings/ascii.h" // for absl::ascii_tolower namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { inline char* memcat(char* dest, size_t destlen, const char* src, @@ -142,7 +142,7 @@ const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, size_t neelen); } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_MEMUTIL_H_ diff --git a/absl/strings/internal/memutil_benchmark.cc b/absl/strings/internal/memutil_benchmark.cc index 77915adb..dc95c3e5 100644 --- a/absl/strings/internal/memutil_benchmark.cc +++ b/absl/strings/internal/memutil_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/internal/memutil_test.cc b/absl/strings/internal/memutil_test.cc index 09424de9..d8681ddf 100644 --- a/absl/strings/internal/memutil_test.cc +++ b/absl/strings/internal/memutil_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/internal/numbers_test_common.h b/absl/strings/internal/numbers_test_common.h index 32aa0bfa..3f6965f2 100644 --- a/absl/strings/internal/numbers_test_common.h +++ b/absl/strings/internal/numbers_test_common.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { template <typename IntType> @@ -175,7 +175,7 @@ inline const std::array<uint64_test_case, 34>& strtouint64_test_cases() { } } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 77f4b0b3..ce2dd6c7 100644 --- a/absl/strings/internal/ostringstream.cc +++ b/absl/strings/internal/ostringstream.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,7 @@ #include "absl/strings/internal/ostringstream.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { OStringStream::Buf::int_type OStringStream::overflow(int c) { @@ -32,5 +32,5 @@ std::streamsize OStringStream::xsputn(const char* s, std::streamsize n) { } } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/ostringstream.h b/absl/strings/internal/ostringstream.h index 908e170c..2cf65133 100644 --- a/absl/strings/internal/ostringstream.h +++ b/absl/strings/internal/ostringstream.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,21 +23,21 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { -// The same as std::ostringstream but appends to a user-specified string, +// The same as std::ostringstream but appends to a user-specified std::string, // and is faster. It is ~70% faster to create, ~50% faster to write to, and -// completely free to extract the result string. +// completely free to extract the result std::string. // -// string s; +// std::string s; // OStringStream strm(&s); // strm << 42 << ' ' << 3.14; // appends to `s` // // The stream object doesn't have to be named. Starting from C++11 operator<< // works with rvalues of std::ostream. // -// string s; +// std::string s; // OStringStream(&s) << 42 << ' ' << 3.14; // appends to `s` // // OStringStream is faster to create than std::ostringstream but it's still @@ -46,14 +46,14 @@ namespace strings_internal { // // Creates unnecessary instances of OStringStream: slow. // -// string s; +// std::string s; // OStringStream(&s) << 42; // OStringStream(&s) << ' '; // OStringStream(&s) << 3.14; // // Creates a single instance of OStringStream and reuses it: fast. // -// string s; +// std::string s; // OStringStream strm(&s); // strm << 42; // strm << ' '; @@ -65,8 +65,8 @@ class OStringStream : private std::basic_streambuf<char>, public std::ostream { // The argument can be null, in which case you'll need to call str(p) with a // non-null argument before you can write to the stream. // - // The destructor of OStringStream doesn't use the std::string. It's OK to destroy - // the std::string before the stream. + // The destructor of OStringStream doesn't use the std::string. It's OK to + // destroy the std::string before the stream. explicit OStringStream(std::string* s) : std::ostream(this), s_(s) {} std::string* str() { return s_; } @@ -83,7 +83,7 @@ class OStringStream : private std::basic_streambuf<char>, public std::ostream { }; } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_OSTRINGSTREAM_H_ diff --git a/absl/strings/internal/ostringstream_benchmark.cc b/absl/strings/internal/ostringstream_benchmark.cc index c93f9690..5979f182 100644 --- a/absl/strings/internal/ostringstream_benchmark.cc +++ b/absl/strings/internal/ostringstream_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/internal/ostringstream_test.cc b/absl/strings/internal/ostringstream_test.cc index 069a0e1f..2879e50e 100644 --- a/absl/strings/internal/ostringstream_test.cc +++ b/absl/strings/internal/ostringstream_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/internal/pow10_helper.cc b/absl/strings/internal/pow10_helper.cc index c7f4875a..5c02ab8f 100644 --- a/absl/strings/internal/pow10_helper.cc +++ b/absl/strings/internal/pow10_helper.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include <cmath> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { namespace { @@ -118,5 +118,5 @@ double Pow10(int exp) { } } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/pow10_helper.h b/absl/strings/internal/pow10_helper.h index 750051bd..c9a1b27f 100644 --- a/absl/strings/internal/pow10_helper.h +++ b/absl/strings/internal/pow10_helper.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,13 +17,13 @@ // precise values are computed across the full range of doubles. We can't rely // on the pow() function, because not all standard libraries ship a version // that is precise. -#ifndef ABSL_STRINGS_POW10_HELPER_H_ -#define ABSL_STRINGS_POW10_HELPER_H_ +#ifndef ABSL_STRINGS_INTERNAL_POW10_HELPER_H_ +#define ABSL_STRINGS_INTERNAL_POW10_HELPER_H_ #include <vector> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { // Computes the precise value of 10^exp. (I.e. the nearest representable @@ -32,7 +32,7 @@ namespace strings_internal { double Pow10(int exp); } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl -#endif // ABSL_STRINGS_POW10_HELPER_H_ +#endif // ABSL_STRINGS_INTERNAL_POW10_HELPER_H_ diff --git a/absl/strings/internal/pow10_helper_test.cc b/absl/strings/internal/pow10_helper_test.cc index 371fe122..4a62a70d 100644 --- a/absl/strings/internal/pow10_helper_test.cc +++ b/absl/strings/internal/pow10_helper_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "absl/strings/str_format.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { namespace { @@ -118,5 +118,5 @@ TEST(Pow10HelperTest, Works) { } // namespace } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h index 2951bf84..ab1d8684 100644 --- a/absl/strings/internal/resize_uninitialized.h +++ b/absl/strings/internal/resize_uninitialized.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,54 +18,57 @@ #define ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ #include <string> +#include <type_traits> #include <utility> #include "absl/base/port.h" #include "absl/meta/type_traits.h" // for void_t namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { // Is a subclass of true_type or false_type, depending on whether or not -// T has a resize_uninitialized member. -template <typename T, typename = void> -struct HasResizeUninitialized : std::false_type {}; -template <typename T> -struct HasResizeUninitialized< - T, absl::void_t<decltype(std::declval<T>().resize_uninitialized(237))>> - : std::true_type {}; +// T has a __resize_default_init member. +template <typename string_type, typename = void> +struct ResizeUninitializedTraits { + using HasMember = std::false_type; + static void Resize(string_type* s, size_t new_size) { s->resize(new_size); } +}; +// __resize_default_init is provided by libc++ >= 8.0 and by Google's internal +// ::string implementation. template <typename string_type> -void ResizeUninit(string_type* s, size_t new_size, std::true_type) { - s->resize_uninitialized(new_size); -} -template <typename string_type> -void ResizeUninit(string_type* s, size_t new_size, std::false_type) { - s->resize(new_size); -} +struct ResizeUninitializedTraits< + string_type, absl::void_t<decltype(std::declval<string_type&>() + .__resize_default_init(237))> > { + using HasMember = std::true_type; + static void Resize(string_type* s, size_t new_size) { + s->__resize_default_init(new_size); + } +}; -// Returns true if the string implementation supports a resize where -// the new characters added to the string are left untouched. +// Returns true if the std::string implementation supports a resize where +// the new characters added to the std::string are left untouched. // // (A better name might be "STLStringSupportsUninitializedResize", alluding to // the previous function.) template <typename string_type> inline constexpr bool STLStringSupportsNontrashingResize(string_type*) { - return HasResizeUninitialized<string_type>(); + return ResizeUninitializedTraits<string_type>::HasMember::value; } // Like str->resize(new_size), except any new characters added to "*str" as a // result of resizing may be left uninitialized, rather than being filled with // '0' bytes. Typically used when code is then going to overwrite the backing -// store of the string with known data. Uses a Google extension to ::string. +// store of the std::string with known data. template <typename string_type, typename = void> inline void STLStringResizeUninitialized(string_type* s, size_t new_size) { - ResizeUninit(s, new_size, HasResizeUninitialized<string_type>()); + ResizeUninitializedTraits<string_type>::Resize(s, new_size); } } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_RESIZE_UNINITIALIZED_H_ diff --git a/absl/strings/internal/resize_uninitialized_test.cc b/absl/strings/internal/resize_uninitialized_test.cc index ad282efc..c5be0b12 100644 --- a/absl/strings/internal/resize_uninitialized_test.cc +++ b/absl/strings/internal/resize_uninitialized_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,44 +24,44 @@ struct resizable_string { void resize(size_t) { resize_call_count += 1; } }; -int resize_uninitialized_call_count = 0; +int resize_default_init_call_count = 0; -struct resize_uninitializable_string { +struct resize_default_init_string { void resize(size_t) { resize_call_count += 1; } - void resize_uninitialized(size_t) { resize_uninitialized_call_count += 1; } + void __resize_default_init(size_t) { resize_default_init_call_count += 1; } }; TEST(ResizeUninit, WithAndWithout) { resize_call_count = 0; - resize_uninitialized_call_count = 0; + resize_default_init_call_count = 0; { resizable_string rs; EXPECT_EQ(resize_call_count, 0); - EXPECT_EQ(resize_uninitialized_call_count, 0); + EXPECT_EQ(resize_default_init_call_count, 0); EXPECT_FALSE( absl::strings_internal::STLStringSupportsNontrashingResize(&rs)); EXPECT_EQ(resize_call_count, 0); - EXPECT_EQ(resize_uninitialized_call_count, 0); + EXPECT_EQ(resize_default_init_call_count, 0); absl::strings_internal::STLStringResizeUninitialized(&rs, 237); EXPECT_EQ(resize_call_count, 1); - EXPECT_EQ(resize_uninitialized_call_count, 0); + EXPECT_EQ(resize_default_init_call_count, 0); } resize_call_count = 0; - resize_uninitialized_call_count = 0; + resize_default_init_call_count = 0; { - resize_uninitializable_string rus; + resize_default_init_string rus; EXPECT_EQ(resize_call_count, 0); - EXPECT_EQ(resize_uninitialized_call_count, 0); + EXPECT_EQ(resize_default_init_call_count, 0); EXPECT_TRUE( absl::strings_internal::STLStringSupportsNontrashingResize(&rus)); EXPECT_EQ(resize_call_count, 0); - EXPECT_EQ(resize_uninitialized_call_count, 0); + EXPECT_EQ(resize_default_init_call_count, 0); absl::strings_internal::STLStringResizeUninitialized(&rus, 237); EXPECT_EQ(resize_call_count, 0); - EXPECT_EQ(resize_uninitialized_call_count, 1); + EXPECT_EQ(resize_default_init_call_count, 1); } } diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h index fed7bf7c..af50be7c 100644 --- a/absl/strings/internal/stl_type_traits.h +++ b/absl/strings/internal/stl_type_traits.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -40,7 +40,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { template <typename C, template <typename...> class T> @@ -243,6 +243,6 @@ struct IsStrictlyBaseOfAndConvertibleToSTLContainer IsConvertibleToSTLContainer<C>> {}; } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_ diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc index e5e1eee5..667cc133 100644 --- a/absl/strings/internal/str_format/arg.cc +++ b/absl/strings/internal/str_format/arg.cc @@ -14,7 +14,7 @@ #include "absl/strings/internal/str_format/float_conversion.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { @@ -375,5 +375,5 @@ ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(); } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 0af4c839..b6f708c6 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h @@ -7,6 +7,7 @@ #include <cstdio> #include <iomanip> #include <limits> +#include <memory> #include <sstream> #include <string> #include <type_traits> @@ -21,7 +22,7 @@ class Cord; class CordReader; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { class FormatCountCapture; class FormatSink; @@ -36,12 +37,14 @@ struct HasUserDefinedConvert< T, void_t<decltype(AbslFormatConvert( std::declval<const T&>(), std::declval<ConversionSpec>(), std::declval<FormatSink*>()))>> : std::true_type {}; + template <typename T> class StreamedWrapper; // If 'v' can be converted (in the printf sense) according to 'conv', // then convert it, appending to `sink` and return `true`. // Otherwise fail and return `false`. + // Raw pointers. struct VoidPtr { VoidPtr() = default; @@ -55,7 +58,8 @@ ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, ConversionSpec conv, FormatSinkImpl* sink); // Strings. -ConvertResult<Conv::s> FormatConvertImpl(const std::string& v, ConversionSpec conv, +ConvertResult<Conv::s> FormatConvertImpl(const std::string& v, + ConversionSpec conv, FormatSinkImpl* sink); ConvertResult<Conv::s> FormatConvertImpl(string_view v, ConversionSpec conv, FormatSinkImpl* sink); @@ -81,7 +85,7 @@ ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, int precision = conv.precision(); if (precision >= 0) - to_write = std::min(to_write, static_cast<size_t>(precision)); + to_write = (std::min)(to_write, static_cast<size_t>(precision)); space_remaining = Excess(to_write, space_remaining); @@ -290,7 +294,7 @@ class FormatArgImpl { struct Manager<T, ByPointer> { static Data SetValue(const T& value) { Data data; - data.ptr = &value; + data.ptr = std::addressof(value); return data; } @@ -410,13 +414,13 @@ class FormatArgImpl { ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__); \ + ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__) ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern); } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc index 9cb9559c..c9c51951 100644 --- a/absl/strings/internal/str_format/arg_test.cc +++ b/absl/strings/internal/str_format/arg_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // #include "absl/strings/internal/str_format/arg.h" @@ -14,7 +14,7 @@ #include "absl/strings/str_format.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { @@ -109,5 +109,5 @@ const char kMyArray[] = "ABCDE"; } // namespace } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc index 5cf026b6..48a306d4 100644 --- a/absl/strings/internal/str_format/bind.cc +++ b/absl/strings/internal/str_format/bind.cc @@ -6,7 +6,7 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { @@ -26,12 +26,12 @@ class ArgContext { explicit ArgContext(absl::Span<const FormatArgImpl> pack) : pack_(pack) {} // Fill 'bound' with the results of applying the context's argument pack - // to the specified 'props'. We synthesize a BoundConversion by + // to the specified 'unbound'. We synthesize a BoundConversion by // lining up a UnboundConversion with a user argument. We also // resolve any '*' specifiers for width and precision, so after // this call, 'bound' has all the information it needs to be formatted. // Returns false on failure. - bool Bind(const UnboundConversion *props, BoundConversion *bound); + bool Bind(const UnboundConversion* unbound, BoundConversion* bound); private: absl::Span<const FormatArgImpl> pack_; @@ -54,7 +54,8 @@ inline bool ArgContext::Bind(const UnboundConversion* unbound, // "A negative field width is taken as a '-' flag followed by a // positive field width." force_left = true; - width = -width; + // Make sure we don't overflow the width when negating it. + width = -std::max(width, -std::numeric_limits<int>::max()); } } @@ -160,7 +161,7 @@ bool BindWithPack(const UnboundConversion* props, } std::string Summarize(const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { + absl::Span<const FormatArgImpl> args) { typedef SummarizingConverter Converter; std::string out; { @@ -188,7 +189,7 @@ std::ostream& Streamable::Print(std::ostream& os) const { } std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { + absl::Span<const FormatArgImpl> args) { size_t orig = out->size(); if (ABSL_PREDICT_FALSE(!FormatUntyped(out, format, args))) { out->erase(orig); @@ -227,5 +228,5 @@ int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format, } } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h index df5562f6..db79eb6c 100644 --- a/absl/strings/internal/str_format/bind.h +++ b/absl/strings/internal/str_format/bind.h @@ -7,14 +7,13 @@ #include <string> #include "absl/base/port.h" -#include "absl/container/inlined_vector.h" #include "absl/strings/internal/str_format/arg.h" #include "absl/strings/internal/str_format/checker.h" #include "absl/strings/internal/str_format/parser.h" #include "absl/types/span.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { class UntypedFormatSpec; @@ -139,7 +138,17 @@ class Streamable { public: Streamable(const UntypedFormatSpecImpl& format, absl::Span<const FormatArgImpl> args) - : format_(format), args_(args.begin(), args.end()) {} + : format_(format) { + if (args.size() <= ABSL_ARRAYSIZE(few_args_)) { + for (size_t i = 0; i < args.size(); ++i) { + few_args_[i] = args[i]; + } + args_ = absl::MakeSpan(few_args_, args.size()); + } else { + many_args_.assign(args.begin(), args.end()); + args_ = many_args_; + } + } std::ostream& Print(std::ostream& os) const; @@ -149,12 +158,17 @@ class Streamable { private: const UntypedFormatSpecImpl& format_; - absl::InlinedVector<FormatArgImpl, 4> args_; + absl::Span<const FormatArgImpl> args_; + // if args_.size() is 4 or less: + FormatArgImpl few_args_[4] = {FormatArgImpl(0), FormatArgImpl(0), + FormatArgImpl(0), FormatArgImpl(0)}; + // if args_.size() is more than 4: + std::vector<FormatArgImpl> many_args_; }; // for testing std::string Summarize(UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args); + absl::Span<const FormatArgImpl> args); bool BindWithPack(const UnboundConversion* props, absl::Span<const FormatArgImpl> pack, BoundConversion* bound); @@ -163,10 +177,10 @@ bool FormatUntyped(FormatRawSinkImpl raw_sink, absl::Span<const FormatArgImpl> args); std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args); + absl::Span<const FormatArgImpl> args); inline std::string FormatPack(const UntypedFormatSpecImpl format, - absl::Span<const FormatArgImpl> args) { + absl::Span<const FormatArgImpl> args) { std::string out; AppendPack(&out, format, args); return out; @@ -177,7 +191,7 @@ int FprintF(std::FILE* output, UntypedFormatSpecImpl format, int SnprintF(char* output, size_t size, UntypedFormatSpecImpl format, absl::Span<const FormatArgImpl> args); -// Returned by Streamed(v). Converts via '%s' to the string created +// Returned by Streamed(v). Converts via '%s' to the std::string created // by std::ostream << v. template <typename T> class StreamedWrapper { @@ -193,7 +207,7 @@ class StreamedWrapper { }; } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ diff --git a/absl/strings/internal/str_format/bind_test.cc b/absl/strings/internal/str_format/bind_test.cc index 58d9e072..8bccc92a 100644 --- a/absl/strings/internal/str_format/bind_test.cc +++ b/absl/strings/internal/str_format/bind_test.cc @@ -1,24 +1,20 @@ #include "absl/strings/internal/str_format/bind.h" #include <string.h> +#include <limits> #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { -template <typename T, size_t N> -size_t ArraySize(T (&)[N]) { - return N; -} - class FormatBindTest : public ::testing::Test { public: bool Extract(const char *s, UnboundConversion *props, int *next) const { - absl::string_view src = s; - return ConsumeUnboundConversion(&src, props, next) && src.empty(); + return ConsumeUnboundConversion(s, s + strlen(s), props, next) == + s + strlen(s); } }; @@ -92,6 +88,20 @@ TEST_F(FormatBindTest, BindSingle) { } } +TEST_F(FormatBindTest, WidthUnderflowRegression) { + UnboundConversion props; + BoundConversion bound; + int next = 0; + const int args_i[] = {std::numeric_limits<int>::min(), 17}; + const FormatArgImpl args[] = {FormatArgImpl(args_i[0]), + FormatArgImpl(args_i[1])}; + ASSERT_TRUE(Extract("*d", &props, &next)); + ASSERT_TRUE(BindWithPack(&props, args, &bound)); + + EXPECT_EQ(bound.width(), std::numeric_limits<int>::max()); + EXPECT_EQ(bound.arg(), args + 1); +} + TEST_F(FormatBindTest, FormatPack) { struct Expectation { int line; @@ -129,5 +139,5 @@ TEST_F(FormatBindTest, FormatPack) { } // namespace } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h index d0191968..262887cd 100644 --- a/absl/strings/internal/str_format/checker.h +++ b/absl/strings/internal/str_format/checker.h @@ -15,7 +15,7 @@ #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { constexpr bool AllOf() { return true; } @@ -321,7 +321,7 @@ constexpr bool ValidFormatImpl(string_view format) { #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc index b4f38979..2aa3b128 100644 --- a/absl/strings/internal/str_format/checker_test.cc +++ b/absl/strings/internal/str_format/checker_test.cc @@ -5,7 +5,7 @@ #include "absl/strings/str_format.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { @@ -63,32 +63,32 @@ TEST(StrFormatChecker, ValidFormat) { ValidFormat<int>("%% %d"), // ValidFormat<int>("%ld"), // ValidFormat<int>("%lld"), // - ValidFormat<std::string>("%s"), // - ValidFormat<std::string>("%10s"), // + ValidFormat<std::string>("%s"), // + ValidFormat<std::string>("%10s"), // ValidFormat<int>("%.10x"), // ValidFormat<int, int>("%*.3x"), // ValidFormat<int>("%1.d"), // ValidFormat<int>("%.d"), // ValidFormat<int, double>("%d %g"), // - ValidFormat<int, std::string>("%*s"), // + ValidFormat<int, std::string>("%*s"), // ValidFormat<int, double>("%.*f"), // ValidFormat<void (*)(), volatile int*>("%p %p"), // ValidFormat<string_view, const char*, double, void*>( "string_view=%s const char*=%s double=%f void*=%p)"), - ValidFormat<int>("%% %1$d"), // - ValidFormat<int>("%1$ld"), // - ValidFormat<int>("%1$lld"), // - ValidFormat<std::string>("%1$s"), // - ValidFormat<std::string>("%1$10s"), // - ValidFormat<int>("%1$.10x"), // - ValidFormat<int>("%1$*1$.*1$d"), // - ValidFormat<int, int>("%1$*2$.3x"), // - ValidFormat<int>("%1$1.d"), // - ValidFormat<int>("%1$.d"), // - ValidFormat<double, int>("%2$d %1$g"), // - ValidFormat<int, std::string>("%2$*1$s"), // - ValidFormat<int, double>("%2$.*1$f"), // + ValidFormat<int>("%% %1$d"), // + ValidFormat<int>("%1$ld"), // + ValidFormat<int>("%1$lld"), // + ValidFormat<std::string>("%1$s"), // + ValidFormat<std::string>("%1$10s"), // + ValidFormat<int>("%1$.10x"), // + ValidFormat<int>("%1$*1$.*1$d"), // + ValidFormat<int, int>("%1$*2$.3x"), // + ValidFormat<int>("%1$1.d"), // + ValidFormat<int>("%1$.d"), // + ValidFormat<double, int>("%2$d %1$g"), // + ValidFormat<int, std::string>("%2$*1$s"), // + ValidFormat<int, double>("%2$.*1$f"), // ValidFormat<void*, string_view, const char*, double>( "string_view=%2$s const char*=%3$s double=%4$f void*=%1$p " "repeat=%3$s)")}; @@ -100,25 +100,25 @@ TEST(StrFormatChecker, ValidFormat) { constexpr Case falses[] = { ValidFormat<int>(""), // - ValidFormat<e>("%s"), // - ValidFormat<e2>("%s"), // - ValidFormat<>("%s"), // - ValidFormat<>("%r"), // - ValidFormat<int>("%s"), // - ValidFormat<int>("%.1.d"), // - ValidFormat<int>("%*1d"), // - ValidFormat<int>("%1-d"), // + ValidFormat<e>("%s"), // + ValidFormat<e2>("%s"), // + ValidFormat<>("%s"), // + ValidFormat<>("%r"), // + ValidFormat<int>("%s"), // + ValidFormat<int>("%.1.d"), // + ValidFormat<int>("%*1d"), // + ValidFormat<int>("%1-d"), // ValidFormat<std::string, int>("%*s"), // - ValidFormat<int>("%*d"), // + ValidFormat<int>("%*d"), // ValidFormat<std::string>("%p"), // - ValidFormat<int (*)(int)>("%d"), // - - ValidFormat<>("%3$d"), // - ValidFormat<>("%1$r"), // - ValidFormat<int>("%1$s"), // - ValidFormat<int>("%1$.1.d"), // - ValidFormat<int>("%1$*2$1d"), // - ValidFormat<int>("%1$1-d"), // + ValidFormat<int (*)(int)>("%d"), // + + ValidFormat<>("%3$d"), // + ValidFormat<>("%1$r"), // + ValidFormat<int>("%1$s"), // + ValidFormat<int>("%1$.1.d"), // + ValidFormat<int>("%1$*2$1d"), // + ValidFormat<int>("%1$1-d"), // ValidFormat<std::string, int>("%2$*1$s"), // ValidFormat<std::string>("%1$p"), @@ -148,5 +148,5 @@ TEST(StrFormatChecker, LongFormat) { } // namespace } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index 95d57b67..751bfc34 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc @@ -1,6 +1,7 @@ #include <errno.h> #include <stdarg.h> #include <stdio.h> +#include <cctype> #include <cmath> #include <string> @@ -8,7 +9,7 @@ #include "absl/strings/internal/str_format/bind.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { @@ -33,7 +34,9 @@ std::string LengthModFor(long long) { return "ll"; } // NOLINT std::string LengthModFor(unsigned long long) { return "ll"; } // NOLINT std::string EscCharImpl(int v) { - if (isprint(v)) return std::string(1, static_cast<char>(v)); + if (std::isprint(static_cast<unsigned char>(v))) { + return std::string(1, static_cast<char>(v)); + } char buf[64]; int n = snprintf(buf, sizeof(buf), "\\%#.2x", static_cast<unsigned>(v & 0xff)); @@ -156,7 +159,7 @@ TEST_F(FormatConvertTest, StringPrecision) { } TEST_F(FormatConvertTest, Pointer) { -#if _MSC_VER +#ifdef _MSC_VER // MSVC's printf implementation prints pointers differently. We can't easily // compare our implementation to theirs. return; @@ -233,7 +236,7 @@ TEST_F(FormatConvertTest, Enum) { template <typename T> class TypedFormatConvertTest : public FormatConvertTest { }; -TYPED_TEST_CASE_P(TypedFormatConvertTest); +TYPED_TEST_SUITE_P(TypedFormatConvertTest); std::vector<std::string> AllFlagCombinations() { const char kFlags[] = {'-', '#', '0', '+', ' '}; @@ -364,6 +367,18 @@ typedef ::testing::Types< AllIntTypes; INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes, TypedFormatConvertTest, AllIntTypes); + +TEST_F(FormatConvertTest, VectorBool) { + // Make sure vector<bool>'s values behave as bools. + std::vector<bool> v = {true, false}; + const std::vector<bool> cv = {true, false}; + EXPECT_EQ("1,0,1,0", + FormatPack(UntypedFormatSpecImpl("%d,%d,%d,%d"), + absl::Span<const FormatArgImpl>( + {FormatArgImpl(v[0]), FormatArgImpl(v[1]), + FormatArgImpl(cv[0]), FormatArgImpl(cv[1])}))); +} + TEST_F(FormatConvertTest, Uint128) { absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979; absl::uint128 max = absl::Uint128Max(); @@ -390,7 +405,7 @@ TEST_F(FormatConvertTest, Uint128) { } TEST_F(FormatConvertTest, Float) { -#if _MSC_VER +#ifdef _MSC_VER // MSVC has a different rounding policy than us so we can't test our // implementation against the native one there. return; @@ -573,5 +588,5 @@ TEST_F(FormatConvertTest, ExpectedFailures) { } // namespace } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/extension.cc b/absl/strings/internal/str_format/extension.cc index e3b41c82..1a0c4172 100644 --- a/absl/strings/internal/str_format/extension.cc +++ b/absl/strings/internal/str_format/extension.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { // clang-format off @@ -82,5 +82,5 @@ bool FormatSinkImpl::PutPaddedString(string_view v, int w, int p, bool l) { } } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h index d401b4ed..4fbe4a06 100644 --- a/absl/strings/internal/str_format/extension.h +++ b/absl/strings/internal/str_format/extension.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -13,7 +13,6 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ @@ -29,7 +28,7 @@ class Cord; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { @@ -361,7 +360,7 @@ enum class Conv : uint64_t { integral = d | i | u | o | x | X, floating = a | e | f | g | A | E | F | G, numeric = integral | floating, - string = s, // absl:ignore(std::string) + string = s, pointer = p }; @@ -408,7 +407,7 @@ inline size_t Excess(size_t used, size_t capacity) { } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ diff --git a/absl/strings/internal/str_format/extension_test.cc b/absl/strings/internal/str_format/extension_test.cc index 224fc923..4e23fefb 100644 --- a/absl/strings/internal/str_format/extension_test.cc +++ b/absl/strings/internal/str_format/extension_test.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,6 +18,7 @@ #include <random> #include <string> + #include "absl/strings/str_format.h" #include "gtest/gtest.h" diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc index 7b617689..a0484feb 100644 --- a/absl/strings/internal/str_format/float_conversion.cc +++ b/absl/strings/internal/str_format/float_conversion.cc @@ -6,8 +6,10 @@ #include <cmath> #include <string> +#include "absl/base/config.h" + namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { @@ -335,7 +337,7 @@ bool FloatToBuffer(Decomposed<Float> decomposed, int precision, Buffer *out, static_cast<std::uint64_t>(decomposed.exponent), precision, out, exp)) return true; -#if defined(__SIZEOF_INT128__) +#if defined(ABSL_HAVE_INTRINSIC_INT128) // If that is not enough, try with __uint128_t. return CanFitMantissa<Float, __uint128_t>() && FloatToBufferImpl<__uint128_t, Float, mode>( @@ -481,5 +483,5 @@ bool ConvertFloatImpl(double v, const ConversionSpec &conv, } } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/float_conversion.h b/absl/strings/internal/str_format/float_conversion.h index 280c471a..5d76ce2b 100644 --- a/absl/strings/internal/str_format/float_conversion.h +++ b/absl/strings/internal/str_format/float_conversion.h @@ -4,7 +4,7 @@ #include "absl/strings/internal/str_format/extension.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { bool ConvertFloatImpl(float v, const ConversionSpec &conv, @@ -17,7 +17,7 @@ bool ConvertFloatImpl(long double v, const ConversionSpec &conv, FormatSinkImpl *sink); } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_FLOAT_CONVERSION_H_ diff --git a/absl/strings/internal/str_format/output.cc b/absl/strings/internal/str_format/output.cc index 010bf341..d5ead8d5 100644 --- a/absl/strings/internal/str_format/output.cc +++ b/absl/strings/internal/str_format/output.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include <cstring> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { @@ -68,5 +68,5 @@ void FILERawSink::Write(string_view v) { } } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/output.h b/absl/strings/internal/str_format/output.h index 0f3ab349..ba847471 100644 --- a/absl/strings/internal/str_format/output.h +++ b/absl/strings/internal/str_format/output.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -31,7 +31,7 @@ class Cord; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { // RawSink implementation that writes into a char* buffer. @@ -97,7 +97,7 @@ auto InvokeFlush(T* out, string_view s) } } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_OUTPUT_H_ diff --git a/absl/strings/internal/str_format/output_test.cc b/absl/strings/internal/str_format/output_test.cc index 0a014cac..5c3c4afb 100644 --- a/absl/strings/internal/str_format/output_test.cc +++ b/absl/strings/internal/str_format/output_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,24 +17,17 @@ #include <sstream> #include <string> - #include "gmock/gmock.h" #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { TEST(InvokeFlush, String) { std::string str = "ABC"; str_format_internal::InvokeFlush(&str, "DEF"); EXPECT_EQ(str, "ABCDEF"); - -#if UTIL_FORMAT_HAS_GLOBAL_STRING - std::string str2 = "ABC"; - str_format_internal::InvokeFlush(&str2, "DEF"); - EXPECT_EQ(str2, "ABCDEF"); -#endif // UTIL_FORMAT_HAS_GLOBAL_STRING } TEST(InvokeFlush, Stream) { @@ -75,6 +68,6 @@ TEST(BufferRawSink, Limits) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc index 119a711e..4b8ca4d2 100644 --- a/absl/strings/internal/str_format/parser.cc +++ b/absl/strings/internal/str_format/parser.cc @@ -14,8 +14,46 @@ #include <unordered_set> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { + +using CC = ConversionChar::Id; +using LM = LengthMod::Id; +ABSL_CONST_INIT const ConvTag kTags[256] = { + {}, {}, {}, {}, {}, {}, {}, {}, // 00-07 + {}, {}, {}, {}, {}, {}, {}, {}, // 08-0f + {}, {}, {}, {}, {}, {}, {}, {}, // 10-17 + {}, {}, {}, {}, {}, {}, {}, {}, // 18-1f + {}, {}, {}, {}, {}, {}, {}, {}, // 20-27 + {}, {}, {}, {}, {}, {}, {}, {}, // 28-2f + {}, {}, {}, {}, {}, {}, {}, {}, // 30-37 + {}, {}, {}, {}, {}, {}, {}, {}, // 38-3f + {}, CC::A, {}, CC::C, {}, CC::E, CC::F, CC::G, // @ABCDEFG + {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO + {}, {}, {}, CC::S, {}, {}, {}, {}, // PQRSTUVW + CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ + {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg + LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno + CC::p, LM::q, {}, CC::s, LM::t, CC::u, {}, {}, // pqrstuvw + CC::x, {}, LM::z, {}, {}, {}, {}, {}, // xyz{|}! + {}, {}, {}, {}, {}, {}, {}, {}, // 80-87 + {}, {}, {}, {}, {}, {}, {}, {}, // 88-8f + {}, {}, {}, {}, {}, {}, {}, {}, // 90-97 + {}, {}, {}, {}, {}, {}, {}, {}, // 98-9f + {}, {}, {}, {}, {}, {}, {}, {}, // a0-a7 + {}, {}, {}, {}, {}, {}, {}, {}, // a8-af + {}, {}, {}, {}, {}, {}, {}, {}, // b0-b7 + {}, {}, {}, {}, {}, {}, {}, {}, // b8-bf + {}, {}, {}, {}, {}, {}, {}, {}, // c0-c7 + {}, {}, {}, {}, {}, {}, {}, {}, // c8-cf + {}, {}, {}, {}, {}, {}, {}, {}, // d0-d7 + {}, {}, {}, {}, {}, {}, {}, {}, // d8-df + {}, {}, {}, {}, {}, {}, {}, {}, // e0-e7 + {}, {}, {}, {}, {}, {}, {}, {}, // e8-ef + {}, {}, {}, {}, {}, {}, {}, {}, // f0-f7 + {}, {}, {}, {}, {}, {}, {}, {}, // f8-ff +}; + namespace { bool CheckFastPathSetting(const UnboundConversion& conv) { @@ -37,60 +75,17 @@ bool CheckFastPathSetting(const UnboundConversion& conv) { return should_be_basic == conv.flags.basic; } -// Keep a single table for all the conversion chars and length modifiers. -// We invert the length modifiers to make them negative so that we can easily -// test for them. -// Everything else is `none`, which is a negative constant. -using CC = ConversionChar::Id; -using LM = LengthMod::Id; -static constexpr std::int8_t none = -128; -static constexpr std::int8_t kIds[] = { - none, none, none, none, none, none, none, none, // 00-07 - none, none, none, none, none, none, none, none, // 08-0f - none, none, none, none, none, none, none, none, // 10-17 - none, none, none, none, none, none, none, none, // 18-1f - none, none, none, none, none, none, none, none, // 20-27 - none, none, none, none, none, none, none, none, // 28-2f - none, none, none, none, none, none, none, none, // 30-37 - none, none, none, none, none, none, none, none, // 38-3f - none, CC::A, none, CC::C, none, CC::E, CC::F, CC::G, // @ABCDEFG - none, none, none, none, ~LM::L, none, none, none, // HIJKLMNO - none, none, none, CC::S, none, none, none, none, // PQRSTUVW - CC::X, none, none, none, none, none, none, none, // XYZ[\]^_ - none, CC::a, none, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg - ~LM::h, CC::i, ~LM::j, none, ~LM::l, none, CC::n, CC::o, // hijklmno - CC::p, ~LM::q, none, CC::s, ~LM::t, CC::u, none, none, // pqrstuvw - CC::x, none, ~LM::z, none, none, none, none, none, // xyz{|}~! - none, none, none, none, none, none, none, none, // 80-87 - none, none, none, none, none, none, none, none, // 88-8f - none, none, none, none, none, none, none, none, // 90-97 - none, none, none, none, none, none, none, none, // 98-9f - none, none, none, none, none, none, none, none, // a0-a7 - none, none, none, none, none, none, none, none, // a8-af - none, none, none, none, none, none, none, none, // b0-b7 - none, none, none, none, none, none, none, none, // b8-bf - none, none, none, none, none, none, none, none, // c0-c7 - none, none, none, none, none, none, none, none, // c8-cf - none, none, none, none, none, none, none, none, // d0-d7 - none, none, none, none, none, none, none, none, // d8-df - none, none, none, none, none, none, none, none, // e0-e7 - none, none, none, none, none, none, none, none, // e8-ef - none, none, none, none, none, none, none, none, // f0-f7 - none, none, none, none, none, none, none, none, // f8-ff -}; - template <bool is_positional> -bool ConsumeConversion(string_view *src, UnboundConversion *conv, - int *next_arg) { - const char *pos = src->data(); - const char *const end = pos + src->size(); +const char *ConsumeConversion(const char *pos, const char *const end, + UnboundConversion *conv, int *next_arg) { + const char* const original_pos = pos; char c; // Read the next char into `c` and update `pos`. Returns false if there are // no more chars to read. -#define ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR() \ - do { \ - if (ABSL_PREDICT_FALSE(pos == end)) return false; \ - c = *pos++; \ +#define ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR() \ + do { \ + if (ABSL_PREDICT_FALSE(pos == end)) return nullptr; \ + c = *pos++; \ } while (0) const auto parse_digits = [&] { @@ -100,10 +95,11 @@ bool ConsumeConversion(string_view *src, UnboundConversion *conv, // digit doesn't match the expected characters. int num_digits = std::numeric_limits<int>::digits10; for (;;) { - if (ABSL_PREDICT_FALSE(pos == end || !num_digits)) break; + if (ABSL_PREDICT_FALSE(pos == end)) break; c = *pos++; if (!std::isdigit(c)) break; --num_digits; + if (ABSL_PREDICT_FALSE(!num_digits)) break; digits = 10 * digits + c - '0'; } return digits; @@ -111,10 +107,10 @@ bool ConsumeConversion(string_view *src, UnboundConversion *conv, if (is_positional) { ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); - if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return false; + if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; conv->arg_position = parse_digits(); assert(conv->arg_position > 0); - if (ABSL_PREDICT_FALSE(c != '$')) return false; + if (ABSL_PREDICT_FALSE(c != '$')) return nullptr; } ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); @@ -129,10 +125,9 @@ bool ConsumeConversion(string_view *src, UnboundConversion *conv, conv->flags.basic = false; for (; c <= '0';) { - // FIXME: We might be able to speed this up reusing the kIds lookup table - // from above. - // It might require changing Flags to be a plain integer where we can |= a - // value. + // FIXME: We might be able to speed this up reusing the lookup table from + // above. It might require changing Flags to be a plain integer where we + // can |= a value. switch (c) { case '-': conv->flags.left = true; @@ -160,20 +155,20 @@ flags_done: if (c >= '0') { int maybe_width = parse_digits(); if (!is_positional && c == '$') { - if (ABSL_PREDICT_FALSE(*next_arg != 0)) return false; + if (ABSL_PREDICT_FALSE(*next_arg != 0)) return nullptr; // Positional conversion. *next_arg = -1; conv->flags = Flags(); conv->flags.basic = true; - return ConsumeConversion<true>(src, conv, next_arg); + return ConsumeConversion<true>(original_pos, end, conv, next_arg); } conv->width.set_value(maybe_width); } else if (c == '*') { ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); if (is_positional) { - if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return false; + if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; conv->width.set_from_arg(parse_digits()); - if (ABSL_PREDICT_FALSE(c != '$')) return false; + if (ABSL_PREDICT_FALSE(c != '$')) return nullptr; ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); } else { conv->width.set_from_arg(++*next_arg); @@ -188,9 +183,9 @@ flags_done: } else if (c == '*') { ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); if (is_positional) { - if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return false; + if (ABSL_PREDICT_FALSE(c < '1' || c > '9')) return nullptr; conv->precision.set_from_arg(parse_digits()); - if (c != '$') return false; + if (c != '$') return nullptr; ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); } else { conv->precision.set_from_arg(++*next_arg); @@ -201,14 +196,14 @@ flags_done: } } - std::int8_t id = kIds[static_cast<unsigned char>(c)]; + auto tag = GetTagForChar(c); - if (id < 0) { - if (ABSL_PREDICT_FALSE(id == none)) return false; + if (ABSL_PREDICT_FALSE(!tag.is_conv())) { + if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr; // It is a length modifier. using str_format_internal::LengthMod; - LengthMod length_mod = LengthMod::FromId(static_cast<LM>(~id)); + LengthMod length_mod = tag.as_length(); ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); if (c == 'h' && length_mod.id() == LengthMod::h) { conv->length_mod = LengthMod::FromId(LengthMod::hh); @@ -219,25 +214,24 @@ flags_done: } else { conv->length_mod = length_mod; } - id = kIds[static_cast<unsigned char>(c)]; - if (ABSL_PREDICT_FALSE(id < 0)) return false; + tag = GetTagForChar(c); + if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr; } assert(CheckFastPathSetting(*conv)); (void)(&CheckFastPathSetting); - conv->conv = ConversionChar::FromId(static_cast<CC>(id)); + conv->conv = tag.as_conv(); if (!is_positional) conv->arg_position = ++*next_arg; - *src = string_view(pos, end - pos); - return true; + return pos; } } // namespace -bool ConsumeUnboundConversion(string_view *src, UnboundConversion *conv, - int *next_arg) { - if (*next_arg < 0) return ConsumeConversion<true>(src, conv, next_arg); - return ConsumeConversion<false>(src, conv, next_arg); +const char *ConsumeUnboundConversion(const char *p, const char *end, + UnboundConversion *conv, int *next_arg) { + if (*next_arg < 0) return ConsumeConversion<true>(p, end, conv, next_arg); + return ConsumeConversion<false>(p, end, conv, next_arg); } struct ParsedFormatBase::ParsedFormatConsumer { @@ -307,5 +301,5 @@ bool ParsedFormatBase::MatchesConversions( } } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h index 9842c117..d3357fde 100644 --- a/absl/strings/internal/str_format/parser.h +++ b/absl/strings/internal/str_format/parser.h @@ -16,7 +16,7 @@ #include "absl/strings/internal/str_format/extension.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { // The analyzed properties of a single specified conversion. @@ -64,17 +64,45 @@ struct UnboundConversion { ConversionChar conv; }; -// Consume conversion spec prefix (not including '%') of '*src' if valid. +// Consume conversion spec prefix (not including '%') of [p, end) if valid. // Examples of valid specs would be e.g.: "s", "d", "-12.6f". -// If valid, the front of src is advanced such that src becomes the -// part following the conversion spec, and the spec part is broken down and -// returned in 'conv'. -// If invalid, returns false and leaves 'src' unmodified. -// For example: -// Given "d9", returns "d", and leaves src="9", -// Given "!", returns "" and leaves src="!". -bool ConsumeUnboundConversion(string_view* src, UnboundConversion* conv, - int* next_arg); +// If valid, it returns the first character following the conversion spec, +// and the spec part is broken down and returned in 'conv'. +// If invalid, returns nullptr. +const char* ConsumeUnboundConversion(const char* p, const char* end, + UnboundConversion* conv, int* next_arg); + +// Helper tag class for the table below. +// It allows fast `char -> ConversionChar/LengthMod` checking and conversions. +class ConvTag { + public: + constexpr ConvTag(ConversionChar::Id id) : tag_(id) {} // NOLINT + // We invert the length modifiers to make them negative so that we can easily + // test for them. + constexpr ConvTag(LengthMod::Id id) : tag_(~id) {} // NOLINT + // Everything else is -128, which is negative to make is_conv() simpler. + constexpr ConvTag() : tag_(-128) {} + + bool is_conv() const { return tag_ >= 0; } + bool is_length() const { return tag_ < 0 && tag_ != -128; } + ConversionChar as_conv() const { + assert(is_conv()); + return ConversionChar::FromId(static_cast<ConversionChar::Id>(tag_)); + } + LengthMod as_length() const { + assert(is_length()); + return LengthMod::FromId(static_cast<LengthMod::Id>(~tag_)); + } + + private: + std::int8_t tag_; +}; + +extern const ConvTag kTags[256]; +// Keep a single table for all the conversion chars and length modifiers. +inline ConvTag GetTagForChar(char c) { + return kTags[static_cast<unsigned char>(c)]; +} // Parse the format string provided in 'src' and pass the identified items into // 'consumer'. @@ -89,51 +117,53 @@ bool ConsumeUnboundConversion(string_view* src, UnboundConversion* conv, template <typename Consumer> bool ParseFormatString(string_view src, Consumer consumer) { int next_arg = 0; - while (!src.empty()) { - const char* percent = - static_cast<const char*>(memchr(src.data(), '%', src.size())); + const char* p = src.data(); + const char* const end = p + src.size(); + while (p != end) { + const char* percent = static_cast<const char*>(memchr(p, '%', end - p)); if (!percent) { // We found the last substring. - return consumer.Append(src); + return consumer.Append(string_view(p, end - p)); } // We found a percent, so push the text run then process the percent. - size_t percent_loc = percent - src.data(); - if (!consumer.Append(string_view(src.data(), percent_loc))) return false; - if (percent + 1 >= src.data() + src.size()) return false; - - UnboundConversion conv; - - switch (percent[1]) { - case '%': - if (!consumer.Append("%")) return false; - src.remove_prefix(percent_loc + 2); - continue; - -#define PARSER_CASE(ch) \ - case #ch[0]: \ - src.remove_prefix(percent_loc + 2); \ - conv.conv = ConversionChar::FromId(ConversionChar::ch); \ - conv.arg_position = ++next_arg; \ - break; - ABSL_CONVERSION_CHARS_EXPAND_(PARSER_CASE, ); -#undef PARSER_CASE - - default: - src.remove_prefix(percent_loc + 1); - if (!ConsumeUnboundConversion(&src, &conv, &next_arg)) return false; - break; - } - if (next_arg == 0) { - // This indicates an error in the format std::string. - // The only way to get next_arg == 0 is to have a positional argument - // first which sets next_arg to -1 and then a non-positional argument - // which does ++next_arg. - // Checking here seems to be the cheapeast place to do it. + if (ABSL_PREDICT_FALSE(!consumer.Append(string_view(p, percent - p)))) { return false; } - if (!consumer.ConvertOne( - conv, string_view(percent + 1, src.data() - (percent + 1)))) { - return false; + if (ABSL_PREDICT_FALSE(percent + 1 >= end)) return false; + + auto tag = GetTagForChar(percent[1]); + if (tag.is_conv()) { + if (ABSL_PREDICT_FALSE(next_arg < 0)) { + // This indicates an error in the format std::string. + // The only way to get `next_arg < 0` here is to have a positional + // argument first which sets next_arg to -1 and then a non-positional + // argument. + return false; + } + p = percent + 2; + + // Keep this case separate from the one below. + // ConvertOne is more efficient when the compiler can see that the `basic` + // flag is set. + UnboundConversion conv; + conv.conv = tag.as_conv(); + conv.arg_position = ++next_arg; + if (ABSL_PREDICT_FALSE( + !consumer.ConvertOne(conv, string_view(percent + 1, 1)))) { + return false; + } + } else if (percent[1] != '%') { + UnboundConversion conv; + p = ConsumeUnboundConversion(percent + 1, end, &conv, &next_arg); + if (ABSL_PREDICT_FALSE(p == nullptr)) return false; + if (ABSL_PREDICT_FALSE(!consumer.ConvertOne( + conv, string_view(percent + 1, p - (percent + 1))))) { + return false; + } + } else { + if (ABSL_PREDICT_FALSE(!consumer.Append("%"))) return false; + p = percent + 2; + continue; } } return true; @@ -288,7 +318,7 @@ class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase { : ParsedFormatBase(s, allow_ignored, {C...}) {} }; } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc index 14d90344..d77a8ea5 100644 --- a/absl/strings/internal/str_format/parser_test.cc +++ b/absl/strings/internal/str_format/parser_test.cc @@ -1,15 +1,19 @@ #include "absl/strings/internal/str_format/parser.h" #include <string.h> + +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/macros.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace str_format_internal { namespace { +using testing::Pair; + TEST(LengthModTest, Names) { struct Expectation { int line; @@ -64,20 +68,21 @@ TEST(ConversionCharTest, Names) { class ConsumeUnboundConversionTest : public ::testing::Test { public: - typedef UnboundConversion Props; - string_view Consume(string_view* src) { + std::pair<string_view, string_view> Consume(string_view src) { int next = 0; - const char* prev_begin = src->data(); o = UnboundConversion(); // refresh - ConsumeUnboundConversion(src, &o, &next); - return {prev_begin, static_cast<size_t>(src->data() - prev_begin)}; + const char* p = ConsumeUnboundConversion( + src.data(), src.data() + src.size(), &o, &next); + if (!p) return {{}, src}; + return {string_view(src.data(), p - src.data()), + string_view(p, src.data() + src.size() - p)}; } bool Run(const char *fmt, bool force_positional = false) { - string_view src = fmt; int next = force_positional ? -1 : 0; o = UnboundConversion(); // refresh - return ConsumeUnboundConversion(&src, &o, &next) && src.empty(); + return ConsumeUnboundConversion(fmt, fmt + strlen(fmt), &o, &next) == + fmt + strlen(fmt); } UnboundConversion o; }; @@ -105,11 +110,7 @@ TEST_F(ConsumeUnboundConversionTest, ConsumeSpecification) { }; for (const auto& e : kExpect) { SCOPED_TRACE(e.line); - string_view src = e.src; - EXPECT_EQ(e.src, src); - string_view out = Consume(&src); - EXPECT_EQ(e.out, out); - EXPECT_EQ(e.src_post, src); + EXPECT_THAT(Consume(e.src), Pair(e.out, e.src_post)); } } @@ -247,6 +248,8 @@ TEST_F(ConsumeUnboundConversionTest, WidthAndPrecision) { EXPECT_FALSE(Run("1000000000.999999999d")); EXPECT_FALSE(Run("999999999.1000000000d")); + EXPECT_FALSE(Run("9999999999d")); + EXPECT_FALSE(Run(".9999999999d")); } TEST_F(ConsumeUnboundConversionTest, Flags) { @@ -387,5 +390,5 @@ TEST_F(ParsedFormatTest, ParsingFlagOrder) { } // namespace } // namespace str_format_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h index 90b06d6f..02787dd1 100644 --- a/absl/strings/internal/str_join_internal.h +++ b/absl/strings/internal/str_join_internal.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -43,7 +43,7 @@ #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { // @@ -194,7 +194,7 @@ struct DefaultFormatter<std::unique_ptr<ValueType>> // and formats each element using the provided Formatter object. template <typename Iterator, typename Formatter> std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s, - Formatter&& f) { + Formatter&& f) { std::string result; absl::string_view sep(""); for (Iterator it = start; it != end; ++it) { @@ -213,7 +213,7 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s, // This is an overload of the previous JoinAlgorithm() function. Here the // Formatter argument is of type NoFormatter. Since NoFormatter is an internal // type, this overload is only invoked when strings::Join() is called with a -// range of string-like objects (e.g., string, absl::string_view), and an +// range of string-like objects (e.g., std::string, absl::string_view), and an // explicit Formatter argument was NOT specified. // // The optimization is that the needed space will be reserved in the output @@ -225,7 +225,7 @@ template <typename Iterator, typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value>::type> std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s, - NoFormatter) { + NoFormatter) { std::string result; if (start != end) { // Sums size @@ -277,14 +277,15 @@ struct JoinTupleLoop<N, N> { template <typename... T, typename Formatter> std::string JoinAlgorithm(const std::tuple<T...>& tup, absl::string_view sep, - Formatter&& fmt) { + Formatter&& fmt) { std::string result; JoinTupleLoop<0, sizeof...(T)>()(&result, tup, sep, fmt); return result; } template <typename Iterator> -std::string JoinRange(Iterator first, Iterator last, absl::string_view separator) { +std::string JoinRange(Iterator first, Iterator last, + absl::string_view separator) { // No formatter was explicitly given, so a default must be chosen. typedef typename std::iterator_traits<Iterator>::value_type ValueType; typedef typename DefaultFormatter<ValueType>::Type Formatter; @@ -293,7 +294,7 @@ std::string JoinRange(Iterator first, Iterator last, absl::string_view separator template <typename Range, typename Formatter> std::string JoinRange(const Range& range, absl::string_view separator, - Formatter&& fmt) { + Formatter&& fmt) { using std::begin; using std::end; return JoinAlgorithm(begin(range), end(range), separator, fmt); @@ -307,7 +308,7 @@ std::string JoinRange(const Range& range, absl::string_view separator) { } } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 2300193a..92a678e0 100644 --- a/absl/strings/internal/str_split_internal.h +++ b/absl/strings/internal/str_split_internal.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -47,7 +47,7 @@ #endif // _GLIBCXX_DEBUG namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { // This class is implicitly constructible from everything that absl::string_view @@ -97,8 +97,8 @@ ConvertibleToStringView(std::string&& s) // NOLINT(runtime/explicit) } } - // Holds the data moved from temporary std::string arguments. Declared first so - // that 'value' can refer to 'copy_'. + // Holds the data moved from temporary std::string arguments. Declared first + // so that 'value' can refer to 'copy_'. std::string copy_; absl::string_view value_; }; @@ -377,10 +377,10 @@ class Splitter { // Partial specialization for a std::vector<std::string>. // - // Optimized for the common case of splitting to a std::vector<std::string>. In - // this case we first split the results to a std::vector<absl::string_view> so - // the returned std::vector<std::string> can have space reserved to avoid std::string - // moves. + // Optimized for the common case of splitting to a std::vector<std::string>. + // In this case we first split the results to a std::vector<absl::string_view> + // so the returned std::vector<std::string> can have space reserved to avoid + // std::string moves. template <typename A> struct ConvertToContainer<std::vector<std::string, A>, std::string, false> { std::vector<std::string, A> operator()(const Splitter& splitter) const { @@ -449,7 +449,7 @@ class Splitter { }; } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 c6ab0d52..fe4a9beb 100644 --- a/absl/strings/internal/utf8.cc +++ b/absl/strings/internal/utf8.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include "absl/strings/internal/utf8.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) { @@ -49,5 +49,5 @@ size_t EncodeUTF8Char(char *buffer, char32_t utf8_char) { } } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/internal/utf8.h b/absl/strings/internal/utf8.h index 59c4f5b4..b1bb954e 100644 --- a/absl/strings/internal/utf8.h +++ b/absl/strings/internal/utf8.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -13,7 +13,6 @@ // limitations under the License. // // UTF8 utilities, implemented to reduce dependencies. -// #ifndef ABSL_STRINGS_INTERNAL_UTF8_H_ #define ABSL_STRINGS_INTERNAL_UTF8_H_ @@ -22,7 +21,7 @@ #include <cstdint> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { // For Unicode code points 0 through 0x10FFFF, EncodeUTF8Char writes @@ -43,7 +42,7 @@ enum { kMaxEncodedUTF8Size = 4 }; size_t EncodeUTF8Char(char *buffer, char32_t utf8_char); } // namespace strings_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_INTERNAL_UTF8_H_ diff --git a/absl/strings/internal/utf8_test.cc b/absl/strings/internal/utf8_test.cc index 64cec70d..88dd5036 100644 --- a/absl/strings/internal/utf8_test.cc +++ b/absl/strings/internal/utf8_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,12 +22,17 @@ namespace { +#if !defined(__cpp_char8_t) +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++2a-compat" +#endif TEST(EncodeUTF8Char, BasicFunction) { std::pair<char32_t, std::string> tests[] = {{0x0030, u8"\u0030"}, - {0x00A3, u8"\u00A3"}, - {0x00010000, u8"\U00010000"}, - {0x0000FFFF, u8"\U0000FFFF"}, - {0x0010FFFD, u8"\U0010FFFD"}}; + {0x00A3, u8"\u00A3"}, + {0x00010000, u8"\U00010000"}, + {0x0000FFFF, u8"\U0000FFFF"}, + {0x0010FFFD, u8"\U0010FFFD"}}; for (auto &test : tests) { char buf0[7] = {'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'}; char buf1[7] = {'\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', '\xFF'}; @@ -53,5 +58,9 @@ TEST(EncodeUTF8Char, BasicFunction) { EXPECT_LE(absl::strings_internal::EncodeUTF8Char(buf2, -1), absl::strings_internal::kMaxEncodedUTF8Size); } +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#endif // !defined(__cpp_char8_t) } // namespace diff --git a/absl/strings/match.cc b/absl/strings/match.cc index 12ba8edf..01ed1f15 100644 --- a/absl/strings/match.cc +++ b/absl/strings/match.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,16 +17,7 @@ #include "absl/strings/internal/memutil.h" namespace absl { -inline namespace lts_2018_12_18 { - -namespace { -bool CaseEqual(absl::string_view piece1, absl::string_view piece2) { - return (piece1.size() == piece2.size() && - 0 == strings_internal::memcasecmp(piece1.data(), piece2.data(), - piece1.size())); - // memcasecmp uses ascii_tolower(). -} -} // namespace +inline namespace lts_2019_08_08 { bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) { return (piece1.size() == piece2.size() && @@ -37,13 +28,13 @@ bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) { bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix) { return (text.size() >= prefix.size()) && - CaseEqual(text.substr(0, prefix.size()), prefix); + EqualsIgnoreCase(text.substr(0, prefix.size()), prefix); } bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix) { return (text.size() >= suffix.size()) && - CaseEqual(text.substr(text.size() - suffix.size()), suffix); + EqualsIgnoreCase(text.substr(text.size() - suffix.size()), suffix); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/match.h b/absl/strings/match.h index 5805207c..15fdc0c8 100644 --- a/absl/strings/match.h +++ b/absl/strings/match.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,7 +38,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // StrContains() // @@ -63,8 +63,7 @@ inline bool EndsWith(absl::string_view text, absl::string_view suffix) { return suffix.empty() || (text.size() >= suffix.size() && memcmp(text.data() + (text.size() - suffix.size()), suffix.data(), - suffix.size()) == 0 - ); + suffix.size()) == 0); } // EqualsIgnoreCase() @@ -75,17 +74,17 @@ bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2); // StartsWithIgnoreCase() // -// Returns whether a given ASCII string `text` starts with `starts_with`, +// Returns whether a given ASCII string `text` starts with `prefix`, // ignoring case in the comparison. bool StartsWithIgnoreCase(absl::string_view text, absl::string_view prefix); // EndsWithIgnoreCase() // -// Returns whether a given ASCII string `text` ends with `ends_with`, ignoring +// Returns whether a given ASCII string `text` ends with `suffix`, ignoring // case in the comparison. bool EndsWithIgnoreCase(absl::string_view text, absl::string_view suffix); -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_MATCH_H_ diff --git a/absl/strings/match_test.cc b/absl/strings/match_test.cc index c21e00bf..4c313dda 100644 --- a/absl/strings/match_test.cc +++ b/absl/strings/match_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,7 +19,7 @@ namespace { TEST(MatchTest, StartsWith) { - const std::string s1("123" "\0" "456", 7); + const std::string s1("123\0abc", 7); const absl::string_view a("foobar"); const absl::string_view b(s1); const absl::string_view e; @@ -36,7 +36,7 @@ TEST(MatchTest, StartsWith) { } TEST(MatchTest, EndsWith) { - const std::string s1("123" "\0" "456", 7); + const std::string s1("123\0abc", 7); const absl::string_view a("foobar"); const absl::string_view b(s1); const absl::string_view e; diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc index 4c3ddb34..2bac4adc 100644 --- a/absl/strings/numbers.cc +++ b/absl/strings/numbers.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -35,18 +35,19 @@ #include "absl/strings/ascii.h" #include "absl/strings/charconv.h" #include "absl/strings/internal/memutil.h" +#include "absl/strings/match.h" #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { -bool SimpleAtof(absl::string_view str, float* value) { - *value = 0.0; +bool SimpleAtof(absl::string_view str, float* out) { + *out = 0.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); + auto result = absl::from_chars(str.data(), str.data() + str.size(), *out); if (result.ec == std::errc::invalid_argument) { return false; } @@ -54,25 +55,25 @@ bool SimpleAtof(absl::string_view str, float* value) { // not all non-whitespace characters consumed return false; } - // from_chars() with DR 3801's current wording will return max() on + // from_chars() with DR 3081'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<float>::infinity(); - } else if (*value < -1.0) { - *value = -std::numeric_limits<float>::infinity(); + if (*out > 1.0) { + *out = std::numeric_limits<float>::infinity(); + } else if (*out < -1.0) { + *out = -std::numeric_limits<float>::infinity(); } } return true; } -bool SimpleAtod(absl::string_view str, double* value) { - *value = 0.0; +bool SimpleAtod(absl::string_view str, double* out) { + *out = 0.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); + auto result = absl::from_chars(str.data(), str.data() + str.size(), *out); if (result.ec == std::errc::invalid_argument) { return false; } @@ -80,13 +81,13 @@ bool SimpleAtod(absl::string_view str, double* value) { // not all non-whitespace characters consumed return false; } - // from_chars() with DR 3801's current wording will return max() on + // from_chars() with DR 3081'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<double>::infinity(); - } else if (*value < -1.0) { - *value = -std::numeric_limits<double>::infinity(); + if (*out > 1.0) { + *out = std::numeric_limits<double>::infinity(); + } else if (*out < -1.0) { + *out = -std::numeric_limits<double>::infinity(); } } return true; @@ -94,14 +95,6 @@ bool SimpleAtod(absl::string_view str, double* value) { namespace { -// TODO(rogeeff): replace with the real released thing once we figure out what -// it is. -inline bool CaseEqual(absl::string_view piece1, absl::string_view piece2) { - return (piece1.size() == piece2.size() && - 0 == strings_internal::memcasecmp(piece1.data(), piece2.data(), - piece1.size())); -} - // Writes a two-character representation of 'i' to 'buf'. 'i' must be in the // range 0 <= i < 100, and buf must have space for two characters. Example: // char buf[2]; @@ -137,18 +130,18 @@ inline void PutTwoDigits(size_t i, char* buf) { } // namespace -bool SimpleAtob(absl::string_view str, bool* value) { - ABSL_RAW_CHECK(value != nullptr, "Output pointer must not be nullptr."); - if (CaseEqual(str, "true") || CaseEqual(str, "t") || - CaseEqual(str, "yes") || CaseEqual(str, "y") || - CaseEqual(str, "1")) { - *value = true; +bool SimpleAtob(absl::string_view str, bool* out) { + ABSL_RAW_CHECK(out != nullptr, "Output pointer must not be nullptr."); + if (EqualsIgnoreCase(str, "true") || EqualsIgnoreCase(str, "t") || + EqualsIgnoreCase(str, "yes") || EqualsIgnoreCase(str, "y") || + EqualsIgnoreCase(str, "1")) { + *out = true; return true; } - if (CaseEqual(str, "false") || CaseEqual(str, "f") || - CaseEqual(str, "no") || CaseEqual(str, "n") || - CaseEqual(str, "0")) { - *value = false; + if (EqualsIgnoreCase(str, "false") || EqualsIgnoreCase(str, "f") || + EqualsIgnoreCase(str, "no") || EqualsIgnoreCase(str, "n") || + EqualsIgnoreCase(str, "0")) { + *out = false; return true; } return false; @@ -910,5 +903,5 @@ bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) { } } // namespace numbers_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index 250d2603..e9878016 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -38,48 +38,53 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // SimpleAtoi() // // Converts the given string into an integer value, returning `true` if // successful. The string must reflect a base-10 integer (optionally followed or // preceded by ASCII whitespace) whose value falls within the range of the -// integer type. +// integer type. If any errors are encountered, this function returns `false`, +// leaving `out` in an unspecified state. template <typename int_type> -ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view s, int_type* out); +ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out); // SimpleAtof() // // Converts the given string (optionally followed or preceded by ASCII // whitespace) into a float, which may be rounded on overflow or underflow. -// See http://en.cppreference.com/w/c/string/byte/strtof for details about the -// allowed formats for `str`. -ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* value); +// See https://en.cppreference.com/w/c/string/byte/strtof for details about the +// allowed formats for `str`. If any errors are encountered, this function +// returns `false`, leaving `out` in an unspecified state. +ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out); // SimpleAtod() // // Converts the given string (optionally followed or preceded by ASCII // whitespace) into a double, which may be rounded on overflow or underflow. -// See http://en.cppreference.com/w/c/string/byte/strtof for details about the -// allowed formats for `str`. -ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* value); +// See https://en.cppreference.com/w/c/string/byte/strtof for details about the +// allowed formats for `str`. If any errors are encountered, this function +// returns `false`, leaving `out` in an unspecified state. +ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out); // SimpleAtob() // // Converts the given string into a boolean, returning `true` if successful. // The following case-insensitive strings are interpreted as boolean `true`: // "true", "t", "yes", "y", "1". The following case-insensitive strings -// are interpreted as boolean `false`: "false", "f", "no", "n", "0". -ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* value); +// are interpreted as boolean `false`: "false", "f", "no", "n", "0". If any +// errors are encountered, this function returns `false`, leaving `out` in an +// unspecified state. +ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out); -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // End of public API. Implementation details follow. namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace numbers_internal { // safe_strto?() functions for implementing SimpleAtoi() @@ -178,11 +183,11 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out, // preceded by ASCII whitespace, with a value in the range of the corresponding // integer type. template <typename int_type> -ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view s, int_type* out) { - return numbers_internal::safe_strtoi_base(s, out, 10); +ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) { + return numbers_internal::safe_strtoi_base(str, out, 10); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_NUMBERS_H_ diff --git a/absl/strings/numbers_benchmark.cc b/absl/strings/numbers_benchmark.cc index 0570b758..54dbedd3 100644 --- a/absl/strings/numbers_benchmark.cc +++ b/absl/strings/numbers_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index 099326c2..77d7e783 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -191,7 +191,8 @@ void CheckUInt64(uint64_t x) { EXPECT_EQ(expected, std::string(&buffer[1], actual)) << " Input " << x; char* generic_actual = absl::numbers_internal::FastIntToBuffer(x, &buffer[1]); - EXPECT_EQ(expected, std::string(&buffer[1], generic_actual)) << " Input " << x; + EXPECT_EQ(expected, std::string(&buffer[1], generic_actual)) + << " Input " << x; char* my_actual = absl::numbers_internal::FastIntToBuffer(MyUInt64(x), &buffer[1]); @@ -712,7 +713,7 @@ TEST(stringtest, safe_strtou64_base_length_delimited) { } } -// feenableexcept() and fedisableexcept() are missing on Mac OS X, MSVC, +// feenableexcept() and fedisableexcept() are missing on macOS, MSVC, // and WebAssembly. #if defined(_MSC_VER) || defined(__APPLE__) || defined(__EMSCRIPTEN__) #define ABSL_MISSING_FEENABLEEXCEPT 1 @@ -784,7 +785,7 @@ void ExhaustiveFloat(uint32_t cases, R&& runnable) { if (iters_per_float == 0) iters_per_float = 1; for (float f : floats) { if (f == last) continue; - float testf = nextafter(last, std::numeric_limits<float>::max()); + float testf = std::nextafter(last, std::numeric_limits<float>::max()); runnable(testf); runnable(-testf); last = testf; @@ -798,7 +799,7 @@ void ExhaustiveFloat(uint32_t cases, R&& runnable) { last = testf; } } - testf = nextafter(f, 0.0f); + testf = std::nextafter(f, 0.0f); if (testf > last) { runnable(testf); runnable(-testf); @@ -879,8 +880,8 @@ TEST_F(SimpleDtoaTest, ExhaustiveDoubleToSixDigits) { char buf[kSixDigitsToBufferSize]; ABSL_RAW_LOG( INFO, "%s", - absl::StrCat("Exp ", exponent, " powten=", powten, "(", - powten, ") (", + absl::StrCat("Exp ", exponent, " powten=", powten, "(", powten, + ") (", std::string(buf, SixDigitsToBuffer(powten, buf)), ")") .c_str()); } diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index 2f2e5315..73b9e0ba 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/strings/internal/resize_uninitialized.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { AlphaNum::AlphaNum(Hex hex) { char* const end = &digits_[numbers_internal::kFastToBufferSize]; @@ -79,7 +79,7 @@ AlphaNum::AlphaNum(Dec dec) { // ---------------------------------------------------------------------- // StrCat() -// This merges the given strings or integers, with no delimiter. This +// This merges the given strings or integers, with no delimiter. This // is designed to be the fastest possible way to construct a string out // of a mix of raw C strings, string_views, strings, and integer values. // ---------------------------------------------------------------------- @@ -90,7 +90,9 @@ static char* Append(char* out, const AlphaNum& x) { // memcpy is allowed to overwrite arbitrary memory, so doing this after the // call would force an extra fetch of x.size(). char* after = out + x.size(); - memcpy(out, x.data(), x.size()); + if (x.size() != 0) { + memcpy(out, x.data(), x.size()); + } return after; } @@ -120,7 +122,7 @@ std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c) { } std::string StrCat(const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, - const AlphaNum& d) { + const AlphaNum& d) { std::string result; strings_internal::STLStringResizeUninitialized( &result, a.size() + b.size() + c.size() + d.size()); @@ -147,8 +149,10 @@ std::string CatPieces(std::initializer_list<absl::string_view> pieces) { char* out = begin; for (const absl::string_view piece : pieces) { const size_t this_size = piece.size(); - memcpy(out, piece.data(), this_size); - out += this_size; + if (this_size != 0) { + memcpy(out, piece.data(), this_size); + out += this_size; + } } assert(out == begin + result.size()); return result; @@ -177,8 +181,10 @@ void AppendPieces(std::string* dest, char* out = begin + old_size; for (const absl::string_view piece : pieces) { const size_t this_size = piece.size(); - memcpy(out, piece.data(), this_size); - out += this_size; + if (this_size != 0) { + memcpy(out, piece.data(), this_size); + out += this_size; + } } assert(out == begin + dest->size()); } @@ -237,5 +243,5 @@ void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, assert(out == begin + dest->size()); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index edda40ad..c2700475 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -37,12 +37,12 @@ // attempt to pass ':' instead of ":" might result in a 58 ending up in your // result. // -// Bools convert to "0" or "1". +// Bools convert to "0" or "1". Pointers to types other than `char *` are not +// valid inputs. No output is generated for null `char *` pointers. // // Floating point numbers are formatted with six-digit precision, which is // the default for "std::cout <<" or printf "%g" (the same as "%.6g"). // -// // You can convert to hexadecimal output rather than decimal output using the // `Hex` type contained here. To do so, pass `Hex(my_int)` as a parameter to // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using @@ -57,13 +57,14 @@ #include <cstdint> #include <string> #include <type_traits> +#include <vector> #include "absl/base/port.h" #include "absl/strings/numbers.h" #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { // AlphaNumBuffer allows a way to pass a string to StrCat without having to do @@ -79,8 +80,8 @@ struct AlphaNumBuffer { // Enum that specifies the number of significant digits to return in a `Hex` or // `Dec` conversion and fill character to use. A `kZeroPad2` value, for example, -// would produce hexadecimal strings such as "0A","0F" and a 'kSpacePad5' value -// would produce hexadecimal strings such as " A"," F". +// would produce hexadecimal strings such as "0a","0f" and a 'kSpacePad5' value +// would produce hexadecimal strings such as " a"," f". enum PadSpec : uint8_t { kNoPad = 1, kZeroPad2, @@ -246,6 +247,7 @@ class AlphaNum { AlphaNum(const char* c_str) : piece_(c_str) {} // NOLINT(runtime/explicit) AlphaNum(absl::string_view pc) : piece_(pc) {} // NOLINT(runtime/explicit) + template <typename Allocator> AlphaNum( // NOLINT(runtime/explicit) const std::basic_string<char, std::char_traits<char>, Allocator>& str) @@ -269,6 +271,17 @@ class AlphaNum { AlphaNum(T e) // NOLINT(runtime/explicit) : AlphaNum(static_cast<typename std::underlying_type<T>::type>(e)) {} + // vector<bool>::reference and const_reference require special help to + // convert to `AlphaNum` because it requires two user defined conversions. + template < + typename T, + typename std::enable_if< + std::is_class<T>::value && + (std::is_same<T, std::vector<bool>::reference>::value || + std::is_same<T, std::vector<bool>::const_reference>::value)>::type* = + nullptr> + AlphaNum(T e) : AlphaNum(static_cast<bool>(e)) {} // NOLINT(runtime/explicit) + private: absl::string_view piece_; char digits_[numbers_internal::kFastToBufferSize]; @@ -318,16 +331,15 @@ ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a) { ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b); ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c); + const AlphaNum& c); ABSL_MUST_USE_RESULT std::string StrCat(const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d); + const AlphaNum& c, const AlphaNum& d); // Support 5 or more arguments template <typename... AV> -ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, - const AlphaNum& e, - const AV&... args) { +ABSL_MUST_USE_RESULT inline std::string StrCat( + const AlphaNum& a, const AlphaNum& b, const AlphaNum& c, const AlphaNum& d, + const AlphaNum& e, const AV&... args) { return strings_internal::CatPieces( {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(), static_cast<const AlphaNum&>(args).Piece()...}); @@ -345,18 +357,18 @@ ABSL_MUST_USE_RESULT inline std::string StrCat(const AlphaNum& a, const AlphaNum // not try to check each of its input arguments to be sure that they are not // a subset of the string being appended to. That is, while this will work: // -// string s = "foo"; +// std::string s = "foo"; // s += s; // // This output is undefined: // -// string s = "foo"; +// std::string s = "foo"; // StrAppend(&s, s); // // This output is undefined as well, since `absl::string_view` does not own its // data: // -// string s = "foobar"; +// std::string s = "foobar"; // absl::string_view p = s; // StrAppend(&s, p); @@ -389,7 +401,7 @@ SixDigits(double d) { return result; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_STR_CAT_H_ diff --git a/absl/strings/str_cat_benchmark.cc b/absl/strings/str_cat_benchmark.cc index b6df9e30..14c63b3f 100644 --- a/absl/strings/str_cat_benchmark.cc +++ b/absl/strings/str_cat_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc index 07141072..29db9c02 100644 --- a/absl/strings/str_cat_test.cc +++ b/absl/strings/str_cat_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,6 +18,7 @@ #include <cstdint> #include <string> +#include <vector> #include "gtest/gtest.h" #include "absl/strings/substitute.h" @@ -106,11 +107,7 @@ TEST(StrCat, Enums) { TEST(StrCat, Basics) { std::string result; - std::string strs[] = { - "Hello", - "Cruel", - "World" - }; + std::string strs[] = {"Hello", "Cruel", "World"}; std::string stdstrs[] = { "std::Hello", @@ -164,9 +161,10 @@ TEST(StrCat, Basics) { result = absl::StrCat(ui64s[0], ", ", ui64s[1], "!"); EXPECT_EQ(result, "12345678910, 10987654321!"); - std::string one = "1"; // Actually, it's the size of this std::string that we want; a - // 64-bit build distinguishes between size_t and uint64_t, - // even though they're both unsigned 64-bit values. + std::string one = + "1"; // Actually, it's the size of this std::string that we want; a + // 64-bit build distinguishes between size_t and uint64_t, + // even though they're both unsigned 64-bit values. result = absl::StrCat("And a ", one.size(), " and a ", &result[2] - &result[0], " and a ", one, " 2 3 4", "!"); EXPECT_EQ(result, "And a 1 and a 2 and a 1 2 3 4!"); @@ -306,11 +304,7 @@ TEST(StrCat, MaxArgs) { TEST(StrAppend, Basics) { std::string result = "existing text"; - std::string strs[] = { - "Hello", - "Cruel", - "World" - }; + std::string strs[] = {"Hello", "Cruel", "World"}; std::string stdstrs[] = { "std::Hello", @@ -365,9 +359,10 @@ TEST(StrAppend, Basics) { absl::StrAppend(&result, ui64s[0], ", ", ui64s[1], "!"); EXPECT_EQ(result.substr(old_size), "12345678910, 10987654321!"); - std::string one = "1"; // Actually, it's the size of this std::string that we want; a - // 64-bit build distinguishes between size_t and uint64_t, - // even though they're both unsigned 64-bit values. + std::string one = + "1"; // Actually, it's the size of this std::string that we want; a + // 64-bit build distinguishes between size_t and uint64_t, + // even though they're both unsigned 64-bit values. old_size = result.size(); absl::StrAppend(&result, "And a ", one.size(), " and a ", &result[2] - &result[0], " and a ", one, " 2 3 4", "!"); @@ -401,6 +396,32 @@ TEST(StrAppend, Basics) { "No limit thanks to C++11's variadic templates"); } +TEST(StrCat, VectorBoolReferenceTypes) { + std::vector<bool> v; + v.push_back(true); + v.push_back(false); + std::vector<bool> const& cv = v; + // Test that vector<bool>::reference and vector<bool>::const_reference + // are handled as if the were really bool types and not the proxy types + // they really are. + std::string result = absl::StrCat(v[0], v[1], cv[0], cv[1]); // NOLINT + EXPECT_EQ(result, "1010"); +} + +// Passing nullptr to memcpy is undefined behavior and this test +// provides coverage of codepaths that handle empty strings with nullptrs. +TEST(StrCat, AvoidsMemcpyWithNullptr) { + EXPECT_EQ(absl::StrCat(42, absl::string_view{}), "42"); + + // Cover CatPieces code. + EXPECT_EQ(absl::StrCat(1, 2, 3, 4, 5, absl::string_view{}), "12345"); + + // Cover AppendPieces. + std::string result; + absl::StrAppend(&result, 1, 2, 3, 4, 5, absl::string_view{}); + EXPECT_EQ(result, "12345"); +} + #ifdef GTEST_HAS_DEATH_TEST TEST(StrAppend, Death) { std::string s = "self"; diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h index 7b19d411..b4d1b7bd 100644 --- a/absl/strings/str_format.h +++ b/absl/strings/str_format.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,11 +20,13 @@ // The `str_format` library is a typesafe replacement for the family of // `printf()` string formatting routines within the `<cstdio>` standard library // header. Like the `printf` family, the `str_format` uses a "format string" to -// perform argument substitutions based on types. +// perform argument substitutions based on types. See the `FormatSpec` section +// below for format string documentation. // // Example: // -// string s = absl::StrFormat("%s %s You have $%d!", "Hello", name, dollars); +// std::string s = absl::StrFormat( +// "%s %s You have $%d!", "Hello", name, dollars); // // The library consists of the following basic utilities: // @@ -49,7 +51,7 @@ // * A `ParsedFormat` instance, which encapsulates a specific, pre-compiled // format string for a specific set of type(s), and which can be passed // between API boundaries. (The `FormatSpec` type should not be used -// directly.) +// directly except as an argument type for wrapper functions.) // // The `str_format` library provides the ability to output its format strings to // arbitrary sink types: @@ -66,6 +68,7 @@ // In addition, the `str_format` library provides extension points for // augmenting formatting to new types. These extensions are fully documented // within the `str_format_extension.h` header file. + #ifndef ABSL_STRINGS_STR_FORMAT_H_ #define ABSL_STRINGS_STR_FORMAT_H_ @@ -79,7 +82,7 @@ #include "absl/strings/internal/str_format/parser.h" // IWYU pragma: export namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // UntypedFormatSpec // @@ -90,7 +93,7 @@ inline namespace lts_2018_12_18 { // Example: // // absl::UntypedFormatSpec format("%d"); -// string out; +// std::string out; // CHECK(absl::FormatUntyped(&out, format, {absl::FormatArg(1)})); class UntypedFormatSpec { public: @@ -136,8 +139,8 @@ str_format_internal::StreamedWrapper<T> FormatStreamed(const T& v) { // Example: // // int n = 0; -// string s = absl::StrFormat("%s%d%n", "hello", 123, -// absl::FormatCountCapture(&n)); +// std::string s = absl::StrFormat("%s%d%n", "hello", 123, +// absl::FormatCountCapture(&n)); // EXPECT_EQ(8, n); class FormatCountCapture { public: @@ -157,10 +160,15 @@ class FormatCountCapture { // FormatSpec // // The `FormatSpec` type defines the makeup of a format string within the -// `str_format` library. You should not need to use or manipulate this type -// directly. A `FormatSpec` is a variadic class template that is evaluated at -// compile-time, according to the format string and arguments that are passed -// to it. +// `str_format` library. It is a variadic class template that is evaluated at +// compile-time, according to the format string and arguments that are passed to +// it. +// +// You should not need to manipulate this type directly. You should only name it +// if you are writing wrapper functions which accept format arguments that will +// be provided unmodified to functions in this library. Such a wrapper function +// might be a class method that provides format arguments and/or internally uses +// the result of formatting. // // For a `FormatSpec` to be valid at compile-time, it must be provided as // either: @@ -187,7 +195,7 @@ class FormatCountCapture { // A format string generally follows the POSIX syntax as used within the POSIX // `printf` specification. // -// (See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html.) +// (See http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html.) // // In specific, the `FormatSpec` supports the following type specifiers: // * `c` for characters @@ -206,6 +214,11 @@ class FormatCountCapture { // written to this point. The resulting value must be captured within an // `absl::FormatCountCapture` type. // +// Implementation-defined behavior: +// * A null pointer provided to "%s" or "%p" is output as "(nil)". +// * A non-null pointer provided to "%p" is output in hex as if by %#x or +// %#lx. +// // NOTE: `o`, `x\X` and `u` will convert signed values to their unsigned // counterpart before formatting. // @@ -221,10 +234,10 @@ class FormatCountCapture { // "%e", .01 -> "1.00000e-2" // "%a", -3.0 -> "-0x1.8p+1" // "%g", .01 -> "1e-2" -// "%p", *int -> "0x7ffdeb6ad2a4" +// "%p", (void*)&value -> "0x7ffdeb6ad2a4" // // int n = 0; -// string s = absl::StrFormat( +// std::string s = absl::StrFormat( // "%s%d%n", "hello", 123, absl::FormatCountCapture(&n)); // EXPECT_EQ(8, n); // @@ -291,14 +304,14 @@ using ParsedFormat = str_format_internal::ExtendedParsedFormat< // // Example: // -// string s = absl::StrFormat( +// std::string s = absl::StrFormat( // "Welcome to %s, Number %d!", "The Village", 6); // EXPECT_EQ("Welcome to The Village, Number 6!", s); // // Returns an empty string in case of error. template <typename... Args> ABSL_MUST_USE_RESULT std::string StrFormat(const FormatSpec<Args...>& format, - const Args&... args) { + const Args&... args) { return str_format_internal::FormatPack( str_format_internal::UntypedFormatSpecImpl::Extract(format), {str_format_internal::FormatArgImpl(args)...}); @@ -312,11 +325,12 @@ ABSL_MUST_USE_RESULT std::string StrFormat(const FormatSpec<Args...>& format, // // Example: // -// string orig("For example PI is approximately "); +// std::string orig("For example PI is approximately "); // std::cout << StrAppendFormat(&orig, "%12.6f", 3.14); template <typename... Args> -std::string& StrAppendFormat(std::string* dst, const FormatSpec<Args...>& format, - const Args&... args) { +std::string& StrAppendFormat(std::string* dst, + const FormatSpec<Args...>& format, + const Args&... args) { return str_format_internal::AppendPack( dst, str_format_internal::UntypedFormatSpecImpl::Extract(format), {str_format_internal::FormatArgImpl(args)...}); @@ -435,7 +449,8 @@ class FormatRawSink { // `absl::FormatRawSink` interface), using a format string and zero or more // additional arguments. // -// By default, `string` and `std::ostream` are supported as destination objects. +// By default, `std::string` and `std::ostream` are supported as destination +// objects. If a `std::string` is used the formatted string is appended to it. // // `absl::Format()` is a generic version of `absl::StrFormat(), for custom // sinks. The format string, like format strings for `StrFormat()`, is checked @@ -484,9 +499,10 @@ using FormatArg = str_format_internal::FormatArgImpl; // // Example: // -// std::optional<string> FormatDynamic(const string& in_format, -// const vector<string>& in_args) { -// string out; +// std::optional<std::string> FormatDynamic( +// const std::string& in_format, +// const vector<std::string>& in_args) { +// std::string out; // std::vector<absl::FormatArg> args; // for (const auto& v : in_args) { // // It is important that 'v' is a reference to the objects in in_args. @@ -509,6 +525,7 @@ ABSL_MUST_USE_RESULT inline bool FormatUntyped( str_format_internal::UntypedFormatSpecImpl::Extract(format), args); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_STRINGS_STR_FORMAT_H_ diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index 77b8647f..bb0f58bf 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc @@ -10,11 +10,11 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { using str_format_internal::FormatArgImpl; -class FormatEntryPointTest : public ::testing::Test { }; +using FormatEntryPointTest = ::testing::Test; TEST_F(FormatEntryPointTest, Format) { std::string sink; @@ -31,8 +31,8 @@ TEST_F(FormatEntryPointTest, UntypedFormat) { "", "a", "%80d", -#if !defined(_MSC_VER) && !defined(__ANDROID__) - // MSVC and Android don't support positional syntax. +#if !defined(_MSC_VER) && !defined(__ANDROID__) && !defined(__native_client__) + // MSVC, NaCL and Android don't support positional syntax. "complicated multipart %% %1$d format %1$0999d", #endif // _MSC_VER }; @@ -154,17 +154,20 @@ TEST_F(FormatEntryPointTest, Stream) { "", "a", "%80d", -#if !defined(_MSC_VER) && !defined(__ANDROID__) - // MSVC doesn't support positional syntax. + "%d %u %c %s %f %g", +#if !defined(_MSC_VER) && !defined(__ANDROID__) && !defined(__native_client__) + // MSVC, NaCL and Android don't support positional syntax. "complicated multipart %% %1$d format %1$080d", #endif // _MSC_VER }; std::string buf(4096, '\0'); for (const auto& fmt : formats) { - const auto parsed = ParsedFormat<'d'>::NewAllowIgnored(fmt); + const auto parsed = + ParsedFormat<'d', 'u', 'c', 's', 'f', 'g'>::NewAllowIgnored(fmt); std::ostringstream oss; - oss << StreamFormat(*parsed, 123); - int fmt_result = snprintf(&*buf.begin(), buf.size(), fmt.c_str(), 123); + oss << StreamFormat(*parsed, 123, 3, 49, "multistreaming!!!", 1.01, 1.01); + int fmt_result = snprintf(&*buf.begin(), buf.size(), fmt.c_str(), // + 123, 3, 49, "multistreaming!!!", 1.01, 1.01); ASSERT_TRUE(oss) << fmt; ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size()) << fmt_result; @@ -272,7 +275,7 @@ TEST_F(FormatEntryPointTest, FPrintFError) { EXPECT_EQ(errno, EBADF); } -#if __GLIBC__ +#ifdef __GLIBC__ TEST_F(FormatEntryPointTest, FprintfTooLarge) { std::FILE* f = std::fopen("/dev/null", "w"); int width = 2000000000; @@ -342,7 +345,7 @@ TEST(StrFormat, BehavesAsDocumented) { EXPECT_EQ(StrFormat("%c", int{'a'}), "a"); EXPECT_EQ(StrFormat("%c", long{'a'}), "a"); // NOLINT EXPECT_EQ(StrFormat("%c", uint64_t{'a'}), "a"); - // "s" - std::string Eg: "C" -> "C", std::string("C++") -> "C++" + // "s" - std::string Eg: "C" -> "C", std::string("C++") -> "C++" // Formats std::string, char*, string_view, and Cord. EXPECT_EQ(StrFormat("%s", "C"), "C"); EXPECT_EQ(StrFormat("%s", std::string("C++")), "C++"); @@ -459,7 +462,7 @@ std::string SummarizeParsedFormat(const ParsedFormatBase& pc) { return out; } -class ParsedFormatTest : public testing::Test {}; +using ParsedFormatTest = ::testing::Test; TEST_F(ParsedFormatTest, SimpleChecked) { EXPECT_EQ("[ABC]{d:1$d}[DEF]", @@ -601,28 +604,46 @@ TEST_F(ParsedFormatTest, RegressionMixPositional) { EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::o>::New("%1$d %o"))); } +using FormatWrapperTest = ::testing::Test; + +// Plain wrapper for StrFormat. +template <typename... Args> +std::string WrappedFormat(const absl::FormatSpec<Args...>& format, + const Args&... args) { + return StrFormat(format, args...); +} + +TEST_F(FormatWrapperTest, ConstexprStringFormat) { + EXPECT_EQ(WrappedFormat("%s there", "hello"), "hello there"); +} + +TEST_F(FormatWrapperTest, ParsedFormat) { + ParsedFormat<'s'> format("%s there"); + EXPECT_EQ(WrappedFormat(format, "hello"), "hello there"); +} + } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // Some codegen thunks that we can use to easily dump the generated assembly for // different StrFormat calls. -std::string CodegenAbslStrFormatInt(int i) { // NOLINT +std::string CodegenAbslStrFormatInt(int i) { // NOLINT return absl::StrFormat("%d", i); } std::string CodegenAbslStrFormatIntStringInt64(int i, const std::string& s, - int64_t i64) { // NOLINT + int64_t i64) { // NOLINT return absl::StrFormat("%d %s %d", i, s, i64); } -void CodegenAbslStrAppendFormatInt(std::string* out, int i) { // NOLINT +void CodegenAbslStrAppendFormatInt(std::string* out, int i) { // NOLINT absl::StrAppendFormat(out, "%d", i); } void CodegenAbslStrAppendFormatIntStringInt64(std::string* out, int i, - const std::string& s, - int64_t i64) { // NOLINT + const std::string& s, + int64_t i64) { // NOLINT absl::StrAppendFormat(out, "%d %s %d", i, s, i64); } diff --git a/absl/strings/str_join.h b/absl/strings/str_join.h index dc476a22..c6c0c98a 100644 --- a/absl/strings/str_join.h +++ b/absl/strings/str_join.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,13 +18,13 @@ // ----------------------------------------------------------------------------- // // This header file contains functions for joining a range of elements and -// returning the result as a string. StrJoin operations are specified by passing -// a range, a separator string to use between the elements joined, and an -// optional Formatter responsible for converting each argument in the range to a -// string. If omitted, a default `AlphaNumFormatter()` is called on the elements -// to be joined, using the same formatting that `absl::StrCat()` uses. This -// package defines a number of default formatters, and you can define your own -// implementations. +// returning the result as a std::string. StrJoin operations are specified by +// passing a range, a separator string to use between the elements joined, and +// an optional Formatter responsible for converting each argument in the range +// to a string. If omitted, a default `AlphaNumFormatter()` is called on the +// elements to be joined, using the same formatting that `absl::StrCat()` uses. +// This package defines a number of default formatters, and you can define your +// own implementations. // // Ranges are specified by passing a container with `std::begin()` and // `std::end()` iterators, container-specific `begin()` and `end()` iterators, a @@ -37,8 +37,8 @@ // // Example: // -// std::vector<string> v = {"foo", "bar", "baz"}; -// string s = absl::StrJoin(v, "-"); +// std::vector<std::string> v = {"foo", "bar", "baz"}; +// std::string s = absl::StrJoin(v, "-"); // EXPECT_EQ("foo-bar-baz", s); // // See comments on the `absl::StrJoin()` function for more examples. @@ -60,23 +60,23 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // Concept: Formatter // ----------------------------------------------------------------------------- // // A Formatter is a function object that is responsible for formatting its -// argument as a string and appending it to a given output string. Formatters -// may be implemented as function objects, lambdas, or normal functions. You may -// provide your own Formatter to enable `absl::StrJoin()` to work with arbitrary -// types. +// argument as a string and appending it to a given output std::string. +// Formatters may be implemented as function objects, lambdas, or normal +// functions. You may provide your own Formatter to enable `absl::StrJoin()` to +// work with arbitrary types. // // The following is an example of a custom Formatter that simply uses -// `std::to_string()` to format an integer as a string. +// `std::to_string()` to format an integer as a std::string. // // struct MyFormatter { -// void operator()(string* out, int i) const { +// void operator()(std::string* out, int i) const { // out->append(std::to_string(i)); // } // }; @@ -85,7 +85,7 @@ inline namespace lts_2018_12_18 { // argument to `absl::StrJoin()`: // // std::vector<int> v = {1, 2, 3, 4}; -// string s = absl::StrJoin(v, "-", MyFormatter()); +// std::string s = absl::StrJoin(v, "-", MyFormatter()); // EXPECT_EQ("1-2-3-4", s); // // The following standard formatters are provided within this file: @@ -157,7 +157,7 @@ DereferenceFormatter() { // StrJoin() // ----------------------------------------------------------------------------- // -// Joins a range of elements and returns the result as a string. +// Joins a range of elements and returns the result as a std::string. // `absl::StrJoin()` takes a range, a separator string to use between the // elements joined, and an optional Formatter responsible for converting each // argument in the range to a string. @@ -168,22 +168,22 @@ DereferenceFormatter() { // Example 1: // // Joins a collection of strings. This pattern also works with a collection // // of `absl::string_view` or even `const char*`. -// std::vector<string> v = {"foo", "bar", "baz"}; -// string s = absl::StrJoin(v, "-"); +// std::vector<std::string> v = {"foo", "bar", "baz"}; +// std::string s = absl::StrJoin(v, "-"); // EXPECT_EQ("foo-bar-baz", s); // // Example 2: // // Joins the values in the given `std::initializer_list<>` specified using // // brace initialization. This pattern also works with an initializer_list // // of ints or `absl::string_view` -- any `AlphaNum`-compatible type. -// string s = absl::StrJoin({"foo", "bar", "baz"}, "-"); +// std::string s = absl::StrJoin({"foo", "bar", "baz"}, "-"); // EXPECT_EQ("foo-bar-baz", s); // // Example 3: // // Joins a collection of ints. This pattern also works with floats, // // doubles, int64s -- any `StrCat()`-compatible type. // std::vector<int> v = {1, 2, 3, -4}; -// string s = absl::StrJoin(v, "-"); +// std::string s = absl::StrJoin(v, "-"); // EXPECT_EQ("1-2-3--4", s); // // Example 4: @@ -194,7 +194,7 @@ DereferenceFormatter() { // // `std::vector<int*>`. // int x = 1, y = 2, z = 3; // std::vector<int*> v = {&x, &y, &z}; -// string s = absl::StrJoin(v, "-"); +// std::string s = absl::StrJoin(v, "-"); // EXPECT_EQ("1-2-3", s); // // Example 5: @@ -203,65 +203,65 @@ DereferenceFormatter() { // v.emplace_back(new int(1)); // v.emplace_back(new int(2)); // v.emplace_back(new int(3)); -// string s = absl::StrJoin(v, "-"); +// std::string s = absl::StrJoin(v, "-"); // EXPECT_EQ("1-2-3", s); // // Example 6: // // Joins a `std::map`, with each key-value pair separated by an equals // // sign. This pattern would also work with, say, a // // `std::vector<std::pair<>>`. -// std::map<string, int> m = { +// std::map<std::string, int> m = { // std::make_pair("a", 1), // std::make_pair("b", 2), // std::make_pair("c", 3)}; -// string s = absl::StrJoin(m, ",", absl::PairFormatter("=")); +// std::string s = absl::StrJoin(m, ",", absl::PairFormatter("=")); // EXPECT_EQ("a=1,b=2,c=3", s); // // Example 7: // // These examples show how `absl::StrJoin()` handles a few common edge // // cases: -// std::vector<string> v_empty; +// std::vector<std::string> v_empty; // EXPECT_EQ("", absl::StrJoin(v_empty, "-")); // -// std::vector<string> v_one_item = {"foo"}; +// std::vector<std::string> v_one_item = {"foo"}; // EXPECT_EQ("foo", absl::StrJoin(v_one_item, "-")); // -// std::vector<string> v_empty_string = {""}; +// std::vector<std::string> v_empty_string = {""}; // EXPECT_EQ("", absl::StrJoin(v_empty_string, "-")); // -// std::vector<string> v_one_item_empty_string = {"a", ""}; +// std::vector<std::string> v_one_item_empty_string = {"a", ""}; // EXPECT_EQ("a-", absl::StrJoin(v_one_item_empty_string, "-")); // -// std::vector<string> v_two_empty_string = {"", ""}; +// std::vector<std::string> v_two_empty_string = {"", ""}; // EXPECT_EQ("-", absl::StrJoin(v_two_empty_string, "-")); // // Example 8: // // Joins a `std::tuple<T...>` of heterogeneous types, converting each to -// // a string using the `absl::AlphaNum` class. -// string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-"); +// // a std::string using the `absl::AlphaNum` class. +// std::string s = absl::StrJoin(std::make_tuple(123, "abc", 0.456), "-"); // EXPECT_EQ("123-abc-0.456", s); template <typename Iterator, typename Formatter> std::string StrJoin(Iterator start, Iterator end, absl::string_view sep, - Formatter&& fmt) { + Formatter&& fmt) { return strings_internal::JoinAlgorithm(start, end, sep, fmt); } template <typename Range, typename Formatter> std::string StrJoin(const Range& range, absl::string_view separator, - Formatter&& fmt) { + Formatter&& fmt) { return strings_internal::JoinRange(range, separator, fmt); } template <typename T, typename Formatter> std::string StrJoin(std::initializer_list<T> il, absl::string_view separator, - Formatter&& fmt) { + Formatter&& fmt) { return strings_internal::JoinRange(il, separator, fmt); } template <typename... T, typename Formatter> std::string StrJoin(const std::tuple<T...>& value, absl::string_view separator, - Formatter&& fmt) { + Formatter&& fmt) { return strings_internal::JoinAlgorithm(value, separator, fmt); } @@ -276,16 +276,18 @@ std::string StrJoin(const Range& range, absl::string_view separator) { } template <typename T> -std::string StrJoin(std::initializer_list<T> il, absl::string_view separator) { +std::string StrJoin(std::initializer_list<T> il, + absl::string_view separator) { return strings_internal::JoinRange(il, separator); } template <typename... T> -std::string StrJoin(const std::tuple<T...>& value, absl::string_view separator) { +std::string StrJoin(const std::tuple<T...>& value, + absl::string_view separator) { return strings_internal::JoinAlgorithm(value, separator, AlphaNumFormatter()); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_STR_JOIN_H_ diff --git a/absl/strings/str_join_benchmark.cc b/absl/strings/str_join_benchmark.cc index 7fb0e497..d6f689ff 100644 --- a/absl/strings/str_join_benchmark.cc +++ b/absl/strings/str_join_benchmark.cc @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -58,7 +58,8 @@ void BM_Join2_KeysAndValues(benchmark::State& state) { const int string_len = state.range(0); const int num_pairs = state.range(1); const std::string s(string_len, 'x'); - const std::vector<std::pair<std::string, int>> v(num_pairs, std::make_pair(s, 42)); + const std::vector<std::pair<std::string, int>> v(num_pairs, + std::make_pair(s, 42)); for (auto _ : state) { std::string s = absl::StrJoin(v, ",", absl::PairFormatter("=")); benchmark::DoNotOptimize(s); diff --git a/absl/strings/str_join_test.cc b/absl/strings/str_join_test.cc index c941f9c8..921d9c2b 100644 --- a/absl/strings/str_join_test.cc +++ b/absl/strings/str_join_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -118,7 +118,7 @@ TEST(StrJoin, APIExamples) { { // A std::map, which is a collection of std::pair<>s. - std::map<std::string, int> m = { {"a", 1}, {"b", 2}, {"c", 3} }; + std::map<std::string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}}; EXPECT_EQ("a=1,b=2,c=3", absl::StrJoin(m, ",", absl::PairFormatter("="))); } @@ -140,7 +140,8 @@ TEST(StrJoin, APIExamples) { } { - // A range of 1 element gives a std::string with that element but no separator. + // A range of 1 element gives a std::string with that element but no + // separator. std::vector<std::string> v = {"foo"}; EXPECT_EQ("foo", absl::StrJoin(v, "-")); } @@ -173,9 +174,10 @@ TEST(StrJoin, APIExamples) { TEST(StrJoin, CustomFormatter) { std::vector<std::string> v{"One", "Two", "Three"}; { - std::string joined = absl::StrJoin(v, "", [](std::string* out, const std::string& in) { - absl::StrAppend(out, "(", in, ")"); - }); + std::string joined = + absl::StrJoin(v, "", [](std::string* out, const std::string& in) { + absl::StrAppend(out, "(", in, ")"); + }); EXPECT_EQ("(One)(Two)(Three)", joined); } { diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc index 72a0b584..3bd8bae1 100644 --- a/absl/strings/str_replace.cc +++ b/absl/strings/str_replace.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include "absl/strings/str_cat.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace strings_internal { using FixedMapping = @@ -69,13 +69,14 @@ int ApplySubstitutions( // aren't inlined. std::string StrReplaceAll(absl::string_view s, - strings_internal::FixedMapping replacements) { + strings_internal::FixedMapping replacements) { return StrReplaceAll<strings_internal::FixedMapping>(s, replacements); } -int StrReplaceAll(strings_internal::FixedMapping replacements, std::string* target) { +int StrReplaceAll(strings_internal::FixedMapping replacements, + std::string* target) { return StrReplaceAll<strings_internal::FixedMapping>(replacements, target); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h index a963f91e..cb995456 100644 --- a/absl/strings/str_replace.h +++ b/absl/strings/str_replace.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -29,12 +29,12 @@ // // Example: // -// string html_escaped = absl::StrReplaceAll(user_input, { -// {"&", "&"}, -// {"<", "<"}, -// {">", ">"}, -// {"\"", """}, -// {"'", "'"}}); +// std::string html_escaped = absl::StrReplaceAll(user_input, { +// {"&", "&"}, +// {"<", "<"}, +// {">", ">"}, +// {"\"", """}, +// {"'", "'"}}); #ifndef ABSL_STRINGS_STR_REPLACE_H_ #define ABSL_STRINGS_STR_REPLACE_H_ @@ -46,7 +46,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // StrReplaceAll() // @@ -59,10 +59,11 @@ inline namespace lts_2018_12_18 { // // Example: // -// string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!", -// {{"$count", absl::StrCat(5)}, -// {"$who", "Bob"}, -// {"#Noun", "Apples"}}); +// std::string s = absl::StrReplaceAll( +// "$who bought $count #Noun. Thanks $who!", +// {{"$count", absl::StrCat(5)}, +// {"$who", "Bob"}, +// {"#Noun", "Apples"}}); // EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s); ABSL_MUST_USE_RESULT std::string StrReplaceAll( absl::string_view s, @@ -79,20 +80,22 @@ ABSL_MUST_USE_RESULT std::string StrReplaceAll( // replacements["$who"] = "Bob"; // replacements["$count"] = "5"; // replacements["#Noun"] = "Apples"; -// string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!", -// replacements); +// std::string s = absl::StrReplaceAll( +// "$who bought $count #Noun. Thanks $who!", +// replacements); // EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s); // // // A std::vector of std::pair elements can be more efficient. -// std::vector<std::pair<const absl::string_view, string>> replacements; +// std::vector<std::pair<const absl::string_view, std::string>> replacements; // replacements.push_back({"&", "&"}); // replacements.push_back({"<", "<"}); // replacements.push_back({">", ">"}); -// string s = absl::StrReplaceAll("if (ptr < &foo)", +// std::string s = absl::StrReplaceAll("if (ptr < &foo)", // replacements); // EXPECT_EQ("if (ptr < &foo)", s); template <typename StrToStrMapping> -std::string StrReplaceAll(absl::string_view s, const StrToStrMapping& replacements); +std::string StrReplaceAll(absl::string_view s, + const StrToStrMapping& replacements); // Overload of `StrReplaceAll()` to replace character sequences within a given // output string *in place* with replacements provided within an initializer @@ -100,7 +103,7 @@ std::string StrReplaceAll(absl::string_view s, const StrToStrMapping& replacemen // // Example: // -// string s = std::string("$who bought $count #Noun. Thanks $who!"); +// std::string s = std::string("$who bought $count #Noun. Thanks $who!"); // int count; // count = absl::StrReplaceAll({{"$count", absl::StrCat(5)}, // {"$who", "Bob"}, @@ -118,7 +121,7 @@ int StrReplaceAll( // // Example: // -// string s = std::string("if (ptr < &foo)"); +// std::string s = std::string("if (ptr < &foo)"); // int count = absl::StrReplaceAll({{"&", "&"}, // {"<", "<"}, // {">", ">"}}, &s); @@ -188,7 +191,8 @@ int ApplySubstitutions(absl::string_view s, } // namespace strings_internal template <typename StrToStrMapping> -std::string StrReplaceAll(absl::string_view s, const StrToStrMapping& replacements) { +std::string StrReplaceAll(absl::string_view s, + const StrToStrMapping& replacements) { auto subs = strings_internal::FindSubstitutions(s, replacements); std::string result; result.reserve(s.size()); @@ -209,7 +213,7 @@ int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) { return substitutions; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_STR_REPLACE_H_ diff --git a/absl/strings/str_replace_benchmark.cc b/absl/strings/str_replace_benchmark.cc index 8386f2e6..95b2dc10 100644 --- a/absl/strings/str_replace_benchmark.cc +++ b/absl/strings/str_replace_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -54,7 +54,7 @@ void SetUpStrings() { size_t r = 0; big_string = new std::string(1000 * 1000, ' '); for (std::string phrase : {"the quick brown fox jumped over the lazy dogs", - "pack my box with the five dozen liquor jugs"}) { + "pack my box with the five dozen liquor jugs"}) { for (int i = 0; i < 10 * 1000; ++i) { r = r * 237 + 41; // not very random. memcpy(&(*big_string)[r % (big_string->size() - phrase.size())], @@ -108,11 +108,11 @@ void BM_StrReplaceAll(benchmark::State& state) { std::string src = *big_string; for (auto _ : state) { std::string dest = absl::StrReplaceAll(src, {{"the", "box"}, - {"brown", "quick"}, - {"jumped", "liquored"}, - {"dozen", "brown"}, - {"lazy", "pack"}, - {"liquor", "shakes"}}); + {"brown", "quick"}, + {"jumped", "liquored"}, + {"dozen", "brown"}, + {"lazy", "pack"}, + {"liquor", "shakes"}}); ABSL_RAW_CHECK(dest == *after_replacing_many, "not benchmarking intended behavior"); } diff --git a/absl/strings/str_replace_test.cc b/absl/strings/str_replace_test.cc index 5d003a22..1ca23aff 100644 --- a/absl/strings/str_replace_test.cc +++ b/absl/strings/str_replace_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -148,7 +148,7 @@ TEST(StrReplaceAll, ManyReplacementsInMap) { replacements["$count"] = "5"; replacements["#Noun"] = "Apples"; std::string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!", - replacements); + replacements); EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s); } diff --git a/absl/strings/str_split.cc b/absl/strings/str_split.cc index cd90425f..f1e03717 100644 --- a/absl/strings/str_split.cc +++ b/absl/strings/str_split.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,7 @@ #include "absl/strings/ascii.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { @@ -135,5 +135,5 @@ absl::string_view ByLength::Find(absl::string_view text, return absl::string_view(substr.data() + length_, 0); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h index 9483b30e..463ca008 100644 --- a/absl/strings/str_split.h +++ b/absl/strings/str_split.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -49,7 +49,7 @@ #include "absl/strings/strip.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { //------------------------------------------------------------------------------ // Delimiters @@ -72,23 +72,23 @@ inline namespace lts_2018_12_18 { // - `ByLength` // - `MaxSplits` // -// -// A Delimiter's Find() member function will be passed the input text that is to -// be split and the position to begin searching for the next delimiter in the -// input text. The returned absl::string_view should refer to the next -// occurrence (after pos) of the represented delimiter; this returned -// absl::string_view represents the next location where the input string should -// be broken. The returned absl::string_view may be zero-length if the Delimiter -// does not represent a part of the string (e.g., a fixed-length delimiter). If -// no delimiter is found in the given text, a zero-length absl::string_view -// referring to text.end() should be returned (e.g., -// absl::string_view(text.end(), 0)). It is important that the returned -// absl::string_view always be within the bounds of input text given as an +// A Delimiter's `Find()` member function will be passed an input `text` that is +// to be split and a position (`pos`) to begin searching for the next delimiter +// in `text`. The returned absl::string_view should refer to the next occurrence +// (after `pos`) of the represented delimiter; this returned absl::string_view +// represents the next location where the input `text` should be broken. +// +// The returned absl::string_view may be zero-length if the Delimiter does not +// represent a part of the string (e.g., a fixed-length delimiter). If no +// delimiter is found in the input `text`, a zero-length absl::string_view +// referring to `text.end()` should be returned (e.g., +// `text.substr(text.size())`). It is important that the returned +// absl::string_view always be within the bounds of the input `text` given as an // argument--it must not refer to a string that is physically located outside of // the given string. // // The following example is a simple Delimiter object that is created with a -// single char and will look for that char in the text passed to the Find() +// single char and will look for that char in the text passed to the `Find()` // function: // // struct SimpleDelimiter { @@ -97,9 +97,9 @@ inline namespace lts_2018_12_18 { // absl::string_view Find(absl::string_view text, size_t pos) { // auto found = text.find(c_, pos); // if (found == absl::string_view::npos) -// return absl::string_view(text.end(), 0); +// return text.substr(text.size()); // -// return absl::string_view(text, found, 1); +// return text.substr(found, 1); // } // }; @@ -132,8 +132,7 @@ class ByString { // ByChar // // A single character delimiter. `ByChar` is functionally equivalent to a -// 1-char string within a `ByString` delimiter, but slightly more -// efficient. +// 1-char string within a `ByString` delimiter, but slightly more efficient. // // Example: // @@ -414,10 +413,10 @@ struct SkipWhitespace { // // The `StrSplit()` function adapts the returned collection to the collection // specified by the caller (e.g. `std::vector` above). The returned collections -// may contain `string`, `absl::string_view` (in which case the original string -// being split must ensure that it outlives the collection), or any object that -// can be explicitly created from an `absl::string_view`. This behavior works -// for: +// may contain `std::string`, `absl::string_view` (in which case the original +// string being split must ensure that it outlives the collection), or any +// object that can be explicitly created from an `absl::string_view`. This +// behavior works for: // // 1) All standard STL containers including `std::vector`, `std::list`, // `std::deque`, `std::set`,`std::multiset`, 'std::map`, and `std::multimap` @@ -461,7 +460,7 @@ struct SkipWhitespace { // Example: // // // Stores first two split strings as the members in a std::pair. -// std::pair<string, string> p = absl::StrSplit("a,b,c", ','); +// std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ','); // // p.first == "a", p.second == "b" // "c" is omitted. // // The `StrSplit()` function can be used multiple times to perform more @@ -471,7 +470,7 @@ struct SkipWhitespace { // // // The input string "a=b=c,d=e,f=,g" becomes // // { "a" => "b=c", "d" => "e", "f" => "", "g" => "" } -// std::map<string, string> m; +// std::map<std::string, std::string> m; // for (absl::string_view sp : absl::StrSplit("a=b=c,d=e,f=,g", ',')) { // m.insert(absl::StrSplit(sp, absl::MaxSplits('=', 1))); // } @@ -508,7 +507,7 @@ StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d, std::move(text), DelimiterType(d), std::move(p)); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_STR_SPLIT_H_ diff --git a/absl/strings/str_split_benchmark.cc b/absl/strings/str_split_benchmark.cc index 0ac297c8..f38dfcfe 100644 --- a/absl/strings/str_split_benchmark.cc +++ b/absl/strings/str_split_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -44,6 +44,29 @@ void BM_Split2StringView(benchmark::State& state) { } BENCHMARK_RANGE(BM_Split2StringView, 0, 1 << 20); +static const absl::string_view kDelimiters = ";:,."; + +std::string MakeMultiDelimiterTestString(int desired_length) { + static const int kAverageValueLen = 25; + std::string test(desired_length * kAverageValueLen, 'x'); + for (int i = 0; i * kAverageValueLen < test.size(); ++i) { + // Cycle through a variety of delimiters. + test[i * kAverageValueLen] = kDelimiters[i % kDelimiters.size()]; + } + return test; +} + +// Measure StrSplit with ByAnyChar with four delimiters to choose from. +void BM_Split2StringViewByAnyChar(benchmark::State& state) { + std::string test = MakeMultiDelimiterTestString(state.range(0)); + for (auto _ : state) { + std::vector<absl::string_view> result = + absl::StrSplit(test, absl::ByAnyChar(kDelimiters)); + benchmark::DoNotOptimize(result); + } +} +BENCHMARK_RANGE(BM_Split2StringViewByAnyChar, 0, 1 << 20); + void BM_Split2StringViewLifted(benchmark::State& state) { std::string test = MakeTestString(state.range(0)); std::vector<absl::string_view> result; @@ -69,7 +92,8 @@ BENCHMARK_RANGE(BM_Split2String, 0, 1 << 20); void BM_Split2SplitStringUsing(benchmark::State& state) { std::string test = MakeTestString(state.range(0)); for (auto _ : state) { - std::vector<std::string> result = absl::StrSplit(test, ';', absl::SkipEmpty()); + std::vector<std::string> result = + absl::StrSplit(test, ';', absl::SkipEmpty()); benchmark::DoNotOptimize(result); } } diff --git a/absl/strings/str_split_test.cc b/absl/strings/str_split_test.cc index caa88277..02f27bc4 100644 --- a/absl/strings/str_split_test.cc +++ b/absl/strings/str_split_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -40,8 +40,8 @@ using ::testing::UnorderedElementsAre; TEST(Split, TraitsTest) { static_assert(!absl::strings_internal::SplitterIsConvertibleTo<int>::value, ""); - static_assert(!absl::strings_internal::SplitterIsConvertibleTo<std::string>::value, - ""); + static_assert( + !absl::strings_internal::SplitterIsConvertibleTo<std::string>::value, ""); static_assert(absl::strings_internal::SplitterIsConvertibleTo< std::vector<std::string>>::value, ""); @@ -71,8 +71,8 @@ TEST(Split, TraitsTest) { // namespaces just like callers will need to use. TEST(Split, APIExamples) { { - // Passes std::string delimiter. Assumes the default of Literal. - std::vector<std::string> v = absl::StrSplit("a,b,c", ','); + // Passes std::string delimiter. Assumes the default of ByString. + std::vector<std::string> v = absl::StrSplit("a,b,c", ","); // NOLINT EXPECT_THAT(v, ElementsAre("a", "b", "c")); // Equivalent to... @@ -97,17 +97,6 @@ TEST(Split, APIExamples) { } { - // Same as above, but using std::string - std::vector<std::string> v = absl::StrSplit("a,b,c", ','); - EXPECT_THAT(v, ElementsAre("a", "b", "c")); - - // Equivalent to... - using absl::ByChar; - v = absl::StrSplit("a,b,c", ByChar(',')); - EXPECT_THAT(v, ElementsAre("a", "b", "c")); - } - - { // Uses the Literal std::string "=>" as the delimiter. const std::vector<std::string> v = absl::StrSplit("a=>b=>c", "=>"); EXPECT_THAT(v, ElementsAre("a", "b", "c")); @@ -182,7 +171,8 @@ TEST(Split, APIExamples) { { // Uses the SkipWhitespace predicate. using absl::SkipWhitespace; - std::vector<std::string> v = absl::StrSplit(" a , ,,b,", ',', SkipWhitespace()); + std::vector<std::string> v = + absl::StrSplit(" a , ,,b,", ',', SkipWhitespace()); EXPECT_THAT(v, ElementsAre(" a ", "b")); } @@ -215,7 +205,8 @@ TEST(Split, APIExamples) { { // Results stored in a std::multimap. - std::multimap<std::string, std::string> m = absl::StrSplit("a,1,b,2,a,3", ','); + std::multimap<std::string, std::string> m = + absl::StrSplit("a,1,b,2,a,3", ','); EXPECT_EQ(3, m.size()); auto it = m.find("a"); EXPECT_EQ("1", it->second); @@ -271,7 +262,8 @@ TEST(SplitIterator, Basics) { EXPECT_EQ("a", *it); // tests dereference ++it; // tests preincrement EXPECT_NE(it, end); - EXPECT_EQ("b", std::string(it->data(), it->size())); // tests dereference as ptr + EXPECT_EQ("b", + std::string(it->data(), it->size())); // tests dereference as ptr it++; // tests postincrement EXPECT_EQ(it, end); } @@ -295,7 +287,8 @@ TEST(SplitIterator, Predicate) { EXPECT_EQ("a", *it); // tests dereference ++it; // tests preincrement -- "b" should be skipped here. EXPECT_NE(it, end); - EXPECT_EQ("c", std::string(it->data(), it->size())); // tests dereference as ptr + EXPECT_EQ("c", + std::string(it->data(), it->size())); // tests dereference as ptr it++; // tests postincrement EXPECT_EQ(it, end); } @@ -421,10 +414,13 @@ TEST(Splitter, ConversionOperator) { TestMapConversionOperator<std::map<std::string, std::string>>(splitter); TestMapConversionOperator< std::multimap<absl::string_view, absl::string_view>>(splitter); - TestMapConversionOperator<std::multimap<absl::string_view, std::string>>(splitter); - TestMapConversionOperator<std::multimap<std::string, absl::string_view>>(splitter); + TestMapConversionOperator<std::multimap<absl::string_view, std::string>>( + splitter); + TestMapConversionOperator<std::multimap<std::string, absl::string_view>>( + splitter); TestMapConversionOperator<std::multimap<std::string, std::string>>(splitter); - TestMapConversionOperator<std::unordered_map<std::string, std::string>>(splitter); + TestMapConversionOperator<std::unordered_map<std::string, std::string>>( + splitter); // Tests conversion to std::pair @@ -568,10 +564,9 @@ TEST(Split, AcceptsCertainTemporaries) { } TEST(Split, Temporary) { - // Use a std::string longer than the small-std::string-optimization length, so that when - // the temporary is destroyed, if the splitter keeps a reference to the - // std::string's contents, it'll reference freed memory instead of just dead - // on-stack memory. + // Use a std::string longer than the SSO length, so that when the temporary is + // destroyed, if the splitter keeps a reference to the std::string's contents, + // it'll reference freed memory instead of just dead on-stack memory. const char input[] = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u"; EXPECT_LT(sizeof(std::string), ABSL_ARRAYSIZE(input)) << "Input should be larger than fits on the stack."; @@ -647,6 +642,11 @@ TEST(Split, StringDelimiter) { } } +#if !defined(__cpp_char8_t) +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++2a-compat" +#endif TEST(Split, UTF8) { // Tests splitting utf8 strings and utf8 delimiters. std::string utf8_string = u8"\u03BA\u1F79\u03C3\u03BC\u03B5"; @@ -673,6 +673,10 @@ TEST(Split, UTF8) { EXPECT_THAT(v, ElementsAre("Foo", u8"h\u00E4llo", u8"th\u4E1Ere")); } } +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#endif // !defined(__cpp_char8_t) TEST(Split, EmptyStringDelimiter) { { @@ -782,7 +786,7 @@ static bool IsFoundAt(absl::string_view text, Delimiter d, int expected_pos) { } // -// Tests for Literal +// Tests for ByString // // Tests using any delimiter that represents a single comma. @@ -802,7 +806,7 @@ void TestComma(Delimiter d) { EXPECT_FALSE(IsFoundAt(";", d, -1)); } -TEST(Delimiter, Literal) { +TEST(Delimiter, ByString) { using absl::ByString; TestComma(ByString(",")); diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc index 3620ff44..9d241e51 100644 --- a/absl/strings/string_view.cc +++ b/absl/strings/string_view.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include "absl/strings/internal/memutil.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { void WritePadding(std::ostream& o, size_t pad) { @@ -78,18 +78,6 @@ std::ostream& operator<<(std::ostream& o, string_view piece) { return o; } -string_view::size_type string_view::copy(char* buf, size_type n, - size_type pos) const { - size_type ulen = length_; - assert(pos <= ulen); - size_type rlen = std::min(ulen - pos, n); - if (rlen > 0) { - const char* start = ptr_ + pos; - std::copy(start, start + rlen, buf); - } - return rlen; -} - string_view::size_type string_view::find(string_view s, size_type pos) const noexcept { if (empty() || pos > length_) { @@ -229,7 +217,7 @@ string_view::size_type string_view::find_last_not_of(char c, // member definitions that are required by the C++ standard, resulting in // LNK1169 "multiply defined" errors at link time. __declspec(selectany) asks // MSVC to choose only one definition for the symbol it decorates. See details -// at http://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx +// at https://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx #ifdef _MSC_VER #define ABSL_STRING_VIEW_SELECTANY __declspec(selectany) #else @@ -241,7 +229,7 @@ constexpr string_view::size_type string_view::npos; ABSL_STRING_VIEW_SELECTANY constexpr string_view::size_type string_view::kMaxSize; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_STD_STRING_VIEW diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index df6f1ae4..3a0a4609 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -32,12 +32,12 @@ #ifdef ABSL_HAVE_STD_STRING_VIEW -#include <string_view> +#include <string_view> // IWYU pragma: export namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { using std::string_view; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else // ABSL_HAVE_STD_STRING_VIEW @@ -52,10 +52,11 @@ using std::string_view; #include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" +#include "absl/base/optimization.h" #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // absl::string_view // @@ -104,7 +105,6 @@ inline namespace lts_2018_12_18 { // example, when splitting a string, `std::vector<absl::string_view>` is a // natural data type for the output. // -// // When constructed from a source which is nul-terminated, the `string_view` // itself will not include the nul-terminator unless a specific size (including // the nul) is passed to the constructor. As a result, common idioms that work @@ -176,19 +176,9 @@ class string_view { // Implicit constructor of a `string_view` from nul-terminated `str`. When // accepting possibly null strings, use `absl::NullSafeStringView(str)` // instead (see below). -#if ABSL_HAVE_BUILTIN(__builtin_strlen) || \ - (defined(__GNUC__) && !defined(__clang__)) - // GCC has __builtin_strlen according to - // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html, but - // ABSL_HAVE_BUILTIN doesn't detect that, so we use the extra checks above. - // __builtin_strlen is constexpr. constexpr string_view(const char* str) // NOLINT(runtime/explicit) : ptr_(str), - length_(CheckLengthInternal(str ? __builtin_strlen(str) : 0)) {} -#else - constexpr string_view(const char* str) // NOLINT(runtime/explicit) - : ptr_(str), length_(CheckLengthInternal(str ? strlen(str) : 0)) {} -#endif + length_(str ? CheckLengthInternal(StrlenInternal(str)) : 0) {} // Implicit constructor of a `string_view` from a `const char*` and length. constexpr string_view(const char* data, size_type len) @@ -280,7 +270,7 @@ class string_view { // Checks if the `string_view` is empty (refers to no characters). constexpr bool empty() const noexcept { return length_ == 0; } - // std::string:view::operator[] + // string_view::operator[] // // Returns the ith element of an `string_view` using the array operator. // Note that this operator does not perform any bounds checking. @@ -348,7 +338,17 @@ class string_view { // // Copies the contents of the `string_view` at offset `pos` and length `n` // into `buf`. - size_type copy(char* buf, size_type n, size_type pos = 0) const; + size_type copy(char* buf, size_type n, size_type pos = 0) const { + if (ABSL_PREDICT_FALSE(pos > length_)) { + base_internal::ThrowStdOutOfRange("absl::string_view::copy"); + } + size_type rlen = (std::min)(length_ - pos, n); + if (rlen > 0) { + const char* start = ptr_ + pos; + std::copy(start, start + rlen, buf); + } + return rlen; + } // string_view::substr() // @@ -358,7 +358,7 @@ class string_view { string_view substr(size_type pos, size_type n = npos) const { if (ABSL_PREDICT_FALSE(pos > length_)) base_internal::ThrowStdOutOfRange("absl::string_view::substr"); - n = std::min(n, length_ - pos); + n = (std::min)(n, length_ - pos); return string_view(ptr_ + pos, n); } @@ -371,7 +371,7 @@ class string_view { // on the respective sizes of the two `string_view`s to determine which is // smaller, equal, or greater. int compare(string_view x) const noexcept { - auto min_length = std::min(length_, x.length_); + auto min_length = (std::min)(length_, x.length_); if (min_length > 0) { int r = memcmp(ptr_, x.ptr_, min_length); if (r < 0) return -1; @@ -499,6 +499,24 @@ class string_view { return ABSL_ASSERT(len <= kMaxSize), len; } + static constexpr size_type StrlenInternal(const char* str) { +#if defined(_MSC_VER) && _MSC_VER >= 1910 && !defined(__clang__) + // MSVC 2017+ can evaluate this at compile-time. + const char* begin = str; + while (*str != '\0') ++str; + return str - begin; +#elif ABSL_HAVE_BUILTIN(__builtin_strlen) || \ + (defined(__GNUC__) && !defined(__clang__)) + // GCC has __builtin_strlen according to + // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Other-Builtins.html, but + // ABSL_HAVE_BUILTIN doesn't detect that, so we use the extra checks above. + // __builtin_strlen is constexpr. + return __builtin_strlen(str); +#else + return str ? strlen(str) : 0; +#endif + } + const char* ptr_; size_type length_; }; @@ -511,6 +529,7 @@ inline bool operator==(string_view x, string_view y) noexcept { if (len != y.size()) { return false; } + return x.data() == y.data() || len <= 0 || memcmp(x.data(), y.data(), len) == 0; } @@ -520,7 +539,7 @@ inline bool operator!=(string_view x, string_view y) noexcept { } inline bool operator<(string_view x, string_view y) noexcept { - auto min_size = std::min(x.size(), y.size()); + auto min_size = (std::min)(x.size(), y.size()); const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size); return (r < 0) || (r == 0 && x.size() < y.size()); } @@ -538,13 +557,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_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_STD_STRING_VIEW namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ClippedSubstr() // @@ -552,7 +571,7 @@ inline namespace lts_2018_12_18 { // Provided because std::string_view::substr throws if `pos > size()` inline string_view ClippedSubstr(string_view s, size_t pos, size_t n = string_view::npos) { - pos = std::min(pos, static_cast<size_t>(s.size())); + pos = (std::min)(pos, static_cast<size_t>(s.size())); return s.substr(pos, n); } @@ -565,7 +584,7 @@ inline string_view NullSafeStringView(const char* p) { return p ? string_view(p) : string_view(); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_STRING_VIEW_H_ diff --git a/absl/strings/string_view_benchmark.cc b/absl/strings/string_view_benchmark.cc index f4420320..46909cb0 100644 --- a/absl/strings/string_view_benchmark.cc +++ b/absl/strings/string_view_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc index ed34ed83..22d43536 100644 --- a/absl/strings/string_view_test.cc +++ b/absl/strings/string_view_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -284,9 +284,10 @@ TEST(StringViewTest, ComparisonOperatorsByCharacterPosition) { } #undef COMPARE -// Sadly, our users often confuse string::npos with absl::string_view::npos; -// So much so that we test here that they are the same. They need to -// both be unsigned, and both be the maximum-valued integer of their type. +// Sadly, our users often confuse std::string::npos with +// absl::string_view::npos; So much so that we test here that they are the same. +// They need to both be unsigned, and both be the maximum-valued integer of +// their type. template <typename T> struct is_type { @@ -368,6 +369,11 @@ TEST(StringViewTest, STL1) { EXPECT_EQ(buf[1], c[1]); EXPECT_EQ(buf[2], c[2]); EXPECT_EQ(buf[3], a[3]); +#ifdef ABSL_HAVE_EXCEPTIONS + EXPECT_THROW(a.copy(buf, 1, 27), std::out_of_range); +#else + EXPECT_DEATH(a.copy(buf, 1, 27), "absl::string_view::copy"); +#endif } // Separated from STL1() because some compilers produce an overly @@ -755,7 +761,6 @@ TEST(StringViewTest, Remove) { std::string s1("123"); s1 += '\0'; s1 += "456"; - absl::string_view b(s1); absl::string_view e; std::string s2; @@ -941,6 +946,11 @@ TEST(StringViewTest, ConstexprCompiles) { #error GCC/clang should have constexpr string_view. #endif +// MSVC 2017+ should be able to construct a constexpr string_view from a cstr. +#if defined(_MSC_VER) && _MSC_VER >= 1910 +#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1 +#endif + #endif // ABSL_HAVE_STD_STRING_VIEW #ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR @@ -995,8 +1005,8 @@ TEST(StringViewTest, ConstexprCompiles) { TEST(StringViewTest, Noexcept) { EXPECT_TRUE((std::is_nothrow_constructible<absl::string_view, const std::string&>::value)); - EXPECT_TRUE( - (std::is_nothrow_constructible<absl::string_view, const std::string&>::value)); + EXPECT_TRUE((std::is_nothrow_constructible<absl::string_view, + const std::string&>::value)); EXPECT_TRUE(std::is_nothrow_constructible<absl::string_view>::value); constexpr absl::string_view sp; EXPECT_TRUE(noexcept(sp.begin())); diff --git a/absl/strings/strip.h b/absl/strings/strip.h index 059f57b7..8e2ae422 100644 --- a/absl/strings/strip.h +++ b/absl/strings/strip.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,7 +30,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ConsumePrefix() // @@ -85,7 +85,7 @@ ABSL_MUST_USE_RESULT inline absl::string_view StripSuffix( return str; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_STRIP_H_ diff --git a/absl/strings/strip_test.cc b/absl/strings/strip_test.cc index 40c4c607..e4e00cb6 100644 --- a/absl/strings/strip_test.cc +++ b/absl/strings/strip_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,9 +27,6 @@ namespace { -using testing::ElementsAre; -using testing::IsEmpty; - TEST(Strip, ConsumePrefixOneChar) { absl::string_view input("abc"); EXPECT_TRUE(absl::ConsumePrefix(&input, "a")); diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc index b70e70e8..6bc9641d 100644 --- a/absl/strings/substitute.cc +++ b/absl/strings/substitute.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/strings/string_view.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace substitute_internal { void SubstituteAndAppendArray(std::string* output, absl::string_view format, @@ -168,5 +168,5 @@ Arg::Arg(Dec dec) { } } // namespace substitute_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index 43d73ad7..3ba7f4d3 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -35,19 +35,21 @@ // and single digit positional ids to indicate which substitution arguments to // use at that location within the format string. // +// A '$$' sequence in the format string causes a literal '$' character to be +// output. +// // Example 1: -// string s = Substitute("$1 purchased $0 $2. Thanks $1!", -// 5, "Bob", "Apples"); -// EXPECT_EQ("Bob purchased 5 Apples. Thanks Bob!", s); +// std::string s = Substitute("$1 purchased $0 $2 for $$10. Thanks $1!", +// 5, "Bob", "Apples"); +// EXPECT_EQ("Bob purchased 5 Apples for $10. Thanks Bob!", s); // // Example 2: -// string s = "Hi. "; +// std::string s = "Hi. "; // SubstituteAndAppend(&s, "My name is $0 and I am $1 years old.", "Bob", 5); // EXPECT_EQ("Hi. My name is Bob and I am 5 years old.", s); // -// // Supported types: -// * absl::string_view, string, const char* (null is equivalent to "") +// * absl::string_view, std::string, const char* (null is equivalent to "") // * int32_t, int64_t, uint32_t, uint64 // * float, double // * bool (Printed as "true" or "false") @@ -70,6 +72,8 @@ #include <cstring> #include <string> +#include <type_traits> +#include <vector> #include "absl/base/macros.h" #include "absl/base/port.h" @@ -82,7 +86,7 @@ #include "absl/strings/strip.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace substitute_internal { // Arg @@ -153,6 +157,17 @@ class Arg { Arg(Hex hex); // NOLINT(runtime/explicit) Arg(Dec dec); // NOLINT(runtime/explicit) + // vector<bool>::reference and const_reference require special help to + // convert to `AlphaNum` because it requires two user defined conversions. + template <typename T, + absl::enable_if_t< + std::is_class<T>::value && + (std::is_same<T, std::vector<bool>::reference>::value || + std::is_same<T, std::vector<bool>::const_reference>::value)>* = + nullptr> + Arg(T value) // NOLINT(google-explicit-constructor) + : Arg(static_cast<bool>(value)) {} + // `void*` values, with the exception of `char*`, are printed as // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed. Arg(const void* value); // NOLINT(runtime/explicit) @@ -457,7 +472,7 @@ void SubstituteAndAppend( // Example: // template <typename... Args> // void VarMsg(absl::string_view format, const Args&... args) { -// string s = absl::Substitute(format, args...); +// std::string s = absl::Substitute(format, args...); ABSL_MUST_USE_RESULT inline std::string Substitute(absl::string_view format) { std::string result; @@ -575,70 +590,70 @@ std::string Substitute(const char* format, const substitute_internal::Arg& a0) "contains one of $1-$9"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1) + const substitute_internal::Arg& a1) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 3, "There were 2 substitution arguments given, but " "this format std::string is either missing its $0/$1, or " "contains one of $2-$9"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2) + const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 7, "There were 3 substitution arguments given, but " "this format std::string is either missing its $0/$1/$2, or " "contains one of $3-$9"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3) + const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, + const substitute_internal::Arg& a3) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 15, "There were 4 substitution arguments given, but " "this format std::string is either missing its $0-$3, or " "contains one of $4-$9"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, - const substitute_internal::Arg& a4) + const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, + const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 31, "There were 5 substitution arguments given, but " "this format std::string is either missing its $0-$4, or " "contains one of $5-$9"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, - const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5) + const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, + const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, + const substitute_internal::Arg& a5) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 63, "There were 6 substitution arguments given, but " "this format std::string is either missing its $0-$5, or " "contains one of $6-$9"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, - const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5, - const substitute_internal::Arg& a6) + const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, + const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, + const substitute_internal::Arg& a5, + const substitute_internal::Arg& a6) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 127, "There were 7 substitution arguments given, but " "this format std::string is either missing its $0-$6, or " "contains one of $7-$9"); std::string Substitute(const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, - const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5, - const substitute_internal::Arg& a6, - const substitute_internal::Arg& a7) + const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, + const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, + const substitute_internal::Arg& a5, + const substitute_internal::Arg& a6, + const substitute_internal::Arg& a7) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 255, "There were 8 substitution arguments given, but " "this format std::string is either missing its $0-$7, or " @@ -667,7 +682,7 @@ std::string Substitute( "format std::string doesn't contain all of $0 through $9"); #endif // ABSL_BAD_CALL_IF -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_STRINGS_SUBSTITUTE_H_ diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc index 144df01e..e27abb17 100644 --- a/absl/strings/substitute_test.cc +++ b/absl/strings/substitute_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,6 +15,7 @@ #include "absl/strings/substitute.h" #include <cstdint> +#include <vector> #include "gtest/gtest.h" #include "absl/strings/str_cat.h" @@ -172,6 +173,17 @@ TEST(SubstituteTest, SubstituteAndAppend) { EXPECT_EQ("a b c d e f g h i j", str); } +TEST(SubstituteTest, VectorBoolRef) { + std::vector<bool> v = {true, false}; + const auto& cv = v; + EXPECT_EQ("true false true false", + absl::Substitute("$0 $1 $2 $3", v[0], v[1], cv[0], cv[1])); + + std::string str = "Logic be like: "; + absl::SubstituteAndAppend(&str, "$0 $1 $2 $3", v[0], v[1], cv[0], cv[1]); + EXPECT_EQ("Logic be like: true false true false", str); +} + #ifdef GTEST_HAS_DEATH_TEST TEST(SubstituteDeathTest, SubstituteDeath) { diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index e63b1d16..fca8cb69 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,9 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -34,6 +35,7 @@ cc_library( "internal/graphcycles.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -69,9 +71,14 @@ cc_library( "notification.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = select({ + "//absl:windows": [], + "//conditions:default": ["-pthread"], + }) + ABSL_DEFAULT_LINKOPTS, deps = [ ":graphcycles_internal", "//absl/base", + "//absl/base:atomic_hook", "//absl/base:base_internal", "//absl/base:config", "//absl/base:core_headers", @@ -88,9 +95,7 @@ cc_test( size = "small", srcs = ["barrier_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_wasm", - ], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":synchronization", "//absl/time", @@ -103,9 +108,7 @@ cc_test( size = "small", srcs = ["blocking_counter_test.cc"], copts = ABSL_TEST_COPTS, - tags = [ - "no_test_wasm", - ], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":synchronization", "//absl/time", @@ -118,6 +121,7 @@ cc_test( size = "medium", srcs = ["internal/graphcycles_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":graphcycles_internal", "//absl/base", @@ -130,6 +134,7 @@ cc_test( name = "graphcycles_benchmark", srcs = ["internal/graphcycles_benchmark.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = [ "benchmark", ], @@ -144,6 +149,7 @@ cc_library( name = "thread_pool", testonly = 1, hdrs = ["internal/thread_pool.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl:__subpackages__", ], @@ -158,6 +164,7 @@ cc_test( size = "large", srcs = ["mutex_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, shard_count = 25, deps = [ ":synchronization", @@ -174,7 +181,8 @@ cc_library( name = "mutex_benchmark_common", testonly = 1, srcs = ["mutex_benchmark.cc"], - copts = ABSL_DEFAULT_COPTS, + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/synchronization:__pkg__", ], @@ -182,7 +190,6 @@ cc_library( ":synchronization", ":thread_pool", "//absl/base", - "//absl/base:base_internal", "@com_github_google_benchmark//:benchmark_main", ], alwayslink = 1, @@ -192,6 +199,7 @@ cc_binary( name = "mutex_benchmark", testonly = 1, copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ ":mutex_benchmark_common", @@ -203,6 +211,7 @@ cc_test( size = "small", srcs = ["notification_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":synchronization", "//absl/time", @@ -215,6 +224,7 @@ cc_library( testonly = 1, srcs = ["internal/per_thread_sem_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":synchronization", "//absl/base", @@ -229,7 +239,7 @@ cc_test( name = "per_thread_sem_test", size = "medium", copts = ABSL_TEST_COPTS, - tags = ["no_test_wasm"], + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":per_thread_sem_test_common", ":synchronization", @@ -246,10 +256,7 @@ cc_test( "lifetime_test.cc", ], copts = ABSL_TEST_COPTS, - linkopts = select({ - "//absl:windows": [], - "//conditions:default": ["-pthread"], - }), + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["no_test_ios_x86_64"], deps = [ ":synchronization", diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt index de0d7b7d..4b708823 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,142 +14,185 @@ # limitations under the License. # -list(APPEND SYNCHRONIZATION_PUBLIC_HEADERS - "barrier.h" - "blocking_counter.h" - "mutex.h" - "notification.h" +absl_cc_library( + NAME + graphcycles_internal + HDRS + "internal/graphcycles.h" + SRCS + "internal/graphcycles.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::base_internal + absl::core_headers + absl::malloc_internal ) - -list(APPEND SYNCHRONIZATION_INTERNAL_HEADERS - "internal/create_thread_identity.h" - "internal/graphcycles.h" - "internal/kernel_timeout.h" - "internal/per_thread_sem.h" - "internal/thread_pool.h" - "internal/waiter.h" -) - - - -# synchronization library -list(APPEND SYNCHRONIZATION_SRC - "barrier.cc" - "blocking_counter.cc" - "internal/create_thread_identity.cc" - "internal/per_thread_sem.cc" - "internal/waiter.cc" - "internal/graphcycles.cc" - "notification.cc" - "mutex.cc" -) - -set(SYNCHRONIZATION_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::symbolize absl::time) - -absl_library( - TARGET - absl_synchronization - SOURCES - ${SYNCHRONIZATION_SRC} - PUBLIC_LIBRARIES - ${SYNCHRONIZATION_PUBLIC_LIBRARIES} - EXPORT_NAME +absl_cc_library( + NAME synchronization + HDRS + "barrier.h" + "blocking_counter.h" + "internal/create_thread_identity.h" + "internal/kernel_timeout.h" + "internal/mutex_nonprod.inc" + "internal/per_thread_sem.h" + "internal/waiter.h" + "mutex.h" + "notification.h" + SRCS + "barrier.cc" + "blocking_counter.cc" + "internal/create_thread_identity.cc" + "internal/per_thread_sem.cc" + "internal/waiter.cc" + "notification.cc" + "mutex.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::graphcycles_internal + absl::atomic_hook + absl::base + absl::base_internal + absl::config + absl::core_headers + absl::dynamic_annotations + absl::malloc_internal + absl::stacktrace + absl::symbolize + absl::time + Threads::Threads + PUBLIC ) - -# -## TESTS -# - - -# test barrier_test -set(BARRIER_TEST_SRC "barrier_test.cc") -set(BARRIER_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME barrier_test - SOURCES - ${BARRIER_TEST_SRC} - PUBLIC_LIBRARIES - ${BARRIER_TEST_PUBLIC_LIBRARIES} + SRCS + "barrier_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::time + gmock_main ) - -# test blocking_counter_test -set(BLOCKING_COUNTER_TEST_SRC "blocking_counter_test.cc") -set(BLOCKING_COUNTER_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME blocking_counter_test - SOURCES - ${BLOCKING_COUNTER_TEST_SRC} - PUBLIC_LIBRARIES - ${BLOCKING_COUNTER_TEST_PUBLIC_LIBRARIES} + SRCS + "blocking_counter_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::time + gmock_main ) - -# test graphcycles_test -set(GRAPHCYCLES_TEST_SRC "internal/graphcycles_test.cc") -set(GRAPHCYCLES_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME graphcycles_test - SOURCES - ${GRAPHCYCLES_TEST_SRC} - PUBLIC_LIBRARIES - ${GRAPHCYCLES_TEST_PUBLIC_LIBRARIES} + SRCS + "internal/graphcycles_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::graphcycles_internal + absl::base + absl::core_headers + gmock_main ) +absl_cc_library( + NAME + thread_pool + HDRS + "internal/thread_pool.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::synchronization + absl::core_headers + TESTONLY +) -# test mutex_test -set(MUTEX_TEST_SRC "mutex_test.cc") -set(MUTEX_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME mutex_test - SOURCES - ${MUTEX_TEST_SRC} - PUBLIC_LIBRARIES - ${MUTEX_TEST_PUBLIC_LIBRARIES} + SRCS + "mutex_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::thread_pool + absl::base + absl::core_headers + absl::memory + absl::time + gmock_main ) - -# test notification_test -set(NOTIFICATION_TEST_SRC "notification_test.cc") -set(NOTIFICATION_TEST_PUBLIC_LIBRARIES absl::synchronization) - -absl_test( - TARGET +absl_cc_test( + NAME notification_test - SOURCES - ${NOTIFICATION_TEST_SRC} - PUBLIC_LIBRARIES - ${NOTIFICATION_TEST_PUBLIC_LIBRARIES} + SRCS + "notification_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::time + gmock_main ) - -# test per_thread_sem_test_common -set(PER_THREAD_SEM_TEST_COMMON_SRC "internal/per_thread_sem_test.cc") -set(PER_THREAD_SEM_TEST_COMMON_PUBLIC_LIBRARIES absl::synchronization absl::strings) - -absl_test( - TARGET +absl_cc_library( + NAME per_thread_sem_test_common - SOURCES - ${PER_THREAD_SEM_TEST_COMMON_SRC} - PUBLIC_LIBRARIES - ${PER_THREAD_SEM_TEST_COMMON_PUBLIC_LIBRARIES} + SRCS + "internal/per_thread_sem_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::base + absl::strings + absl::time + gmock + TESTONLY ) +absl_cc_test( + NAME + per_thread_sem_test + SRCS + "internal/per_thread_sem_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::per_thread_sem_test_common + absl::synchronization + absl::base + absl::strings + absl::time + gmock_main +) - - - - - +absl_cc_test( + NAME + lifetime_test + SRCS + "lifetime_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::synchronization + absl::base + absl::core_headers +) diff --git a/absl/synchronization/barrier.cc b/absl/synchronization/barrier.cc index ee66c240..72089c52 100644 --- a/absl/synchronization/barrier.cc +++ b/absl/synchronization/barrier.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Return whether int *arg is zero. static bool IsZero(void *arg) { @@ -48,5 +48,5 @@ bool Barrier::Block() { return this->num_to_exit_ == 0; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/barrier.h b/absl/synchronization/barrier.h index 77ac3602..53d5ca26 100644 --- a/absl/synchronization/barrier.h +++ b/absl/synchronization/barrier.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Barrier // @@ -74,6 +74,6 @@ class Barrier { int num_to_exit_ GUARDED_BY(lock_); }; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_SYNCHRONIZATION_BARRIER_H_ diff --git a/absl/synchronization/barrier_test.cc b/absl/synchronization/barrier_test.cc index d6cababd..bfc6cb18 100644 --- a/absl/synchronization/barrier_test.cc +++ b/absl/synchronization/barrier_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/synchronization/blocking_counter.cc b/absl/synchronization/blocking_counter.cc index 82d889a9..c6968973 100644 --- a/absl/synchronization/blocking_counter.cc +++ b/absl/synchronization/blocking_counter.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Return whether int *arg is zero. static bool IsZero(void *arg) { @@ -53,5 +53,5 @@ void BlockingCounter::Wait() { // after we return from this method. } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/blocking_counter.h b/absl/synchronization/blocking_counter.h index 554e396c..5dab5a94 100644 --- a/absl/synchronization/blocking_counter.h +++ b/absl/synchronization/blocking_counter.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // BlockingCounter // @@ -93,7 +93,7 @@ class BlockingCounter { int num_waiting_ GUARDED_BY(lock_); }; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 b3b55dd7..62d98738 100644 --- a/absl/synchronization/blocking_counter_test.cc +++ b/absl/synchronization/blocking_counter_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { void PauseAndDecreaseCounter(BlockingCounter* counter, int* done) { @@ -64,5 +64,5 @@ TEST(BlockingCounterTest, BasicFunctionality) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc index f27f16da..65f6d8fc 100644 --- a/absl/synchronization/internal/create_thread_identity.cc +++ b/absl/synchronization/internal/create_thread_identity.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,12 +27,13 @@ #include "absl/synchronization/internal/per_thread_sem.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { // ThreadIdentity storage is persistent, we maintain a free-list of previously // released ThreadIdentity objects. -static base_internal::SpinLock freelist_lock(base_internal::kLinkerInitialized); +static base_internal::SpinLock freelist_lock( + base_internal::kLinkerInitialized); static base_internal::ThreadIdentity* thread_identity_freelist; // A per-thread destructor for reclaiming associated ThreadIdentity objects. @@ -68,6 +69,30 @@ static intptr_t RoundUp(intptr_t addr, intptr_t align) { return (addr + align - 1) & ~(align - 1); } +static void ResetThreadIdentity(base_internal::ThreadIdentity* identity) { + base_internal::PerThreadSynch* pts = &identity->per_thread_synch; + pts->next = nullptr; + pts->skip = nullptr; + pts->may_skip = false; + pts->waitp = nullptr; + pts->suppress_fatal_errors = false; + pts->readers = 0; + pts->priority = 0; + pts->next_priority_read_cycles = 0; + pts->state.store(base_internal::PerThreadSynch::State::kAvailable, + std::memory_order_relaxed); + pts->maybe_unlocking = false; + pts->wake = false; + pts->cond_waiter = false; + pts->all_locks = nullptr; + identity->waiter_state = {}; + identity->blocked_count_ptr = nullptr; + identity->ticker.store(0, std::memory_order_relaxed); + identity->wait_start.store(0, std::memory_order_relaxed); + identity->is_idle.store(false, std::memory_order_relaxed); + identity->next = nullptr; +} + static base_internal::ThreadIdentity* NewThreadIdentity() { base_internal::ThreadIdentity* identity = nullptr; @@ -91,7 +116,7 @@ static base_internal::ThreadIdentity* NewThreadIdentity() { RoundUp(reinterpret_cast<intptr_t>(allocation), base_internal::PerThreadSynch::kAlignment)); } - memset(identity, 0, sizeof(*identity)); + ResetThreadIdentity(identity); return identity; } @@ -108,7 +133,7 @@ base_internal::ThreadIdentity* CreateThreadIdentity() { } } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_LOW_LEVEL_ALLOC_MISSING diff --git a/absl/synchronization/internal/create_thread_identity.h b/absl/synchronization/internal/create_thread_identity.h index 1132d516..d743cc3b 100644 --- a/absl/synchronization/internal/create_thread_identity.h +++ b/absl/synchronization/internal/create_thread_identity.h @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -29,7 +29,7 @@ #include "absl/base/port.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { // Allocates and attaches a ThreadIdentity object for the calling thread. @@ -50,6 +50,7 @@ inline base_internal::ThreadIdentity* GetOrCreateCurrentThreadIdentity() { } } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 139be0f5..f4fbeadd 100644 --- a/absl/synchronization/internal/graphcycles.cc +++ b/absl/synchronization/internal/graphcycles.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -44,7 +44,7 @@ // Do not use STL. This module does not use standard memory allocation. namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { namespace { @@ -691,7 +691,7 @@ int GraphCycles::GetStackTrace(GraphId id, void*** ptr) { } } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_LOW_LEVEL_ALLOC_MISSING diff --git a/absl/synchronization/internal/graphcycles.h b/absl/synchronization/internal/graphcycles.h index 6609ea06..208527c3 100644 --- a/absl/synchronization/internal/graphcycles.h +++ b/absl/synchronization/internal/graphcycles.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -41,7 +41,7 @@ #include <cstdint> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { // Opaque identifier for a graph node. @@ -133,7 +133,7 @@ class GraphCycles { }; } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif diff --git a/absl/synchronization/internal/graphcycles_benchmark.cc b/absl/synchronization/internal/graphcycles_benchmark.cc index a239c25c..54823e0b 100644 --- a/absl/synchronization/internal/graphcycles_benchmark.cc +++ b/absl/synchronization/internal/graphcycles_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/synchronization/internal/graphcycles_test.cc b/absl/synchronization/internal/graphcycles_test.cc index 4dc2bdc5..fca86219 100644 --- a/absl/synchronization/internal/graphcycles_test.cc +++ b/absl/synchronization/internal/graphcycles_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ #include "absl/base/macros.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { // We emulate a GraphCycles object with a node vector and an edge vector. @@ -460,5 +460,5 @@ TEST_F(GraphCyclesTest, ManyEdges) { } } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h index 34ae94ec..e0f01e06 100644 --- a/absl/synchronization/internal/kernel_timeout.h +++ b/absl/synchronization/internal/kernel_timeout.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -34,7 +34,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { class Futex; @@ -54,6 +54,7 @@ class KernelTimeout { // We explicitly do not support other custom formats: timespec, int64_t nanos. // Unify on this and absl::Time, please. + bool has_timeout() const { return ns_ != 0; } private: @@ -101,8 +102,8 @@ class KernelTimeout { if (n < 0) n = 0; struct timespec abstime; - int64_t seconds = std::min(n / kNanosPerSecond, - int64_t{(std::numeric_limits<time_t>::max)()}); + int64_t seconds = (std::min)(n / kNanosPerSecond, + int64_t{(std::numeric_limits<time_t>::max)()}); abstime.tv_sec = static_cast<time_t>(seconds); abstime.tv_nsec = static_cast<decltype(abstime.tv_nsec)>(n % kNanosPerSecond); @@ -148,6 +149,7 @@ class KernelTimeout { }; } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 4b0b8bcd..aa1ed83b 100644 --- a/absl/synchronization/internal/mutex_nonprod.cc +++ b/absl/synchronization/internal/mutex_nonprod.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -31,7 +31,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { namespace { @@ -316,5 +316,5 @@ bool Condition::Eval() const { void RegisterSymbolizer(bool (*)(const void*, char*, int)) {} -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/internal/mutex_nonprod.inc b/absl/synchronization/internal/mutex_nonprod.inc index 0ae4c0ea..ac10879b 100644 --- a/absl/synchronization/internal/mutex_nonprod.inc +++ b/absl/synchronization/internal/mutex_nonprod.inc @@ -36,7 +36,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { class Condition; namespace synchronization_internal { @@ -215,6 +215,9 @@ class SynchronizationStorage { // stack) should use this constructor. explicit SynchronizationStorage(base_internal::LinkerInitialized) {} + constexpr explicit SynchronizationStorage(absl::ConstInitType) + : is_dynamic_(false), once_(), space_{{0}} {} + SynchronizationStorage(SynchronizationStorage&) = delete; SynchronizationStorage& operator=(SynchronizationStorage&) = delete; @@ -254,5 +257,5 @@ class SynchronizationStorage { }; } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc index 9de2d136..284a5df4 100644 --- a/absl/synchronization/internal/per_thread_sem.cc +++ b/absl/synchronization/internal/per_thread_sem.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ #include "absl/synchronization/internal/waiter.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { void PerThreadSem::SetThreadBlockedCounter(std::atomic<int> *counter) { @@ -59,7 +59,7 @@ void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) { } } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl extern "C" { @@ -91,6 +91,7 @@ ABSL_ATTRIBUTE_WEAK bool AbslInternalPerThreadSemWait( if (identity->blocked_count_ptr != nullptr) { identity->blocked_count_ptr->fetch_sub(1, std::memory_order_relaxed); } + identity->is_idle.store(false, std::memory_order_relaxed); identity->wait_start.store(0, std::memory_order_relaxed); return !timeout; diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h index 6efd5951..5bb0978b 100644 --- a/absl/synchronization/internal/per_thread_sem.h +++ b/absl/synchronization/internal/per_thread_sem.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -32,7 +32,7 @@ #include "absl/synchronization/internal/kernel_timeout.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { class Mutex; @@ -81,7 +81,7 @@ class PerThreadSem { }; } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // In some build configurations we pass --detect-odr-violations to the @@ -106,4 +106,5 @@ bool absl::synchronization_internal::PerThreadSem::Wait( absl::synchronization_internal::KernelTimeout t) { return AbslInternalPerThreadSemWait(t); } + #endif // ABSL_SYNCHRONIZATION_INTERNAL_PER_THREAD_SEM_H_ diff --git a/absl/synchronization/internal/per_thread_sem_test.cc b/absl/synchronization/internal/per_thread_sem_test.cc index 18b2458b..93bc4244 100644 --- a/absl/synchronization/internal/per_thread_sem_test.cc +++ b/absl/synchronization/internal/per_thread_sem_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,7 +33,7 @@ // primitives which might use PerThreadSem, most notably absl::Mutex. namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { class SimpleSemaphore { @@ -115,10 +115,9 @@ class PerThreadSemTest : public testing::Test { min_cycles = std::min(min_cycles, cycles); total_cycles += cycles; } - std::string out = - StrCat(msg, "min cycle count=", min_cycles, " avg cycle count=", - absl::SixDigits(static_cast<double>(total_cycles) / - kNumIterations)); + std::string out = StrCat( + msg, "min cycle count=", min_cycles, " avg cycle count=", + absl::SixDigits(static_cast<double>(total_cycles) / kNumIterations)); printf("%s\n", out.c_str()); partner_thread.join(); @@ -153,12 +152,16 @@ TEST_F(PerThreadSemTest, WithTimeout) { } TEST_F(PerThreadSemTest, Timeouts) { - absl::Time timeout = absl::Now() + absl::Milliseconds(50); + const absl::Duration delay = absl::Milliseconds(50); + const absl::Time start = absl::Now(); + EXPECT_FALSE(Wait(start + delay)); + const absl::Duration elapsed = absl::Now() - start; // Allow for a slight early return, to account for quality of implementation // issues on various platforms. const absl::Duration slop = absl::Microseconds(200); - EXPECT_FALSE(Wait(timeout)); - EXPECT_LE(timeout, absl::Now() + slop); + EXPECT_LE(delay - slop, elapsed) + << "Wait returned " << delay - elapsed + << " early (with " << slop << " slop), start time was " << start; absl::Time negative_timeout = absl::UnixEpoch() - absl::Milliseconds(100); EXPECT_FALSE(Wait(negative_timeout)); @@ -173,5 +176,5 @@ TEST_F(PerThreadSemTest, Timeouts) { } // namespace } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/internal/thread_pool.h b/absl/synchronization/internal/thread_pool.h index 66c7546b..8941be68 100644 --- a/absl/synchronization/internal/thread_pool.h +++ b/absl/synchronization/internal/thread_pool.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,6 +16,7 @@ #define ABSL_SYNCHRONIZATION_INTERNAL_THREAD_POOL_H_ #include <cassert> +#include <cstddef> #include <functional> #include <queue> #include <thread> // NOLINT(build/c++11) @@ -25,7 +26,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { // A simple ThreadPool implementation for tests. @@ -43,7 +44,7 @@ class ThreadPool { ~ThreadPool() { { absl::MutexLock l(&mu_); - for (int i = 0; i < threads_.size(); ++i) { + for (size_t i = 0; i < threads_.size(); i++) { queue_.push(nullptr); // Shutdown signal. } } @@ -86,7 +87,7 @@ class ThreadPool { }; } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_THREAD_POOL_H_ diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index 76fdd861..17c6a506 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -40,13 +40,16 @@ #include <atomic> #include <cassert> #include <cstdint> +#include <new> +#include <type_traits> + #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/thread_identity.h" #include "absl/base/optimization.h" #include "absl/synchronization/internal/kernel_timeout.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { static void MaybeBecomeIdle() { @@ -82,6 +85,7 @@ static void MaybeBecomeIdle() { #define FUTEX_BITSET_MATCH_ANY 0xFFFFFFFF #endif #endif + class Futex { public: static int WaitUntil(std::atomic<int32_t> *v, int32_t val, @@ -327,6 +331,43 @@ void Waiter::Poke() { #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_WIN32 +class Waiter::WinHelper { + public: + static SRWLOCK *GetLock(Waiter *w) { + return reinterpret_cast<SRWLOCK *>(&w->mu_storage_); + } + + static CONDITION_VARIABLE *GetCond(Waiter *w) { + return reinterpret_cast<CONDITION_VARIABLE *>(&w->cv_storage_); + } + + static_assert(sizeof(SRWLOCK) == sizeof(Waiter::SRWLockStorage), + "SRWLockStorage does not have the same size as SRWLOCK"); + static_assert( + alignof(SRWLOCK) == alignof(Waiter::SRWLockStorage), + "SRWLockStorage does not have the same alignment as SRWLOCK"); + + static_assert(sizeof(CONDITION_VARIABLE) == + sizeof(Waiter::ConditionVariableStorage), + "ABSL_CONDITION_VARIABLE_STORAGE does not have the same size " + "as CONDITION_VARIABLE"); + static_assert(alignof(CONDITION_VARIABLE) == + alignof(Waiter::ConditionVariableStorage), + "ConditionVariableStorage does not have the same " + "alignment as CONDITION_VARIABLE"); + + // The SRWLOCK and CONDITION_VARIABLE types must be trivially constuctible + // and destructible because we never call their constructors or destructors. + static_assert(std::is_trivially_constructible<SRWLOCK>::value, + "The SRWLOCK type must be trivially constructible"); + static_assert(std::is_trivially_constructible<CONDITION_VARIABLE>::value, + "The CONDITION_VARIABLE type must be trivially constructible"); + static_assert(std::is_trivially_destructible<SRWLOCK>::value, + "The SRWLOCK type must be trivially destructible"); + static_assert(std::is_trivially_destructible<CONDITION_VARIABLE>::value, + "The CONDITION_VARIABLE type must be trivially destructible"); +}; + class LockHolder { public: explicit LockHolder(SRWLOCK* mu) : mu_(mu) { @@ -345,14 +386,19 @@ class LockHolder { }; void Waiter::Init() { - InitializeSRWLock(&mu_); - InitializeConditionVariable(&cv_); + auto *mu = ::new (static_cast<void *>(&mu_storage_)) SRWLOCK; + auto *cv = ::new (static_cast<void *>(&cv_storage_)) CONDITION_VARIABLE; + InitializeSRWLock(mu); + InitializeConditionVariable(cv); waiter_count_.store(0, std::memory_order_relaxed); wakeup_count_.store(0, std::memory_order_relaxed); } bool Waiter::Wait(KernelTimeout t) { - LockHolder h(&mu_); + SRWLOCK *mu = WinHelper::GetLock(this); + CONDITION_VARIABLE *cv = WinHelper::GetCond(this); + + LockHolder h(mu); waiter_count_.fetch_add(1, std::memory_order_relaxed); // Loop until we find a wakeup to consume or timeout. @@ -370,8 +416,7 @@ bool Waiter::Wait(KernelTimeout t) { } // No wakeups available, time to wait. - if (!SleepConditionVariableSRW( - &cv_, &mu_, t.InMillisecondsFromNow(), 0)) { + if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) { // GetLastError() returns a Win32 DWORD, but we assign to // unsigned long to simplify the ABSL_RAW_LOG case below. The uniform // initialization guarantees this is not a narrowing conversion. @@ -398,11 +443,11 @@ void Waiter::Poke() { return; } // Potentially a waker. Take the lock and check again. - LockHolder h(&mu_); + LockHolder h(WinHelper::GetLock(this)); if (waiter_count_.load(std::memory_order_relaxed) == 0) { return; } - WakeConditionVariable(&cv_); + WakeConditionVariable(WinHelper::GetCond(this)); } #else @@ -410,5 +455,5 @@ void Waiter::Poke() { #endif } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index 2b737260..06032642 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,9 +18,7 @@ #include "absl/base/config.h" -#ifdef _WIN32 -#include <windows.h> -#else +#ifndef _WIN32 #include <pthread.h> #endif @@ -53,7 +51,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace synchronization_internal { // Waiter is an OS-specific semaphore. @@ -124,8 +122,20 @@ class Waiter { // primivitives. We are using SRWLOCK and CONDITION_VARIABLE // because they don't require a destructor to release system // resources. - SRWLOCK mu_; - CONDITION_VARIABLE cv_; + // + // However, we can't include Windows.h in our headers, so we use aligned + // storage buffers to define the storage. + using SRWLockStorage = + typename std::aligned_storage<sizeof(void*), alignof(void*)>::type; + using ConditionVariableStorage = + typename std::aligned_storage<sizeof(void*), alignof(void*)>::type; + + // WinHelper - Used to define utilities for accessing the lock and + // condition variable storage once the types are complete. + class WinHelper; + + SRWLockStorage mu_storage_; + ConditionVariableStorage cv_storage_; std::atomic<int> waiter_count_; std::atomic<int> wakeup_count_; @@ -135,7 +145,7 @@ class Waiter { }; } // namespace synchronization_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_SYNCHRONIZATION_INTERNAL_WAITER_H_ diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc index b7360c29..0279c8f8 100644 --- a/absl/synchronization/lifetime_test.cc +++ b/absl/synchronization/lifetime_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,6 +17,7 @@ #include <type_traits> #include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" @@ -95,6 +96,10 @@ void TestLocals() { RunTests(&mutex, &condvar); } +// Normal kConstInit usage +ABSL_CONST_INIT absl::Mutex const_init_mutex(absl::kConstInit); +void TestConstInitGlobal() { RunTests(&const_init_mutex, nullptr); } + // Global variables during start and termination // // In a translation unit, static storage duration variables are initialized in @@ -117,10 +122,53 @@ class OnDestruction { Function fn_; }; +// kConstInit +// Test early usage. (Declaration comes first; definitions must appear after +// the test runner.) +extern absl::Mutex early_const_init_mutex; +// (Normally I'd write this +[], to make the cast-to-function-pointer explicit, +// but in some MSVC setups we support, lambdas provide conversion operators to +// different flavors of function pointers, making this trick ambiguous.) +OnConstruction test_early_const_init([] { + RunTests(&early_const_init_mutex, nullptr); +}); +// This definition appears before test_early_const_init, but it should be +// initialized first (due to constant initialization). Test that the object +// actually works when constructed this way. +ABSL_CONST_INIT absl::Mutex early_const_init_mutex(absl::kConstInit); + +// Furthermore, test that the const-init c'tor doesn't stomp over the state of +// a Mutex. Really, this is a test that the platform under test correctly +// supports C++11 constant initialization. (The constant-initialization +// constructors of globals "happen at link time"; memory is pre-initialized, +// before the constructors of either grab_lock or check_still_locked are run.) +extern absl::Mutex const_init_sanity_mutex; +OnConstruction grab_lock([]() NO_THREAD_SAFETY_ANALYSIS { + const_init_sanity_mutex.Lock(); +}); +ABSL_CONST_INIT absl::Mutex const_init_sanity_mutex(absl::kConstInit); +OnConstruction check_still_locked([]() NO_THREAD_SAFETY_ANALYSIS { + const_init_sanity_mutex.AssertHeld(); + const_init_sanity_mutex.Unlock(); +}); + +// Test shutdown usage. (Declarations come first; definitions must appear after +// the test runner.) +extern absl::Mutex late_const_init_mutex; +// OnDestruction is being used here as a global variable, even though it has a +// non-trivial destructor. This is against the style guide. We're violating +// that rule here to check that the exception we allow for kConstInit is safe. +// NOLINTNEXTLINE +OnDestruction test_late_const_init([] { + RunTests(&late_const_init_mutex, nullptr); +}); +ABSL_CONST_INIT absl::Mutex late_const_init_mutex(absl::kConstInit); + } // namespace int main() { TestLocals(); + TestConstInitGlobal(); // Explicitly call exit(0) here, to make it clear that we intend for the // above global object destructors to run. std::exit(0); diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 9f8d6cd7..07f220f5 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -71,7 +71,7 @@ ABSL_ATTRIBUTE_WEAK void AbslInternalMutexYield() { std::this_thread::yield(); } } // extern "C" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { @@ -119,6 +119,10 @@ ABSL_CONST_INIT absl::base_internal::AtomicHook< } // namespace +static inline bool EvalConditionAnnotated(const Condition *cond, Mutex *mu, + bool locking, bool trylock, + bool read_lock); + void RegisterMutexProfiler(void (*fn)(int64_t wait_timestamp)) { submit_profile_data.Store(fn); } @@ -151,7 +155,7 @@ static int Delay(int32_t c, DelayMode mode) { if (c < limit) { c++; // spin } else { - ABSL_TSAN_MUTEX_PRE_DIVERT(0, 0); + ABSL_TSAN_MUTEX_PRE_DIVERT(nullptr, 0); if (c == limit) { // yield once AbslInternalMutexYield(); c++; @@ -159,7 +163,7 @@ static int Delay(int32_t c, DelayMode mode) { absl::SleepFor(absl::Microseconds(10)); c = 0; } - ABSL_TSAN_MUTEX_POST_DIVERT(0, 0); + ABSL_TSAN_MUTEX_POST_DIVERT(nullptr, 0); } return (c); } @@ -234,15 +238,14 @@ enum { // Mutex and CondVar events passed as "ev" to PostSynchEvent SYNCH_EV_SIGNALALL, }; -enum { // Event flags - SYNCH_F_R = 0x01, // reader event - SYNCH_F_LCK = 0x02, // PostSynchEvent called with mutex held - SYNCH_F_ACQ = 0x04, // event is an acquire +enum { // Event flags + SYNCH_F_R = 0x01, // reader event + SYNCH_F_LCK = 0x02, // PostSynchEvent called with mutex held + SYNCH_F_TRY = 0x04, // TryLock or ReaderTryLock + SYNCH_F_UNLOCK = 0x08, // Unlock or ReaderUnlock SYNCH_F_LCK_W = SYNCH_F_LCK, SYNCH_F_LCK_R = SYNCH_F_LCK | SYNCH_F_R, - SYNCH_F_ACQ_W = SYNCH_F_ACQ, - SYNCH_F_ACQ_R = SYNCH_F_ACQ | SYNCH_F_R, }; } // anonymous namespace @@ -251,21 +254,22 @@ static const struct { int flags; const char *msg; } event_properties[] = { - { SYNCH_F_LCK_W|SYNCH_F_ACQ_W, "TryLock succeeded " }, - { 0, "TryLock failed " }, - { SYNCH_F_LCK_R|SYNCH_F_ACQ_R, "ReaderTryLock succeeded " }, - { 0, "ReaderTryLock failed " }, - { SYNCH_F_ACQ_W, "Lock blocking " }, - { SYNCH_F_LCK_W, "Lock returning " }, - { SYNCH_F_ACQ_R, "ReaderLock blocking " }, - { SYNCH_F_LCK_R, "ReaderLock returning " }, - { SYNCH_F_LCK_W, "Unlock " }, - { SYNCH_F_LCK_R, "ReaderUnlock " }, - { 0, "Wait on " }, - { 0, "Wait unblocked " }, - { 0, "Signal on " }, - { 0, "SignalAll on " }, + {SYNCH_F_LCK_W | SYNCH_F_TRY, "TryLock succeeded "}, + {0, "TryLock failed "}, + {SYNCH_F_LCK_R | SYNCH_F_TRY, "ReaderTryLock succeeded "}, + {0, "ReaderTryLock failed "}, + {0, "Lock blocking "}, + {SYNCH_F_LCK_W, "Lock returning "}, + {0, "ReaderLock blocking "}, + {SYNCH_F_LCK_R, "ReaderLock returning "}, + {SYNCH_F_LCK_W | SYNCH_F_UNLOCK, "Unlock "}, + {SYNCH_F_LCK_R | SYNCH_F_UNLOCK, "ReaderUnlock "}, + {0, "Wait on "}, + {0, "Wait unblocked "}, + {0, "Signal on "}, + {0, "SignalAll on "}, }; + static absl::base_internal::SpinLock synch_event_mu( absl::base_internal::kLinkerInitialized); // protects synch_event @@ -415,9 +419,26 @@ static void PostSynchEvent(void *obj, int ev) { ABSL_RAW_LOG(INFO, "%s%p %s %s", event_properties[ev].msg, obj, (e == nullptr ? "" : e->name), buffer); } - if ((event_properties[ev].flags & SYNCH_F_LCK) != 0 && e != nullptr && - e->invariant != nullptr) { - (*e->invariant)(e->arg); + const int flags = event_properties[ev].flags; + if ((flags & SYNCH_F_LCK) != 0 && e != nullptr && e->invariant != nullptr) { + // Calling the invariant as is causes problems under ThreadSanitizer. + // We are currently inside of Mutex Lock/Unlock and are ignoring all + // memory accesses and synchronization. If the invariant transitively + // synchronizes something else and we ignore the synchronization, we will + // get false positive race reports later. + // Reuse EvalConditionAnnotated to properly call into user code. + struct local { + static bool pred(SynchEvent *ev) { + (*ev->invariant)(ev->arg); + return false; + } + }; + Condition cond(&local::pred, e); + Mutex *mu = static_cast<Mutex *>(obj); + const bool locking = (flags & SYNCH_F_UNLOCK) == 0; + const bool trylock = (flags & SYNCH_F_TRY) != 0; + const bool read_lock = (flags & SYNCH_F_R) != 0; + EvalConditionAnnotated(&cond, mu, locking, trylock, read_lock); } UnrefSynchEvent(e); } @@ -881,11 +902,15 @@ static PerThreadSynch *Enqueue(PerThreadSynch *head, // base_internal::CycleClock::Now() is 0.5%. int policy; struct sched_param param; - pthread_getschedparam(pthread_self(), &policy, ¶m); - s->priority = param.sched_priority; - s->next_priority_read_cycles = - now_cycles + - static_cast<int64_t>(base_internal::CycleClock::Frequency()); + const int err = pthread_getschedparam(pthread_self(), &policy, ¶m); + if (err != 0) { + ABSL_RAW_LOG(ERROR, "pthread_getschedparam failed: %d", err); + } else { + s->priority = param.sched_priority; + s->next_priority_read_cycles = + now_cycles + + static_cast<int64_t>(base_internal::CycleClock::Frequency()); + } } if (s->priority > head->priority) { // s's priority is above head's // try to put s in priority-fifo order, or failing that at the front. @@ -1080,7 +1105,7 @@ void Mutex::TryRemove(PerThreadSynch *s) { // if the wait extends past the absolute time specified, even if "s" is still // on the mutex queue. In this case, remove "s" from the queue and return // true, otherwise return false. -void Mutex::Block(PerThreadSynch *s) { +ABSL_XRAY_LOG_ARGS(1) void Mutex::Block(PerThreadSynch *s) { while (s->state.load(std::memory_order_acquire) == PerThreadSynch::kQueued) { if (!DecrementSynchSem(this, s, s->waitp->timeout)) { // After a timeout, we go into a spin loop until we remove ourselves @@ -1553,7 +1578,7 @@ bool Mutex::AwaitCommon(const Condition &cond, KernelTimeout t) { ABSL_TSAN_MUTEX_PRE_LOCK(this, TsanFlags(how)); this->LockSlowLoop(&waitp, flags); bool res = waitp.cond != nullptr || // => cond known true from LockSlowLoop - cond.Eval(); + EvalConditionAnnotated(&cond, this, true, false, how == kShared); ABSL_TSAN_MUTEX_POST_LOCK(this, TsanFlags(how), 0); return res; } @@ -1731,12 +1756,17 @@ void Mutex::LockSlow(MuHow how, const Condition *cond, int flags) { // Compute cond->Eval() and tell race detectors that we do it under mutex mu. static inline bool EvalConditionAnnotated(const Condition *cond, Mutex *mu, - bool locking, Mutex::MuHow how) { + bool locking, bool trylock, + bool read_lock) { // Delicate annotation dance. // We are currently inside of read/write lock/unlock operation. // All memory accesses are ignored inside of mutex operations + for unlock // operation tsan considers that we've already released the mutex. bool res = false; +#ifdef THREAD_SANITIZER + const int flags = read_lock ? __tsan_mutex_read_lock : 0; + const int tryflags = flags | (trylock ? __tsan_mutex_try_lock : 0); +#endif if (locking) { // For lock we pretend that we have finished the operation, // evaluate the predicate, then unlock the mutex and start locking it again @@ -1744,24 +1774,26 @@ static inline bool EvalConditionAnnotated(const Condition *cond, Mutex *mu, // Note: we can't simply do POST_LOCK, Eval, PRE_LOCK, because then tsan // will think the lock acquisition is recursive which will trigger // deadlock detector. - ABSL_TSAN_MUTEX_POST_LOCK(mu, TsanFlags(how), 0); + ABSL_TSAN_MUTEX_POST_LOCK(mu, tryflags, 0); res = cond->Eval(); - ABSL_TSAN_MUTEX_PRE_UNLOCK(mu, TsanFlags(how)); - ABSL_TSAN_MUTEX_POST_UNLOCK(mu, TsanFlags(how)); - ABSL_TSAN_MUTEX_PRE_LOCK(mu, TsanFlags(how)); + // There is no "try" version of Unlock, so use flags instead of tryflags. + ABSL_TSAN_MUTEX_PRE_UNLOCK(mu, flags); + ABSL_TSAN_MUTEX_POST_UNLOCK(mu, flags); + ABSL_TSAN_MUTEX_PRE_LOCK(mu, tryflags); } else { // Similarly, for unlock we pretend that we have unlocked the mutex, // lock the mutex, evaluate the predicate, and start unlocking it again // to match the annotation at the end of outer unlock operation. - ABSL_TSAN_MUTEX_POST_UNLOCK(mu, TsanFlags(how)); - ABSL_TSAN_MUTEX_PRE_LOCK(mu, TsanFlags(how)); - ABSL_TSAN_MUTEX_POST_LOCK(mu, TsanFlags(how), 0); + ABSL_TSAN_MUTEX_POST_UNLOCK(mu, flags); + ABSL_TSAN_MUTEX_PRE_LOCK(mu, flags); + ABSL_TSAN_MUTEX_POST_LOCK(mu, flags, 0); res = cond->Eval(); - ABSL_TSAN_MUTEX_PRE_UNLOCK(mu, TsanFlags(how)); + ABSL_TSAN_MUTEX_PRE_UNLOCK(mu, flags); } // Prevent unused param warnings in non-TSAN builds. static_cast<void>(mu); - static_cast<void>(how); + static_cast<void>(trylock); + static_cast<void>(read_lock); return res; } @@ -1807,7 +1839,8 @@ bool Mutex::LockSlowWithDeadline(MuHow how, const Condition *cond, v, (how->fast_or | (v & zap_desig_waker[flags & kMuHasBlocked])) + how->fast_add, std::memory_order_acquire, std::memory_order_relaxed)) { - if (cond == nullptr || EvalConditionAnnotated(cond, this, true, how)) { + if (cond == nullptr || + EvalConditionAnnotated(cond, this, true, false, how == kShared)) { return true; } unlock = true; @@ -1825,7 +1858,8 @@ bool Mutex::LockSlowWithDeadline(MuHow how, const Condition *cond, } this->LockSlowLoop(&waitp, flags); return waitp.cond != nullptr || // => cond known true from LockSlowLoop - cond == nullptr || EvalConditionAnnotated(cond, this, true, how); + cond == nullptr || + EvalConditionAnnotated(cond, this, true, false, how == kShared); } // RAW_CHECK_FMT() takes a condition, a printf-style format string, and @@ -1842,7 +1876,7 @@ static void CheckForMutexCorruption(intptr_t v, const char* label) { // Test for either of two situations that should not occur in v: // kMuWriter and kMuReader // kMuWrWait and !kMuWait - const intptr_t w = v ^ kMuWait; + const uintptr_t w = v ^ kMuWait; // By flipping that bit, we can now test for: // kMuWriter and kMuReader in w // kMuWrWait and kMuWait in w @@ -1881,7 +1915,8 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) { waitp->how->fast_add, std::memory_order_acquire, std::memory_order_relaxed)) { if (waitp->cond == nullptr || - EvalConditionAnnotated(waitp->cond, this, true, waitp->how)) { + EvalConditionAnnotated(waitp->cond, this, true, false, + waitp->how == kShared)) { break; // we timed out, or condition true, so return } this->UnlockSlow(waitp); // got lock but condition false @@ -1924,7 +1959,8 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) { std::memory_order_release, std::memory_order_relaxed)); if (waitp->cond == nullptr || - EvalConditionAnnotated(waitp->cond, this, true, waitp->how)) { + EvalConditionAnnotated(waitp->cond, this, true, false, + waitp->how == kShared)) { break; // we timed out, or condition true, so return } this->UnlockSlow(waitp); // got lock but condition false @@ -2552,7 +2588,7 @@ void CondVar::Wakeup(PerThreadSynch *w) { } void CondVar::Signal() { - ABSL_TSAN_MUTEX_PRE_SIGNAL(0, 0); + ABSL_TSAN_MUTEX_PRE_SIGNAL(nullptr, 0); intptr_t v; int c = 0; for (v = cv_.load(std::memory_order_relaxed); v != 0; @@ -2581,17 +2617,17 @@ void CondVar::Signal() { if ((v & kCvEvent) != 0) { PostSynchEvent(this, SYNCH_EV_SIGNAL); } - ABSL_TSAN_MUTEX_POST_SIGNAL(0, 0); + ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0); return; } else { c = Delay(c, GENTLE); } } - ABSL_TSAN_MUTEX_POST_SIGNAL(0, 0); + ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0); } void CondVar::SignalAll () { - ABSL_TSAN_MUTEX_PRE_SIGNAL(0, 0); + ABSL_TSAN_MUTEX_PRE_SIGNAL(nullptr, 0); intptr_t v; int c = 0; for (v = cv_.load(std::memory_order_relaxed); v != 0; @@ -2618,13 +2654,13 @@ void CondVar::SignalAll () { if ((v & kCvEvent) != 0) { PostSynchEvent(this, SYNCH_EV_SIGNALALL); } - ABSL_TSAN_MUTEX_POST_SIGNAL(0, 0); + ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0); return; } else { c = Delay(c, GENTLE); // try again after a delay } } - ABSL_TSAN_MUTEX_POST_SIGNAL(0, 0); + ABSL_TSAN_MUTEX_POST_SIGNAL(nullptr, 0); } void ReleasableMutexLock::Release() { @@ -2685,5 +2721,5 @@ bool Condition::GuaranteedEqual(const Condition *a, const Condition *b) { a->arg_ == b->arg_ && a->method_ == b->method_; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index ce97707f..d6890099 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -61,6 +61,7 @@ #include <cstdint> #include <string> +#include "absl/base/const_init.h" #include "absl/base/internal/identity.h" #include "absl/base/internal/low_level_alloc.h" #include "absl/base/internal/thread_identity.h" @@ -81,7 +82,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { class Condition; struct SynchWaitParams; @@ -137,7 +138,27 @@ struct SynchWaitParams; class LOCKABLE Mutex { public: + // Creates a `Mutex` that is not held by anyone. This constructor is + // typically used for Mutexes allocated on the heap or the stack. + // + // To create `Mutex` instances with static storage duration + // (e.g. a namespace-scoped or global variable), see + // `Mutex::Mutex(absl::kConstInit)` below instead. Mutex(); + + // Creates a mutex with static storage duration. A global variable + // constructed this way avoids the lifetime issues that can occur on program + // startup and shutdown. (See absl/base/const_init.h.) + // + // For Mutexes allocated on the heap and stack, instead use the default + // constructor, which can interact more fully with the thread sanitizer. + // + // Example usage: + // namespace foo { + // ABSL_CONST_INIT Mutex mu(absl::kConstInit); + // } + explicit constexpr Mutex(absl::ConstInitType); + ~Mutex(); // Mutex::Lock() @@ -880,11 +901,15 @@ class SCOPED_LOCKABLE ReleasableMutexLock { }; #ifdef ABSL_INTERNAL_USE_NONPROD_MUTEX +inline constexpr Mutex::Mutex(absl::ConstInitType) : impl_(absl::kConstInit) {} + #else inline Mutex::Mutex() : mu_(0) { ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); } +inline constexpr Mutex::Mutex(absl::ConstInitType) : mu_(0) {} + inline CondVar::CondVar() : cv_(0) {} #endif @@ -1015,7 +1040,7 @@ enum class OnDeadlockCycle { // the manner chosen here. void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode); -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // In some build configurations we pass --detect-odr-violations to the @@ -1027,4 +1052,5 @@ void SetMutexDeadlockDetectionMode(OnDeadlockCycle mode); extern "C" { void AbslInternalMutexYield(); } // extern "C" + #endif // ABSL_SYNCHRONIZATION_MUTEX_H_ diff --git a/absl/synchronization/mutex_benchmark.cc b/absl/synchronization/mutex_benchmark.cc index 2652bb97..ab188001 100644 --- a/absl/synchronization/mutex_benchmark.cc +++ b/absl/synchronization/mutex_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc index b2820e20..9851ac19 100644 --- a/absl/synchronization/mutex_test.cc +++ b/absl/synchronization/mutex_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ #include "absl/synchronization/mutex.h" -#ifdef WIN32 +#ifdef _WIN32 #include <windows.h> #endif @@ -610,9 +610,9 @@ TEST_P(CondVarWaitDeadlock, Test) { waiter2.reset(); // "join" waiter2 } -INSTANTIATE_TEST_CASE_P(CondVarWaitDeadlockTest, CondVarWaitDeadlock, - ::testing::Range(0, 8), - ::testing::PrintToStringParamName()); +INSTANTIATE_TEST_SUITE_P(CondVarWaitDeadlockTest, CondVarWaitDeadlock, + ::testing::Range(0, 8), + ::testing::PrintToStringParamName()); // -------------------------------------------------------- // Test for fix of bug in DequeueAllWakeable() @@ -815,7 +815,12 @@ TEST(Mutex, MutexReaderDecrementBug) NO_THREAD_SAFETY_ANALYSIS { // Test that we correctly handle the situation when a lock is // held and then destroyed (w/o unlocking). +#ifdef THREAD_SANITIZER +// TSAN reports errors when locked Mutexes are destroyed. +TEST(Mutex, DISABLED_LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS { +#else TEST(Mutex, LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS { +#endif for (int i = 0; i != 10; i++) { // Create, lock and destroy 10 locks. const int kNumLocks = 10; @@ -1030,11 +1035,11 @@ TEST(Mutex, DeadlockDetector) { class ScopedDisableBazelTestWarnings { public: ScopedDisableBazelTestWarnings() { -#ifdef WIN32 +#ifdef _WIN32 char file[MAX_PATH]; - if (GetEnvironmentVariable(kVarName, file, sizeof(file)) < sizeof(file)) { + if (GetEnvironmentVariableA(kVarName, file, sizeof(file)) < sizeof(file)) { warnings_output_file_ = file; - SetEnvironmentVariable(kVarName, nullptr); + SetEnvironmentVariableA(kVarName, nullptr); } #else const char *file = getenv(kVarName); @@ -1047,8 +1052,8 @@ class ScopedDisableBazelTestWarnings { ~ScopedDisableBazelTestWarnings() { if (!warnings_output_file_.empty()) { -#ifdef WIN32 - SetEnvironmentVariable(kVarName, warnings_output_file_.c_str()); +#ifdef _WIN32 + SetEnvironmentVariableA(kVarName, warnings_output_file_.c_str()); #else setenv(kVarName, warnings_output_file_.c_str(), 0); #endif @@ -1062,7 +1067,12 @@ class ScopedDisableBazelTestWarnings { const char ScopedDisableBazelTestWarnings::kVarName[] = "TEST_WARNINGS_OUTPUT_FILE"; +#ifdef THREAD_SANITIZER +// This test intentionally creates deadlocks to test the deadlock detector. +TEST(Mutex, DISABLED_DeadlockDetectorBazelWarning) { +#else TEST(Mutex, DeadlockDetectorBazelWarning) { +#endif absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kReport); // Cause deadlock detection to detect something, if it's @@ -1109,7 +1119,12 @@ TEST(Mutex, DeadlockDetectorStessTest) NO_THREAD_SAFETY_ANALYSIS { } } +#ifdef THREAD_SANITIZER +// TSAN reports errors when locked Mutexes are destroyed. +TEST(Mutex, DISABLED_DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS { +#else TEST(Mutex, DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS { +#endif // Test a scenario where a cached deadlock graph node id in the // list of held locks is not invalidated when the corresponding // mutex is deleted. @@ -1367,8 +1382,8 @@ std::vector<TimeoutTestParam> MakeTimeoutTestParamValues() { } // Instantiate `TimeoutTest` with `MakeTimeoutTestParamValues()`. -INSTANTIATE_TEST_CASE_P(All, TimeoutTest, - testing::ValuesIn(MakeTimeoutTestParamValues())); +INSTANTIATE_TEST_SUITE_P(All, TimeoutTest, + testing::ValuesIn(MakeTimeoutTestParamValues())); TEST_P(TimeoutTest, Await) { const TimeoutTestParam params = GetParam(); @@ -1548,9 +1563,9 @@ static std::vector<int> AllThreadCountValues() { class MutexVariableThreadCountTest : public ::testing::TestWithParam<int> {}; // Instantiate the above with AllThreadCountOptions(). -INSTANTIATE_TEST_CASE_P(ThreadCounts, MutexVariableThreadCountTest, - ::testing::ValuesIn(AllThreadCountValues()), - ::testing::PrintToStringParamName()); +INSTANTIATE_TEST_SUITE_P(ThreadCounts, MutexVariableThreadCountTest, + ::testing::ValuesIn(AllThreadCountValues()), + ::testing::PrintToStringParamName()); // Reduces iterations by some factor for slow platforms // (determined empirically). diff --git a/absl/synchronization/notification.cc b/absl/synchronization/notification.cc index 472b7a3e..d691cfca 100644 --- a/absl/synchronization/notification.cc +++ b/absl/synchronization/notification.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { void Notification::Notify() { MutexLock l(&this->mutex_); @@ -83,5 +83,5 @@ bool Notification::WaitForNotificationWithDeadline(absl::Time deadline) const { return notified; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/synchronization/notification.h b/absl/synchronization/notification.h index 25821b18..8ed7f12a 100644 --- a/absl/synchronization/notification.h +++ b/absl/synchronization/notification.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -57,7 +57,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // Notification @@ -110,6 +110,7 @@ class Notification { std::atomic<bool> notified_yet_; // written under mutex_ }; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl + #endif // ABSL_SYNCHRONIZATION_NOTIFICATION_H_ diff --git a/absl/synchronization/notification_test.cc b/absl/synchronization/notification_test.cc index d1b66743..a64674ca 100644 --- a/absl/synchronization/notification_test.cc +++ b/absl/synchronization/notification_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,7 @@ #include "absl/synchronization/mutex.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // A thread-safe class that holds a counter. class ThreadSafeCounter { @@ -73,12 +73,16 @@ static void BasicTests(bool notify_before_waiting, Notification* notification) { EXPECT_FALSE(notification->WaitForNotificationWithDeadline(absl::Now())); const absl::Duration delay = absl::Milliseconds(50); + const absl::Time start = absl::Now(); + EXPECT_FALSE(notification->WaitForNotificationWithTimeout(delay)); + const absl::Duration elapsed = absl::Now() - start; + // Allow for a slight early return, to account for quality of implementation // issues on various platforms. const absl::Duration slop = absl::Microseconds(200); - absl::Time start = absl::Now(); - EXPECT_FALSE(notification->WaitForNotificationWithTimeout(delay)); - EXPECT_LE(start + delay, absl::Now() + slop); + EXPECT_LE(delay - slop, elapsed) + << "WaitForNotificationWithTimeout returned " << delay - elapsed + << " early (with " << slop << " slop), start time was " << start; ThreadSafeCounter ready_counter; ThreadSafeCounter done_counter; @@ -125,5 +129,5 @@ TEST(NotificationTest, SanityTest) { BasicTests(true, &local_notification2); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index 4d9c01c4..55e83a8c 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,8 +15,9 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -41,6 +42,7 @@ cc_library( "time.h", ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base", "//absl/base:core_headers", @@ -60,6 +62,7 @@ cc_library( ], hdrs = ["internal/test_util.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/time:__pkg__", ], @@ -82,6 +85,7 @@ cc_test( "time_zone_test.cc", ], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":test_util", ":time", @@ -103,6 +107,7 @@ cc_test( "time_benchmark.cc", ], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = [ "benchmark", ], @@ -110,6 +115,7 @@ cc_test( ":test_util", ":time", "//absl/base", + "//absl/base:core_headers", "//absl/hash", "@com_github_google_benchmark//:benchmark_main", ], diff --git a/absl/time/CMakeLists.txt b/absl/time/CMakeLists.txt index 53216cda..59098321 100644 --- a/absl/time/CMakeLists.txt +++ b/absl/time/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,85 +14,114 @@ # limitations under the License. # -list(APPEND TIME_PUBLIC_HEADERS - "civil_time.h" - "clock.h" - "time.h" -) - - -list(APPEND TIME_INTERNAL_HEADERS - "internal/test_util.h" - "internal/cctz/include/cctz/civil_time.h" - "internal/cctz/include/cctz/civil_time_detail.h" - "internal/cctz/include/cctz/time_zone.h" - "internal/cctz/include/cctz/zone_info_source.h" -) - -list(APPEND TIME_SRC +absl_cc_library( + NAME + time + HDRS + "civil_time.h" + "clock.h" + "time.h" + SRCS "civil_time.cc" - "time.cc" "clock.cc" "duration.cc" "format.cc" - "internal/cctz/src/civil_time_detail.cc" - "internal/cctz/src/time_zone_fixed.cc" - "internal/cctz/src/time_zone_fixed.h" - "internal/cctz/src/time_zone_format.cc" - "internal/cctz/src/time_zone_if.cc" - "internal/cctz/src/time_zone_if.h" - "internal/cctz/src/time_zone_impl.cc" - "internal/cctz/src/time_zone_impl.h" - "internal/cctz/src/time_zone_info.cc" - "internal/cctz/src/time_zone_info.h" - "internal/cctz/src/time_zone_libc.cc" - "internal/cctz/src/time_zone_libc.h" - "internal/cctz/src/time_zone_lookup.cc" - "internal/cctz/src/time_zone_posix.cc" - "internal/cctz/src/time_zone_posix.h" - "internal/cctz/src/tzfile.h" - "internal/cctz/src/zone_info_source.cc" - ${TIME_PUBLIC_HEADERS} - ${TIME_INTERNAL_HEADERS} + "internal/get_current_time_chrono.inc" + "internal/get_current_time_posix.inc" + "time.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base + absl::core_headers + absl::int128 + absl::strings + absl::civil_time + absl::time_zone + PUBLIC ) -set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128 absl::strings) -absl_library( - TARGET - absl_time - SOURCES - ${TIME_SRC} - PUBLIC_LIBRARIES - ${TIME_PUBLIC_LIBRARIES} - EXPORT_NAME - time +absl_cc_library( + NAME + civil_time + HDRS + "internal/cctz/include/cctz/civil_time.h" + "internal/cctz/include/cctz/civil_time_detail.h" + SRCS + "internal/cctz/src/civil_time_detail.cc" + COPTS + ${ABSL_DEFAULT_COPTS} ) +if(APPLE) + find_library(CoreFoundation CoreFoundation) +endif() +absl_cc_library( + NAME + time_zone + HDRS + "internal/cctz/include/cctz/time_zone.h" + "internal/cctz/include/cctz/zone_info_source.h" + SRCS + "internal/cctz/src/time_zone_fixed.cc" + "internal/cctz/src/time_zone_fixed.h" + "internal/cctz/src/time_zone_format.cc" + "internal/cctz/src/time_zone_if.cc" + "internal/cctz/src/time_zone_if.h" + "internal/cctz/src/time_zone_impl.cc" + "internal/cctz/src/time_zone_impl.h" + "internal/cctz/src/time_zone_info.cc" + "internal/cctz/src/time_zone_info.h" + "internal/cctz/src/time_zone_libc.cc" + "internal/cctz/src/time_zone_libc.h" + "internal/cctz/src/time_zone_lookup.cc" + "internal/cctz/src/time_zone_posix.cc" + "internal/cctz/src/time_zone_posix.h" + "internal/cctz/src/tzfile.h" + "internal/cctz/src/zone_info_source.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + $<$<PLATFORM_ID:Darwin>:${CoreFoundation}> +) -# -## TESTS -# - -# test time_test -list(APPEND TIME_TEST_SRC - "civil_time_test.cc" - "time_test.cc" - "clock_test.cc" - "duration_test.cc" - "format_test.cc" - "time_test.cc" - "time_zone_test.cc" - "internal/test_util.cc" +absl_cc_library( + NAME + test_util + HDRS + "internal/test_util.h" + SRCS + "internal/test_util.cc" + "internal/zoneinfo.inc" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::time + absl::base + absl::time_zone + gmock + TESTONLY ) -set(TIME_TEST_PUBLIC_LIBRARIES absl::time) -absl_test( - TARGET +absl_cc_test( + NAME time_test - SOURCES - ${TIME_TEST_SRC} - PUBLIC_LIBRARIES - ${TIME_TEST_PUBLIC_LIBRARIES} + SRCS + "civil_time_test.cc" + "clock_test.cc" + "duration_test.cc" + "format_test.cc" + "time_test.cc" + "time_zone_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::test_util + absl::time + absl::base + absl::config + absl::core_headers + absl::time_zone + gmock_main ) - diff --git a/absl/time/civil_time.cc b/absl/time/civil_time.cc index c6fa5469..bf7ba5ab 100644 --- a/absl/time/civil_time.cc +++ b/absl/time/civil_time.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { @@ -39,7 +39,8 @@ std::string FormatYearAnd(string_view fmt, CivilSecond cs) { cs.hour(), cs.minute(), cs.second()); const TimeZone utc = UTCTimeZone(); // TODO(absl-team): Avoid conversion of fmt std::string. - return StrCat(cs.year(), FormatTime(std::string(fmt), FromCivil(ncs, utc), utc)); + return StrCat(cs.year(), + FormatTime(std::string(fmt), FromCivil(ncs, utc), utc)); } } // namespace @@ -53,15 +54,9 @@ std::string FormatCivilTime(CivilMinute c) { std::string FormatCivilTime(CivilHour c) { return FormatYearAnd("-%m-%dT%H", c); } -std::string FormatCivilTime(CivilDay c) { - return FormatYearAnd("-%m-%d", c); -} -std::string FormatCivilTime(CivilMonth c) { - return FormatYearAnd("-%m", c); -} -std::string FormatCivilTime(CivilYear c) { - return FormatYearAnd("", c); -} +std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); } +std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); } +std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); } namespace time_internal { @@ -86,5 +81,5 @@ std::ostream& operator<<(std::ostream& os, CivilSecond s) { } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/time/civil_time.h b/absl/time/civil_time.h index f6cc3ff8..f0be303c 100644 --- a/absl/time/civil_time.h +++ b/absl/time/civil_time.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -66,7 +66,6 @@ // // // Valid in C++14 // constexpr absl::CivilDay cd(1969, 07, 20); -// #ifndef ABSL_TIME_CIVIL_TIME_H_ #define ABSL_TIME_CIVIL_TIME_H_ @@ -77,7 +76,7 @@ #include "absl/time/internal/cctz/include/cctz/civil_time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { struct second_tag : cctz::detail::second_tag {}; @@ -372,15 +371,15 @@ using Weekday = time_internal::cctz::weekday; // GetWeekday() // -// Returns the absl::Weekday for the given absl::CivilDay. +// Returns the absl::Weekday for the given (realigned) civil-time value. // // Example: // // absl::CivilDay a(2015, 8, 13); // absl::Weekday wd = absl::GetWeekday(a); // wd == absl::Weekday::thursday // -inline Weekday GetWeekday(CivilDay cd) { - return time_internal::cctz::get_weekday(cd); +inline Weekday GetWeekday(CivilSecond cs) { + return time_internal::cctz::get_weekday(cs); } // NextWeekday() @@ -409,9 +408,9 @@ inline Weekday GetWeekday(CivilDay cd) { // // absl::CivilDay d = ... // // Gets the following Thursday if d is not already Thursday -// absl::CivilDay thurs1 = absl::PrevWeekday(d, absl::Weekday::thursday) + 7; +// absl::CivilDay thurs1 = absl::NextWeekday(d - 1, absl::Weekday::thursday); // // Gets the previous Thursday if d is not already Thursday -// absl::CivilDay thurs2 = absl::NextWeekday(d, absl::Weekday::thursday) - 7; +// absl::CivilDay thurs2 = absl::PrevWeekday(d + 1, absl::Weekday::thursday); // inline CivilDay NextWeekday(CivilDay cd, Weekday wd) { return CivilDay(time_internal::cctz::next_weekday(cd, wd)); @@ -422,7 +421,7 @@ inline CivilDay PrevWeekday(CivilDay cd, Weekday wd) { // GetYearDay() // -// Returns the day-of-year for the given absl::CivilDay. +// Returns the day-of-year for the given (realigned) civil-time value. // // Example: // @@ -431,8 +430,8 @@ inline CivilDay PrevWeekday(CivilDay cd, Weekday wd) { // absl::CivilDay b(2015, 12, 31); // int yd_dec_31 = absl::GetYearDay(b); // yd_dec_31 = 365 // -inline int GetYearDay(CivilDay cd) { - return time_internal::cctz::get_yearday(cd); +inline int GetYearDay(CivilSecond cs) { + return time_internal::cctz::get_yearday(cs); } // FormatCivilTime() @@ -452,7 +451,7 @@ inline int GetYearDay(CivilDay cd) { // Example: // // absl::CivilDay d = absl::CivilDay(1969, 7, 20); -// string day_string = absl::FormatCivilTime(d); // "1969-07-20" +// std::string day_string = absl::FormatCivilTime(d); // "1969-07-20" // std::string FormatCivilTime(CivilSecond c); std::string FormatCivilTime(CivilMinute c); @@ -482,7 +481,7 @@ std::ostream& operator<<(std::ostream& os, CivilSecond s); } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_TIME_CIVIL_TIME_H_ diff --git a/absl/time/civil_time_benchmark.cc b/absl/time/civil_time_benchmark.cc index f30f636d..40869835 100644 --- a/absl/time/civil_time_benchmark.cc +++ b/absl/time/civil_time_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/time/civil_time_test.cc b/absl/time/civil_time_test.cc index dc83d7a9..03cd1f12 100644 --- a/absl/time/civil_time_test.cc +++ b/absl/time/civil_time_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -616,6 +616,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(4, ss.hour()); EXPECT_EQ(5, ss.minute()); EXPECT_EQ(6, ss.second()); + EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(ss)); + EXPECT_EQ(34, absl::GetYearDay(ss)); absl::CivilMinute mm(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, mm.year()); @@ -624,6 +626,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(4, mm.hour()); EXPECT_EQ(5, mm.minute()); EXPECT_EQ(0, mm.second()); + EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(mm)); + EXPECT_EQ(34, absl::GetYearDay(mm)); absl::CivilHour hh(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, hh.year()); @@ -632,6 +636,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(4, hh.hour()); EXPECT_EQ(0, hh.minute()); EXPECT_EQ(0, hh.second()); + EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(hh)); + EXPECT_EQ(34, absl::GetYearDay(hh)); absl::CivilDay d(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, d.year()); @@ -640,6 +646,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(0, d.hour()); EXPECT_EQ(0, d.minute()); EXPECT_EQ(0, d.second()); + EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(d)); + EXPECT_EQ(34, absl::GetYearDay(d)); absl::CivilMonth m(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, m.year()); @@ -648,6 +656,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(0, m.hour()); EXPECT_EQ(0, m.minute()); EXPECT_EQ(0, m.second()); + EXPECT_EQ(absl::Weekday::sunday, absl::GetWeekday(m)); + EXPECT_EQ(32, absl::GetYearDay(m)); absl::CivilYear y(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, y.year()); @@ -656,6 +666,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(0, y.hour()); EXPECT_EQ(0, y.minute()); EXPECT_EQ(0, y.second()); + EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(y)); + EXPECT_EQ(1, absl::GetYearDay(y)); } TEST(CivilTime, Format) { @@ -1028,7 +1040,7 @@ TEST(CivilTime, LeapYears) { TEST(CivilTime, FirstThursdayInMonth) { const absl::CivilDay nov1(2014, 11, 1); const absl::CivilDay thursday = - absl::PrevWeekday(nov1, absl::Weekday::thursday) + 7; + absl::NextWeekday(nov1 - 1, absl::Weekday::thursday); EXPECT_EQ("2014-11-06", absl::FormatCivilTime(thursday)); // Bonus: Date of Thanksgiving in the United States diff --git a/absl/time/clock.cc b/absl/time/clock.cc index 2915d78b..48dc4450 100644 --- a/absl/time/clock.cc +++ b/absl/time/clock.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -34,7 +34,7 @@ #include "absl/base/thread_annotations.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { Time Now() { // TODO(bww): Get a timespec instead so we don't have to divide. int64_t n = absl::GetCurrentTimeNanos(); @@ -44,7 +44,7 @@ Time Now() { } return time_internal::FromUnixDuration(absl::Nanoseconds(n)); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // Decide if we should use the fast GetCurrentTimeNanos() algorithm @@ -73,11 +73,11 @@ Time Now() { #if !ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { int64_t GetCurrentTimeNanos() { return GET_CURRENT_TIME_NANOS_FROM_SYSTEM(); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else // Use the cyclecounter-based implementation below. @@ -95,7 +95,7 @@ static int64_t stats_slow_paths; static int64_t stats_fast_slow_paths; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { // This is a friend wrapper around UnscaledCycleClock::Now() // (needed to access UnscaledCycleClock). @@ -384,7 +384,7 @@ static uint64_t UpdateLastSample( // // Manually mark this 'noinline' to minimize stack frame size of the fast // path. Without this, sometimes a compiler may inline this big block of code -// into the fast past. That causes lots of register spills and reloads that +// into the fast path. That causes lots of register spills and reloads that // are unnecessary unless the slow path is taken. // // TODO(absl-team): Remove this attribute when our compiler is smart enough @@ -520,12 +520,12 @@ static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns, return estimated_base_ns; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { // Returns the maximum duration that SleepOnce() can sleep for. @@ -553,7 +553,7 @@ void SleepOnce(absl::Duration to_sleep) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl extern "C" { diff --git a/absl/time/clock.h b/absl/time/clock.h index b2941126..a3b9ffe9 100644 --- a/absl/time/clock.h +++ b/absl/time/clock.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -26,7 +26,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // Now() // @@ -50,7 +50,7 @@ int64_t GetCurrentTimeNanos(); // * Returns immediately when passed a nonpositive duration. void SleepFor(absl::Duration duration); -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl // ----------------------------------------------------------------------------- diff --git a/absl/time/clock_benchmark.cc b/absl/time/clock_benchmark.cc index 3d3cd9d5..c5c795ec 100644 --- a/absl/time/clock_benchmark.cc +++ b/absl/time/clock_benchmark.cc @@ -3,7 +3,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,6 +15,8 @@ #if !defined(_WIN32) #include <sys/time.h> +#else +#include <winsock2.h> #endif // _WIN32 #include <cstdio> diff --git a/absl/time/clock_test.cc b/absl/time/clock_test.cc index 707166d0..4bcfc6bc 100644 --- a/absl/time/clock_test.cc +++ b/absl/time/clock_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/time/duration.cc b/absl/time/duration.cc index 04669709..6a51baff 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -49,6 +49,10 @@ // // Arithmetic overflows/underflows to +/- infinity and saturates. +#if defined(_MSC_VER) +#include <winsock2.h> // for timeval +#endif + #include <algorithm> #include <cassert> #include <cctype> @@ -67,7 +71,7 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { @@ -750,9 +754,9 @@ void AppendNumberUnit(std::string* out, double n, DisplayUnit unit) { } // namespace -// From Go's doc at http://golang.org/pkg/time/#Duration.String +// From Go's doc at https://golang.org/pkg/time/#Duration.String // [FormatDuration] returns a string representing the duration in the -// form "72h3m0.5s". Leading zero units are omitted. As a special +// form "72h3m0.5s". Leading zero units are omitted. As a special // case, durations less than one second format use a smaller unit // (milli-, micro-, or nanoseconds) to ensure that the leading digit // is non-zero. The zero duration formats as 0, with no unit. @@ -856,8 +860,8 @@ bool ConsumeDurationUnit(const char** start, Duration* unit) { } // namespace -// From Go's doc at http://golang.org/pkg/time/#ParseDuration -// [ParseDuration] parses a duration string. A duration string is +// From Go's doc at https://golang.org/pkg/time/#ParseDuration +// [ParseDuration] parses a duration string. A duration string is // a possibly signed sequence of decimal numbers, each with optional // fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". // Valid time units are "ns", "us" "ms", "s", "m", "h". @@ -902,11 +906,12 @@ bool ParseDuration(const std::string& dur_string, Duration* d) { *d = dur; return true; } + bool ParseFlag(const std::string& text, Duration* dst, std::string* ) { return ParseDuration(text, dst); } std::string UnparseFlag(Duration d) { return FormatDuration(d); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/time/duration_benchmark.cc b/absl/time/duration_benchmark.cc index d5657bd5..83a836c8 100644 --- a/absl/time/duration_benchmark.cc +++ b/absl/time/duration_benchmark.cc @@ -3,7 +3,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,6 +17,7 @@ #include <ctime> #include <string> +#include "absl/base/attributes.h" #include "absl/time/time.h" #include "benchmark/benchmark.h" diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index 61f3c5c0..5dce9ac8 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if defined(_MSC_VER) +#include <winsock2.h> // for timeval +#endif + #include <chrono> // NOLINT(build/c++11) #include <cmath> #include <cstdint> @@ -1771,7 +1775,7 @@ TEST(Duration, ParseDuration) { TEST(Duration, FormatParseRoundTrip) { #define TEST_PARSE_ROUNDTRIP(d) \ do { \ - std::string s = absl::FormatDuration(d); \ + std::string s = absl::FormatDuration(d); \ absl::Duration dur; \ EXPECT_TRUE(absl::ParseDuration(s, &dur)); \ EXPECT_EQ(d, dur); \ diff --git a/absl/time/format.cc b/absl/time/format.cc index 6aabcee9..6eb83d7a 100644 --- a/absl/time/format.cc +++ b/absl/time/format.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ namespace cctz = absl::time_internal::cctz; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { 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"; @@ -68,7 +68,8 @@ absl::Time Join(const cctz_parts& parts) { } // namespace -std::string FormatTime(const std::string& format, absl::Time t, absl::TimeZone tz) { +std::string FormatTime(const std::string& format, absl::Time t, + absl::TimeZone tz) { if (t == absl::InfiniteFuture()) return kInfiniteFutureStr; if (t == absl::InfinitePast()) return kInfinitePastStr; const auto parts = Split(t); @@ -84,15 +85,15 @@ std::string FormatTime(absl::Time t) { return absl::FormatTime(RFC3339_full, t, absl::LocalTimeZone()); } -bool ParseTime(const std::string& format, const std::string& input, absl::Time* time, - std::string* err) { +bool ParseTime(const std::string& format, const std::string& input, + absl::Time* time, std::string* err) { return absl::ParseTime(format, input, absl::UTCTimeZone(), time, err); } // If the input string does not contain an explicit UTC offset, interpret // the fields with respect to the given TimeZone. -bool ParseTime(const std::string& format, const std::string& input, absl::TimeZone tz, - absl::Time* time, std::string* err) { +bool ParseTime(const std::string& format, const std::string& input, + absl::TimeZone tz, absl::Time* time, std::string* err) { const char* data = input.c_str(); while (std::isspace(*data)) ++data; @@ -137,5 +138,5 @@ std::string UnparseFlag(absl::Time t) { return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone()); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/time/format_benchmark.cc b/absl/time/format_benchmark.cc index 766f1b39..249c51d8 100644 --- a/absl/time/format_benchmark.cc +++ b/absl/time/format_benchmark.cc @@ -3,7 +3,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/time/format_test.cc b/absl/time/format_test.cc index ac8d5ea3..ab1f3059 100644 --- a/absl/time/format_test.cc +++ b/absl/time/format_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -27,8 +27,8 @@ namespace { // A helper that tests the given format specifier by itself, and with leading // and trailing characters. For example: TestFormatSpecifier(t, "%a", "Thu"). -void TestFormatSpecifier(absl::Time t, absl::TimeZone tz, const std::string& fmt, - const std::string& ans) { +void TestFormatSpecifier(absl::Time t, absl::TimeZone tz, + const std::string& fmt, const std::string& ans) { EXPECT_EQ(ans, absl::FormatTime(fmt, t, tz)); EXPECT_EQ("xxx " + ans, absl::FormatTime("xxx " + fmt, t, tz)); EXPECT_EQ(ans + " yyy", absl::FormatTime(fmt + " yyy", t, tz)); @@ -201,7 +201,7 @@ TEST(ParseTime, ErrorCases) { err.clear(); EXPECT_FALSE(absl::ParseTime("%Q", "x", &t, &err)) << err; // Exact contents of "err" are platform-dependent because of - // differences in the strptime implementation between OSX and Linux. + // differences in the strptime implementation between macOS and Linux. EXPECT_FALSE(err.empty()); // Fails because of trailing, unparsed data "blah". @@ -375,7 +375,8 @@ TEST(FormatParse, RoundTrip) { // RFC3339, which renders subseconds. { absl::Time out; - const std::string s = absl::FormatTime(absl::RFC3339_full, in + subseconds, lax); + const std::string s = + absl::FormatTime(absl::RFC3339_full, in + subseconds, lax); EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err)) << s << ": " << err; EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez diff --git a/absl/time/internal/cctz/BUILD.bazel b/absl/time/internal/cctz/BUILD.bazel index e2cfe801..b05c2347 100644 --- a/absl/time/internal/cctz/BUILD.bazel +++ b/absl/time/internal/cctz/BUILD.bazel @@ -4,7 +4,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -16,6 +16,20 @@ package(features = ["-parse_headers"]) licenses(["notice"]) # Apache License +config_setting( + name = "osx", + constraint_values = [ + "@bazel_tools//platforms:osx", + ], +) + +config_setting( + name = "ios", + constraint_values = [ + "@bazel_tools//platforms:ios", + ], +) + ### libraries cc_library( @@ -62,6 +76,15 @@ cc_library( "include/cctz/time_zone.h", "include/cctz/zone_info_source.h", ], + linkopts = select({ + ":osx": [ + "-framework Foundation", + ], + ":ios": [ + "-framework Foundation", + ], + "//conditions:default": [], + }), visibility = ["//visibility:public"], deps = [":civil_time"], ) @@ -98,13 +121,13 @@ cc_test( cc_test( name = "time_zone_lookup_test", size = "small", + timeout = "moderate", srcs = ["src/time_zone_lookup_test.cc"], data = [":zoneinfo"], tags = [ "no_test_android_arm", "no_test_android_arm64", "no_test_android_x86", - "no_test_wasm", ], deps = [ ":civil_time", diff --git a/absl/time/internal/cctz/include/cctz/civil_time.h b/absl/time/internal/cctz/include/cctz/civil_time.h index 9fabbc3d..df25db07 100644 --- a/absl/time/internal/cctz/include/cctz/civil_time.h +++ b/absl/time/internal/cctz/include/cctz/civil_time.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include "absl/time/internal/cctz/include/cctz/civil_time_detail.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -280,7 +280,7 @@ using civil_second = detail::civil_second; // using detail::weekday; -// Returns the weekday for the given civil_day. +// Returns the weekday for the given civil-time value. // // civil_day a(2015, 8, 13); // weekday wd = get_weekday(a); // wd == weekday::thursday @@ -307,14 +307,14 @@ using detail::get_weekday; // // civil_day d = ... // // Gets the following Thursday if d is not already Thursday -// civil_day thurs1 = prev_weekday(d, weekday::thursday) + 7; +// civil_day thurs1 = next_weekday(d - 1, weekday::thursday); // // Gets the previous Thursday if d is not already Thursday -// civil_day thurs2 = next_weekday(d, weekday::thursday) - 7; +// civil_day thurs2 = prev_weekday(d + 1, weekday::thursday); // using detail::next_weekday; using detail::prev_weekday; -// Returns the day-of-year for the given civil_day. +// Returns the day-of-year for the given civil-time value. // // civil_day a(2015, 1, 1); // int yd_jan_1 = get_yearday(a); // yd_jan_1 = 1 @@ -325,7 +325,7 @@ using detail::get_yearday; } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 289ff499..53e087a2 100644 --- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h +++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,7 @@ #include <type_traits> // Disable constexpr support unless we are in C++14 mode. -#if __cpp_constexpr >= 201304 || _MSC_VER >= 1910 +#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) #define CONSTEXPR_D constexpr // data #define CONSTEXPR_F constexpr // function #define CONSTEXPR_M constexpr // member @@ -32,7 +32,7 @@ #endif namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -387,12 +387,12 @@ class civil_time { : civil_time(ct.f_) {} // Factories for the maximum/minimum representable civil_time. - static CONSTEXPR_F civil_time max() { - const auto max_year = std::numeric_limits<std::int_least64_t>::max(); + static CONSTEXPR_F civil_time (max)() { + const auto max_year = (std::numeric_limits<std::int_least64_t>::max)(); return civil_time(max_year, 12, 31, 23, 59, 59); } - static CONSTEXPR_F civil_time min() { - const auto min_year = std::numeric_limits<std::int_least64_t>::min(); + static CONSTEXPR_F civil_time (min)() { + const auto min_year = (std::numeric_limits<std::int_least64_t>::min)(); return civil_time(min_year, 1, 1, 0, 0, 0); } @@ -410,7 +410,7 @@ class civil_time { return *this; } CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept { - if (n != std::numeric_limits<diff_t>::min()) { + if (n != (std::numeric_limits<diff_t>::min)()) { f_ = step(T{}, f_, -n); } else { f_ = step(T{}, step(T{}, f_, -(n + 1)), 1); @@ -536,7 +536,7 @@ enum class weekday { sunday, }; -CONSTEXPR_F weekday get_weekday(const civil_day& cd) noexcept { +CONSTEXPR_F weekday get_weekday(const civil_second& cs) noexcept { CONSTEXPR_D weekday k_weekday_by_mon_off[13] = { weekday::monday, weekday::tuesday, weekday::wednesday, weekday::thursday, weekday::friday, weekday::saturday, @@ -547,30 +547,60 @@ CONSTEXPR_F weekday get_weekday(const civil_day& cd) noexcept { CONSTEXPR_D int k_weekday_offsets[1 + 12] = { -1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4, }; - year_t wd = 2400 + (cd.year() % 400) - (cd.month() < 3); + year_t wd = 2400 + (cs.year() % 400) - (cs.month() < 3); wd += wd / 4 - wd / 100 + wd / 400; - wd += k_weekday_offsets[cd.month()] + cd.day(); + wd += k_weekday_offsets[cs.month()] + cs.day(); return k_weekday_by_mon_off[wd % 7 + 6]; } //////////////////////////////////////////////////////////////////////// CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept { - do { cd += 1; } while (get_weekday(cd) != wd); - return cd; + CONSTEXPR_D weekday k_weekdays_forw[14] = { + weekday::monday, weekday::tuesday, weekday::wednesday, + weekday::thursday, weekday::friday, weekday::saturday, + weekday::sunday, weekday::monday, weekday::tuesday, + weekday::wednesday, weekday::thursday, weekday::friday, + weekday::saturday, weekday::sunday, + }; + weekday base = get_weekday(cd); + for (int i = 0;; ++i) { + if (base == k_weekdays_forw[i]) { + for (int j = i + 1;; ++j) { + if (wd == k_weekdays_forw[j]) { + return cd + (j - i); + } + } + } + } } CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept { - do { cd -= 1; } while (get_weekday(cd) != wd); - return cd; + CONSTEXPR_D weekday k_weekdays_back[14] = { + weekday::sunday, weekday::saturday, weekday::friday, + weekday::thursday, weekday::wednesday, weekday::tuesday, + weekday::monday, weekday::sunday, weekday::saturday, + weekday::friday, weekday::thursday, weekday::wednesday, + weekday::tuesday, weekday::monday, + }; + weekday base = get_weekday(cd); + for (int i = 0;; ++i) { + if (base == k_weekdays_back[i]) { + for (int j = i + 1;; ++j) { + if (wd == k_weekdays_back[j]) { + return cd - (j - i); + } + } + } + } } -CONSTEXPR_F int get_yearday(const civil_day& cd) noexcept { +CONSTEXPR_F int get_yearday(const civil_second& cs) noexcept { CONSTEXPR_D int k_month_offsets[1 + 12] = { -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, }; - const int feb29 = (cd.month() > 2 && impl::is_leap_year(cd.year())); - return k_month_offsets[cd.month()] + feb29 + cd.day(); + const int feb29 = (cs.month() > 2 && impl::is_leap_year(cs.year())); + return k_month_offsets[cs.month()] + feb29 + cs.day(); } //////////////////////////////////////////////////////////////////////// @@ -586,7 +616,7 @@ std::ostream& operator<<(std::ostream& os, weekday wd); } // namespace detail } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 0c34393e..f9769c0c 100644 --- a/absl/time/internal/cctz/include/cctz/time_zone.h +++ b/absl/time/internal/cctz/include/cctz/time_zone.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/time/internal/cctz/include/cctz/civil_time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -73,7 +73,7 @@ split_seconds(const time_point<seconds>& tp) { // // See also: // - http://www.iana.org/time-zones -// - http://en.wikipedia.org/wiki/Zoneinfo +// - https://en.wikipedia.org/wiki/Zoneinfo class time_zone { public: time_zone() : time_zone(nullptr) {} // Equivalent to UTC @@ -381,7 +381,7 @@ inline bool parse(const std::string& fmt, const std::string& input, } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 b3274e00..7372c5de 100644 --- a/absl/time/internal/cctz/include/cctz/zone_info_source.h +++ b/absl/time/internal/cctz/include/cctz/zone_info_source.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,7 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -41,11 +41,11 @@ class ZoneInfoSource { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz_extension { @@ -94,7 +94,7 @@ extern ZoneInfoSourceFactory zone_info_source_factory; } // namespace cctz_extension } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_ diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc index 4498d7d0..a40f504e 100644 --- a/absl/time/internal/cctz/src/cctz_benchmark.cc +++ b/absl/time/internal/cctz/src/cctz_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -47,6 +47,56 @@ void BM_Step_Days(benchmark::State& state) { } BENCHMARK(BM_Step_Days); +void BM_GetWeekday(benchmark::State& state) { + const cctz::civil_day c(2014, 8, 22); + while (state.KeepRunning()) { + benchmark::DoNotOptimize(cctz::get_weekday(c)); + } +} +BENCHMARK(BM_GetWeekday); + +void BM_NextWeekday(benchmark::State& state) { + const cctz::civil_day kStart(2014, 8, 22); + const cctz::civil_day kDays[7] = { + kStart + 0, kStart + 1, kStart + 2, kStart + 3, + kStart + 4, kStart + 5, kStart + 6, + }; + const cctz::weekday kWeekdays[7] = { + cctz::weekday::monday, cctz::weekday::tuesday, cctz::weekday::wednesday, + cctz::weekday::thursday, cctz::weekday::friday, cctz::weekday::saturday, + cctz::weekday::sunday, + }; + while (state.KeepRunningBatch(7 * 7)) { + for (const auto from : kDays) { + for (const auto to : kWeekdays) { + benchmark::DoNotOptimize(cctz::next_weekday(from, to)); + } + } + } +} +BENCHMARK(BM_NextWeekday); + +void BM_PrevWeekday(benchmark::State& state) { + const cctz::civil_day kStart(2014, 8, 22); + const cctz::civil_day kDays[7] = { + kStart + 0, kStart + 1, kStart + 2, kStart + 3, + kStart + 4, kStart + 5, kStart + 6, + }; + const cctz::weekday kWeekdays[7] = { + cctz::weekday::monday, cctz::weekday::tuesday, cctz::weekday::wednesday, + cctz::weekday::thursday, cctz::weekday::friday, cctz::weekday::saturday, + cctz::weekday::sunday, + }; + while (state.KeepRunningBatch(7 * 7)) { + for (const auto from : kDays) { + for (const auto to : kWeekdays) { + benchmark::DoNotOptimize(cctz::prev_weekday(from, to)); + } + } + } +} +BENCHMARK(BM_PrevWeekday); + const char RFC3339_full[] = "%Y-%m-%dT%H:%M:%E*S%Ez"; const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez"; @@ -357,6 +407,7 @@ const char* const kTimeZoneNames[] = { "Asia/Pontianak", "Asia/Pyongyang", "Asia/Qatar", + "Asia/Qostanay", "Asia/Qyzylorda", "Asia/Rangoon", "Asia/Riyadh", diff --git a/absl/time/internal/cctz/src/civil_time_detail.cc b/absl/time/internal/cctz/src/civil_time_detail.cc index e888066d..4df15d55 100644 --- a/absl/time/internal/cctz/src/civil_time_detail.cc +++ b/absl/time/internal/cctz/src/civil_time_detail.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -19,7 +19,7 @@ #include <sstream> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { namespace detail { @@ -88,5 +88,5 @@ std::ostream& operator<<(std::ostream& os, weekday wd) { } // namespace detail } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 2417a2a9..7d9a1834 100644 --- a/absl/time/internal/cctz/src/civil_time_test.cc +++ b/absl/time/internal/cctz/src/civil_time_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "gtest/gtest.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -38,7 +38,7 @@ std::string Format(const T& t) { } // namespace -#if __cpp_constexpr >= 201304 || _MSC_VER >= 1910 +#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) // Construction constexpr tests TEST(CivilTime, Normal) { @@ -320,7 +320,7 @@ TEST(CivilTime, YearDay) { constexpr int yd = get_yearday(cd); static_assert(yd == 28, "YearDay"); } -#endif // __cpp_constexpr >= 201304 || _MSC_VER >= 1910 +#endif // __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) // The remaining tests do not use constexpr. @@ -822,6 +822,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(4, ss.hour()); EXPECT_EQ(5, ss.minute()); EXPECT_EQ(6, ss.second()); + EXPECT_EQ(weekday::tuesday, get_weekday(ss)); + EXPECT_EQ(34, get_yearday(ss)); civil_minute mm(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, mm.year()); @@ -830,6 +832,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(4, mm.hour()); EXPECT_EQ(5, mm.minute()); EXPECT_EQ(0, mm.second()); + EXPECT_EQ(weekday::tuesday, get_weekday(mm)); + EXPECT_EQ(34, get_yearday(mm)); civil_hour hh(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, hh.year()); @@ -838,6 +842,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(4, hh.hour()); EXPECT_EQ(0, hh.minute()); EXPECT_EQ(0, hh.second()); + EXPECT_EQ(weekday::tuesday, get_weekday(hh)); + EXPECT_EQ(34, get_yearday(hh)); civil_day d(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, d.year()); @@ -856,6 +862,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(0, m.hour()); EXPECT_EQ(0, m.minute()); EXPECT_EQ(0, m.second()); + EXPECT_EQ(weekday::sunday, get_weekday(m)); + EXPECT_EQ(32, get_yearday(m)); civil_year y(2015, 2, 3, 4, 5, 6); EXPECT_EQ(2015, y.year()); @@ -864,6 +872,8 @@ TEST(CivilTime, Properties) { EXPECT_EQ(0, y.hour()); EXPECT_EQ(0, y.minute()); EXPECT_EQ(0, y.second()); + EXPECT_EQ(weekday::thursday, get_weekday(y)); + EXPECT_EQ(1, get_yearday(y)); } TEST(CivilTime, OutputStream) { @@ -1036,7 +1046,7 @@ TEST(CivilTime, LeapYears) { TEST(CivilTime, FirstThursdayInMonth) { const civil_day nov1(2014, 11, 1); - const civil_day thursday = prev_weekday(nov1, weekday::thursday) + 7; + const civil_day thursday = next_weekday(nov1 - 1, weekday::thursday); EXPECT_EQ("2014-11-06", Format(thursday)); // Bonus: Date of Thanksgiving in the United States @@ -1047,5 +1057,5 @@ TEST(CivilTime, FirstThursdayInMonth) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 070abd26..aa5af020 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.cc +++ b/absl/time/internal/cctz/src/time_zone_fixed.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,14 +21,14 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { namespace { // The prefix used for the internal names of fixed-offset zones. -const char kFixedOffsetPrefix[] = "Fixed/UTC"; +const char kFixedZonePrefix[] = "Fixed/UTC"; const char kDigits[] = "0123456789"; @@ -56,11 +56,11 @@ bool FixedOffsetFromName(const std::string& name, seconds* offset) { return true; } - const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1; - const char* const ep = kFixedOffsetPrefix + prefix_len; + const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; + const char* const ep = kFixedZonePrefix + prefix_len; if (name.size() != prefix_len + 9) // <prefix>+99:99:99 return false; - if (!std::equal(kFixedOffsetPrefix, ep, name.begin())) + if (!std::equal(kFixedZonePrefix, ep, name.begin())) return false; const char* np = name.data() + prefix_len; if (np[0] != '+' && np[0] != '-') @@ -103,9 +103,9 @@ std::string FixedOffsetToName(const seconds& offset) { } int hours = minutes / 60; minutes %= 60; - char buf[sizeof(kFixedOffsetPrefix) - 1 + sizeof("-24:00:00")]; - std::strcpy(buf, kFixedOffsetPrefix); - char* ep = buf + sizeof(kFixedOffsetPrefix) - 1; + const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; + char buf[prefix_len + sizeof("-24:00:00")]; + char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf); *ep++ = sign; ep = Format02d(ep, hours); *ep++ = ':'; @@ -119,7 +119,7 @@ std::string FixedOffsetToName(const seconds& offset) { std::string FixedOffsetToAbbr(const seconds& offset) { std::string abbr = FixedOffsetToName(offset); - const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1; + const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99 abbr.erase(0, prefix_len); // +99:99:99 abbr.erase(6, 1); // +99:9999 @@ -136,5 +136,5 @@ std::string FixedOffsetToAbbr(const seconds& offset) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 dbb2958e..15ea3bdf 100644 --- a/absl/time/internal/cctz/src/time_zone_fixed.h +++ b/absl/time/internal/cctz/src/time_zone_fixed.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -45,7 +45,7 @@ std::string FixedOffsetToAbbr(const seconds& offset); } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 02ecb2cf..ee2ffac0 100644 --- a/absl/time/internal/cctz/src/time_zone_format.cc +++ b/absl/time/internal/cctz/src/time_zone_format.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -13,13 +13,23 @@ // limitations under the License. #if !defined(HAS_STRPTIME) -# if !defined(_MSC_VER) +# if !defined(_MSC_VER) && !defined(__MINGW32__) # define HAS_STRPTIME 1 // assume everyone has strptime() except windows # endif #endif +#if defined(HAS_STRPTIME) && HAS_STRPTIME +# if !defined(_XOPEN_SOURCE) +# define _XOPEN_SOURCE // Definedness suffices for strptime. +# endif +#endif + #include "absl/time/internal/cctz/include/cctz/time_zone.h" +// Include time.h directly since, by C++ standards, ctime doesn't have to +// declare strptime. +#include <time.h> + #include <cctype> #include <chrono> #include <cstddef> @@ -38,7 +48,7 @@ #include "time_zone_if.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { namespace detail { @@ -73,7 +83,7 @@ std::tm ToTM(const time_zone::absolute_lookup& al) { tm.tm_year = static_cast<int>(al.cs.year() - 1900); } - switch (get_weekday(civil_day(al.cs))) { + switch (get_weekday(al.cs)) { case weekday::sunday: tm.tm_wday = 0; break; @@ -96,7 +106,7 @@ std::tm ToTM(const time_zone::absolute_lookup& al) { tm.tm_wday = 6; break; } - tm.tm_yday = get_yearday(civil_day(al.cs)) - 1; + tm.tm_yday = get_yearday(al.cs) - 1; tm.tm_isdst = al.is_dst ? 1 : 0; return tm; } @@ -150,15 +160,25 @@ char* FormatOffset(char* ep, int offset, const char* mode) { offset = -offset; // bounded by 24h so no overflow sign = '-'; } - char sep = mode[0]; - if (sep != '\0' && mode[1] == '*') { - ep = Format02d(ep, offset % 60); + const int seconds = offset % 60; + const int minutes = (offset /= 60) % 60; + const int hours = offset /= 60; + const char sep = mode[0]; + const bool ext = (sep != '\0' && mode[1] == '*'); + const bool ccc = (ext && mode[2] == ':'); + if (ext && (!ccc || seconds != 0)) { + ep = Format02d(ep, seconds); *--ep = sep; + } else { + // If we're not rendering seconds, sub-minute negative offsets + // should get a positive sign (e.g., offset=-10s => "+00:00"). + if (hours == 0 && minutes == 0) sign = '+'; + } + if (!ccc || minutes != 0 || seconds != 0) { + ep = Format02d(ep, minutes); + if (sep != '\0') *--ep = sep; } - int minutes = offset / 60; - ep = Format02d(ep, minutes % 60); - if (sep != '\0') *--ep = sep; - ep = Format02d(ep, minutes / 60); + ep = Format02d(ep, hours); *--ep = sign; return ep; } @@ -385,6 +405,44 @@ std::string format(const std::string& format, const time_point<seconds>& tp, continue; } + // More complex specifiers that we handle ourselves. + if (*cur == ':' && cur + 1 != end) { + if (*(cur + 1) == 'z') { + // Formats %:z. + if (cur - 1 != pending) { + FormatTM(&result, std::string(pending, cur - 1), tm); + } + bp = FormatOffset(ep, al.offset, ":"); + result.append(bp, static_cast<std::size_t>(ep - bp)); + pending = cur += 2; + continue; + } + if (*(cur + 1) == ':' && cur + 2 != end) { + if (*(cur + 2) == 'z') { + // Formats %::z. + if (cur - 1 != pending) { + FormatTM(&result, std::string(pending, cur - 1), tm); + } + bp = FormatOffset(ep, al.offset, ":*"); + result.append(bp, static_cast<std::size_t>(ep - bp)); + pending = cur += 3; + continue; + } + if (*(cur + 2) == ':' && cur + 3 != end) { + if (*(cur + 3) == 'z') { + // Formats %:::z. + if (cur - 1 != pending) { + FormatTM(&result, std::string(pending, cur - 1), tm); + } + bp = FormatOffset(ep, al.offset, ":*:"); + result.append(bp, static_cast<std::size_t>(ep - bp)); + pending = cur += 4; + continue; + } + } + } + } + // Loop if there is no E modifier. if (*cur != 'E' || ++cur == end) continue; @@ -669,17 +727,27 @@ bool parse(const std::string& format, const std::string& input, &percent_s); if (data != nullptr) saw_percent_s = true; continue; + case ':': + if (fmt[0] == 'z' || + (fmt[0] == ':' && + (fmt[1] == 'z' || (fmt[1] == ':' && fmt[2] == 'z')))) { + data = ParseOffset(data, ":", &offset); + if (data != nullptr) saw_offset = true; + fmt += (fmt[0] == 'z') ? 1 : (fmt[1] == 'z') ? 2 : 3; + continue; + } + break; case '%': data = (*data == '%' ? data + 1 : nullptr); continue; case 'E': - if (*fmt == 'z' || (*fmt == '*' && *(fmt + 1) == 'z')) { + if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) { data = ParseOffset(data, ":", &offset); if (data != nullptr) saw_offset = true; - fmt += (*fmt == 'z') ? 1 : 2; + fmt += (fmt[0] == 'z') ? 1 : 2; continue; } - if (*fmt == '*' && *(fmt + 1) == 'S') { + if (fmt[0] == '*' && fmt[1] == 'S') { data = ParseInt(data, 2, 0, 60, &tm.tm_sec); if (data != nullptr && *data == '.') { data = ParseSubSeconds(data + 1, &subseconds); @@ -687,14 +755,14 @@ bool parse(const std::string& format, const std::string& input, fmt += 2; continue; } - if (*fmt == '*' && *(fmt + 1) == 'f') { + if (fmt[0] == '*' && fmt[1] == 'f') { if (data != nullptr && std::isdigit(*data)) { data = ParseSubSeconds(data, &subseconds); } fmt += 2; continue; } - if (*fmt == '4' && *(fmt + 1) == 'Y') { + if (fmt[0] == '4' && fmt[1] == 'Y') { const char* bp = data; data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year); if (data != nullptr) { @@ -849,5 +917,5 @@ bool parse(const std::string& format, const std::string& input, } // namespace detail } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 260c56ad..49737445 100644 --- a/absl/time/internal/cctz/src/time_zone_format_test.cc +++ b/absl/time/internal/cctz/src/time_zone_format_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -26,7 +26,7 @@ namespace chrono = std::chrono; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -65,17 +65,6 @@ void TestFormatSpecifier(time_point<D> tp, time_zone tz, const std::string& fmt, EXPECT_EQ("xxx " + ans + " yyy", format("xxx " + fmt + " yyy", tp, tz)); } -// These tests sometimes run on platforms that have zoneinfo data so old -// that the transition we are attempting to check does not exist, most -// notably Android emulators. Fortunately, AndroidZoneInfoSource supports -// time_zone::version() so, in cases where we've learned that it matters, -// we can make the check conditionally. -int VersionCmp(time_zone tz, const std::string& target) { - std::string version = tz.version(); - if (version.empty() && !target.empty()) return 1; // unknown > known - return version.compare(target); -} - } // namespace // @@ -175,7 +164,9 @@ TEST(Format, PosixConversions) { TestFormatSpecifier(tp, tz, "%M", "00"); TestFormatSpecifier(tp, tz, "%S", "00"); TestFormatSpecifier(tp, tz, "%U", "00"); +#if !defined(__EMSCRIPTEN__) TestFormatSpecifier(tp, tz, "%w", "4"); // 4=Thursday +#endif TestFormatSpecifier(tp, tz, "%W", "00"); TestFormatSpecifier(tp, tz, "%y", "70"); TestFormatSpecifier(tp, tz, "%Y", "1970"); @@ -437,51 +428,165 @@ TEST(Format, CompareExtendSecondsVsSubseconds) { } TEST(Format, ExtendedOffset) { - auto tp = chrono::system_clock::from_time_t(0); + const auto tp = chrono::system_clock::from_time_t(0); - time_zone tz = utc_time_zone(); + auto tz = fixed_time_zone(absl::time_internal::cctz::seconds::zero()); + TestFormatSpecifier(tp, tz, "%z", "+0000"); + TestFormatSpecifier(tp, tz, "%:z", "+00:00"); TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); - EXPECT_TRUE(load_time_zone("America/New_York", &tz)); - TestFormatSpecifier(tp, tz, "%Ez", "-05:00"); - - EXPECT_TRUE(load_time_zone("America/Los_Angeles", &tz)); - TestFormatSpecifier(tp, tz, "%Ez", "-08:00"); + tz = fixed_time_zone(chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "+0000"); + TestFormatSpecifier(tp, tz, "%:z", "+00:00"); + TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); - EXPECT_TRUE(load_time_zone("Australia/Sydney", &tz)); - TestFormatSpecifier(tp, tz, "%Ez", "+10:00"); + tz = fixed_time_zone(-chrono::seconds(56)); // NOTE: +00:00 + TestFormatSpecifier(tp, tz, "%z", "+0000"); + TestFormatSpecifier(tp, tz, "%:z", "+00:00"); + TestFormatSpecifier(tp, tz, "%Ez", "+00:00"); - EXPECT_TRUE(load_time_zone("Africa/Monrovia", &tz)); - // The true offset is -00:44:30 but %z only gives (truncated) minutes. - TestFormatSpecifier(tp, tz, "%z", "-0044"); - TestFormatSpecifier(tp, tz, "%Ez", "-00:44"); + tz = fixed_time_zone(chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%z", "+0034"); + TestFormatSpecifier(tp, tz, "%:z", "+00:34"); + TestFormatSpecifier(tp, tz, "%Ez", "+00:34"); + + tz = fixed_time_zone(-chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%z", "-0034"); + TestFormatSpecifier(tp, tz, "%:z", "-00:34"); + TestFormatSpecifier(tp, tz, "%Ez", "-00:34"); + + tz = fixed_time_zone(chrono::minutes(34) + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "+0034"); + TestFormatSpecifier(tp, tz, "%:z", "+00:34"); + TestFormatSpecifier(tp, tz, "%Ez", "+00:34"); + + tz = fixed_time_zone(-chrono::minutes(34) - chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "-0034"); + TestFormatSpecifier(tp, tz, "%:z", "-00:34"); + TestFormatSpecifier(tp, tz, "%Ez", "-00:34"); + + tz = fixed_time_zone(chrono::hours(12)); + TestFormatSpecifier(tp, tz, "%z", "+1200"); + TestFormatSpecifier(tp, tz, "%:z", "+12:00"); + TestFormatSpecifier(tp, tz, "%Ez", "+12:00"); + + tz = fixed_time_zone(-chrono::hours(12)); + TestFormatSpecifier(tp, tz, "%z", "-1200"); + TestFormatSpecifier(tp, tz, "%:z", "-12:00"); + TestFormatSpecifier(tp, tz, "%Ez", "-12:00"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "+1200"); + TestFormatSpecifier(tp, tz, "%:z", "+12:00"); + TestFormatSpecifier(tp, tz, "%Ez", "+12:00"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "-1200"); + TestFormatSpecifier(tp, tz, "%:z", "-12:00"); + TestFormatSpecifier(tp, tz, "%Ez", "-12:00"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%z", "+1234"); + TestFormatSpecifier(tp, tz, "%:z", "+12:34"); + TestFormatSpecifier(tp, tz, "%Ez", "+12:34"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%z", "-1234"); + TestFormatSpecifier(tp, tz, "%:z", "-12:34"); + TestFormatSpecifier(tp, tz, "%Ez", "-12:34"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34) + + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "+1234"); + TestFormatSpecifier(tp, tz, "%:z", "+12:34"); + TestFormatSpecifier(tp, tz, "%Ez", "+12:34"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34) - + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%z", "-1234"); + TestFormatSpecifier(tp, tz, "%:z", "-12:34"); + TestFormatSpecifier(tp, tz, "%Ez", "-12:34"); } TEST(Format, ExtendedSecondOffset) { - const time_zone utc = utc_time_zone(); - time_point<chrono::seconds> tp; - time_zone tz; - - EXPECT_TRUE(load_time_zone("America/New_York", &tz)); - tp = convert(civil_second(1883, 11, 18, 16, 59, 59), utc); - if (tz.lookup(tp).offset == -5 * 60 * 60) { - // It looks like the tzdata is only 32 bit (probably macOS), - // which bottoms out at 1901-12-13T20:45:52+00:00. - } else { - TestFormatSpecifier(tp, tz, "%E*z", "-04:56:02"); - TestFormatSpecifier(tp, tz, "%Ez", "-04:56"); - } - tp += chrono::seconds(1); - TestFormatSpecifier(tp, tz, "%E*z", "-05:00:00"); - - EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz)); - tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc); - if (VersionCmp(tz, "2016g") >= 0) { - TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19"); - TestFormatSpecifier(tp, tz, "%Ez", "+04:31"); - } - tp += chrono::seconds(1); - TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00"); + const auto tp = chrono::system_clock::from_time_t(0); + + auto tz = fixed_time_zone(absl::time_internal::cctz::seconds::zero()); + TestFormatSpecifier(tp, tz, "%E*z", "+00:00:00"); + TestFormatSpecifier(tp, tz, "%::z", "+00:00:00"); + TestFormatSpecifier(tp, tz, "%:::z", "+00"); + + tz = fixed_time_zone(chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "+00:00:56"); + TestFormatSpecifier(tp, tz, "%::z", "+00:00:56"); + TestFormatSpecifier(tp, tz, "%:::z", "+00:00:56"); + + tz = fixed_time_zone(-chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "-00:00:56"); + TestFormatSpecifier(tp, tz, "%::z", "-00:00:56"); + TestFormatSpecifier(tp, tz, "%:::z", "-00:00:56"); + + tz = fixed_time_zone(chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%E*z", "+00:34:00"); + TestFormatSpecifier(tp, tz, "%::z", "+00:34:00"); + TestFormatSpecifier(tp, tz, "%:::z", "+00:34"); + + tz = fixed_time_zone(-chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%E*z", "-00:34:00"); + TestFormatSpecifier(tp, tz, "%::z", "-00:34:00"); + TestFormatSpecifier(tp, tz, "%:::z", "-00:34"); + + tz = fixed_time_zone(chrono::minutes(34) + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "+00:34:56"); + TestFormatSpecifier(tp, tz, "%::z", "+00:34:56"); + TestFormatSpecifier(tp, tz, "%:::z", "+00:34:56"); + + tz = fixed_time_zone(-chrono::minutes(34) - chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "-00:34:56"); + TestFormatSpecifier(tp, tz, "%::z", "-00:34:56"); + TestFormatSpecifier(tp, tz, "%:::z", "-00:34:56"); + + tz = fixed_time_zone(chrono::hours(12)); + TestFormatSpecifier(tp, tz, "%E*z", "+12:00:00"); + TestFormatSpecifier(tp, tz, "%::z", "+12:00:00"); + TestFormatSpecifier(tp, tz, "%:::z", "+12"); + + tz = fixed_time_zone(-chrono::hours(12)); + TestFormatSpecifier(tp, tz, "%E*z", "-12:00:00"); + TestFormatSpecifier(tp, tz, "%::z", "-12:00:00"); + TestFormatSpecifier(tp, tz, "%:::z", "-12"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "+12:00:56"); + TestFormatSpecifier(tp, tz, "%::z", "+12:00:56"); + TestFormatSpecifier(tp, tz, "%:::z", "+12:00:56"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "-12:00:56"); + TestFormatSpecifier(tp, tz, "%::z", "-12:00:56"); + TestFormatSpecifier(tp, tz, "%:::z", "-12:00:56"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%E*z", "+12:34:00"); + TestFormatSpecifier(tp, tz, "%::z", "+12:34:00"); + TestFormatSpecifier(tp, tz, "%:::z", "+12:34"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34)); + TestFormatSpecifier(tp, tz, "%E*z", "-12:34:00"); + TestFormatSpecifier(tp, tz, "%::z", "-12:34:00"); + TestFormatSpecifier(tp, tz, "%:::z", "-12:34"); + + tz = fixed_time_zone(chrono::hours(12) + chrono::minutes(34) + + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "+12:34:56"); + TestFormatSpecifier(tp, tz, "%::z", "+12:34:56"); + TestFormatSpecifier(tp, tz, "%:::z", "+12:34:56"); + + tz = fixed_time_zone(-chrono::hours(12) - chrono::minutes(34) - + chrono::seconds(56)); + TestFormatSpecifier(tp, tz, "%E*z", "-12:34:56"); + TestFormatSpecifier(tp, tz, "%::z", "-12:34:56"); + TestFormatSpecifier(tp, tz, "%:::z", "-12:34:56"); } TEST(Format, ExtendedYears) { @@ -1161,25 +1266,6 @@ TEST(Parse, ExtendedOffset) { const time_zone utc = utc_time_zone(); time_point<absl::time_internal::cctz::seconds> tp; - // %z against +-HHMM. - EXPECT_TRUE(parse("%z", "+0000", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%z", "-1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); - EXPECT_TRUE(parse("%z", "+1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); - EXPECT_FALSE(parse("%z", "-123", utc, &tp)); - - // %z against +-HH. - EXPECT_TRUE(parse("%z", "+00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%z", "-12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp); - EXPECT_TRUE(parse("%z", "+12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp); - EXPECT_FALSE(parse("%z", "-1", utc, &tp)); - - // %Ez against +-HH:MM. EXPECT_TRUE(parse("%Ez", "+00:00", utc, &tp)); EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); EXPECT_TRUE(parse("%Ez", "-12:34", utc, &tp)); @@ -1188,91 +1274,70 @@ TEST(Parse, ExtendedOffset) { EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); EXPECT_FALSE(parse("%Ez", "-12:3", utc, &tp)); - // %Ez against +-HHMM. - EXPECT_TRUE(parse("%Ez", "+0000", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%Ez", "-1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); - EXPECT_TRUE(parse("%Ez", "+1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); - EXPECT_FALSE(parse("%Ez", "-123", utc, &tp)); - - // %Ez against +-HH. - EXPECT_TRUE(parse("%Ez", "+00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%Ez", "-12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp); - EXPECT_TRUE(parse("%Ez", "+12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp); - EXPECT_FALSE(parse("%Ez", "-1", utc, &tp)); + for (auto fmt : {"%Ez", "%z"}) { + EXPECT_TRUE(parse(fmt, "+0000", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-1234", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+1234", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-123", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+00", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-12", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+12", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-1", utc, &tp)); + } } TEST(Parse, ExtendedSecondOffset) { const time_zone utc = utc_time_zone(); time_point<absl::time_internal::cctz::seconds> tp; - // %Ez against +-HH:MM:SS. - EXPECT_TRUE(parse("%Ez", "+00:00:00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%Ez", "-12:34:56", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); - EXPECT_TRUE(parse("%Ez", "+12:34:56", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); - EXPECT_FALSE(parse("%Ez", "-12:34:5", utc, &tp)); - - // %Ez against +-HHMMSS. - EXPECT_TRUE(parse("%Ez", "+000000", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%Ez", "-123456", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); - EXPECT_TRUE(parse("%Ez", "+123456", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); - EXPECT_FALSE(parse("%Ez", "-12345", utc, &tp)); - - // %E*z against +-HH:MM:SS. - EXPECT_TRUE(parse("%E*z", "+00:00:00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%E*z", "-12:34:56", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); - EXPECT_TRUE(parse("%E*z", "+12:34:56", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); - EXPECT_FALSE(parse("%E*z", "-12:34:5", utc, &tp)); - - // %E*z against +-HHMMSS. - EXPECT_TRUE(parse("%E*z", "+000000", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%E*z", "-123456", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); - EXPECT_TRUE(parse("%E*z", "+123456", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); - EXPECT_FALSE(parse("%E*z", "-12345", utc, &tp)); - - // %E*z against +-HH:MM. - EXPECT_TRUE(parse("%E*z", "+00:00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%E*z", "-12:34", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); - EXPECT_TRUE(parse("%E*z", "+12:34", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); - EXPECT_FALSE(parse("%E*z", "-12:3", utc, &tp)); - - // %E*z against +-HHMM. - EXPECT_TRUE(parse("%E*z", "+0000", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%E*z", "-1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); - EXPECT_TRUE(parse("%E*z", "+1234", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); - EXPECT_FALSE(parse("%E*z", "-123", utc, &tp)); - - // %E*z against +-HH. - EXPECT_TRUE(parse("%E*z", "+00", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); - EXPECT_TRUE(parse("%E*z", "-12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp); - EXPECT_TRUE(parse("%E*z", "+12", utc, &tp)); - EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp); - EXPECT_FALSE(parse("%E*z", "-1", utc, &tp)); + for (auto fmt : {"%Ez", "%E*z", "%:z", "%::z", "%:::z"}) { + EXPECT_TRUE(parse(fmt, "+00:00:00", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-12:34:56", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); + EXPECT_TRUE(parse(fmt, "+12:34:56", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); + EXPECT_FALSE(parse(fmt, "-12:34:5", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+000000", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-123456", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 56), utc), tp); + EXPECT_TRUE(parse(fmt, "+123456", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 25, 4), utc), tp); + EXPECT_FALSE(parse(fmt, "-12345", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+00:00", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-12:34", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+12:34", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-12:3", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+0000", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-1234", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 34, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+1234", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 11, 26, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-123", utc, &tp)); + + EXPECT_TRUE(parse(fmt, "+00", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 0, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "-12", utc, &tp)); + EXPECT_EQ(convert(civil_second(1970, 1, 1, 12, 0, 0), utc), tp); + EXPECT_TRUE(parse(fmt, "+12", utc, &tp)); + EXPECT_EQ(convert(civil_second(1969, 12, 31, 12, 0, 0), utc), tp); + EXPECT_FALSE(parse(fmt, "-1", utc, &tp)); + } } TEST(Parse, ExtendedYears) { @@ -1391,6 +1456,10 @@ TEST(FormatParse, RoundTrip) { #if defined(_WIN32) || defined(_WIN64) // Initial investigations indicate the %c does not roundtrip on Windows. // TODO: Figure out what is going on here (perhaps a locale problem). +#elif defined(__EMSCRIPTEN__) + // strftime() and strptime() use different defintions for "%c" under + // emscripten (see https://github.com/kripken/emscripten/pull/7491), + // causing its round-trip test to fail. #else // Even though we don't know what %c will produce, it should roundtrip, // but only in the 0-offset timezone. @@ -1424,5 +1493,5 @@ TEST(FormatParse, RoundTripDistantPast) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 f7c36b2b..75d23da5 100644 --- a/absl/time/internal/cctz/src/time_zone_if.cc +++ b/absl/time/internal/cctz/src/time_zone_if.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,7 +17,7 @@ #include "time_zone_libc.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -39,5 +39,5 @@ TimeZoneIf::~TimeZoneIf() {} } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 9886f2c5..0081d990 100644 --- a/absl/time/internal/cctz/src/time_zone_if.h +++ b/absl/time/internal/cctz/src/time_zone_if.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -68,7 +68,7 @@ inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 a4e42916..60911f0b 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.cc +++ b/absl/time/internal/cctz/src/time_zone_impl.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "time_zone_fixed.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -106,5 +106,5 @@ const time_zone::Impl* time_zone::Impl::UTCImpl() { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 7da2e99d..33d3f6bd 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.h +++ b/absl/time/internal/cctz/src/time_zone_impl.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ #include "time_zone_info.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -86,7 +86,7 @@ class time_zone::Impl { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 e19d1d2d..b45d82fa 100644 --- a/absl/time/internal/cctz/src/time_zone_info.cc +++ b/absl/time/internal/cctz/src/time_zone_info.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,7 +25,7 @@ // a grain of salt. // // For more information see tzfile(5), http://www.iana.org/time-zones, or -// http://en.wikipedia.org/wiki/Zoneinfo. +// https://en.wikipedia.org/wiki/Zoneinfo. // // Note that we assume the proleptic Gregorian calendar and 60-second // minutes throughout. @@ -50,7 +50,7 @@ #include "time_zone_posix.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -240,8 +240,8 @@ bool TimeZoneInfo::Header::Build(const tzhead& tzh) { leapcnt = static_cast<std::size_t>(v); if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false; ttisstdcnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_ttisgmtcnt)) < 0) return false; - ttisgmtcnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_ttisutcnt)) < 0) return false; + ttisutcnt = static_cast<std::size_t>(v); return true; } @@ -254,7 +254,7 @@ std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const { len += 1 * charcnt; // abbreviations len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC len += 1 * ttisstdcnt; // UTC/local indicators - len += 1 * ttisgmtcnt; // standard/wall indicators + len += 1 * ttisutcnt; // standard/wall indicators return len; } @@ -428,7 +428,7 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { } if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false; - if (hdr.ttisgmtcnt != 0 && hdr.ttisgmtcnt != hdr.typecnt) + if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false; // Read the data into a local buffer. @@ -499,16 +499,16 @@ bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) { // that isn't the case here (see "zic -p"). bp += (8 + 4) * hdr.leapcnt; // leap-time + TAI-UTC bp += 1 * hdr.ttisstdcnt; // UTC/local indicators - bp += 1 * hdr.ttisgmtcnt; // standard/wall indicators + bp += 1 * hdr.ttisutcnt; // standard/wall indicators assert(bp == tbuf.data() + tbuf.size()); future_spec_.clear(); if (tzh.tzh_version[0] != '\0') { // Snarf up the NL-enclosed future POSIX spec. Note // that version '3' files utilize an extended format. - auto get_char = [](ZoneInfoSource* zip) -> int { + auto get_char = [](ZoneInfoSource* azip) -> int { unsigned char ch; // all non-EOF results are positive - return (zip->Read(&ch, 1) == 1) ? ch : EOF; + return (azip->Read(&ch, 1) == 1) ? ch : EOF; }; if (get_char(zip) != '\n') return false; @@ -683,7 +683,6 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( // Use of the "file:" prefix is intended for testing purposes only. if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5)); -#if defined(__ANDROID__) // See Android's libc/tzcode/bionic.cpp for additional information. for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", "/system/usr/share/zoneinfo/tzdata"}) { @@ -718,7 +717,7 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( } } } -#endif // __ANDROID__ + return nullptr; } @@ -922,7 +921,7 @@ bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp, ++begin; } std::int_fast64_t unix_time = ToUnixSeconds(tp); - const Transition target = { unix_time }; + const Transition target = {unix_time, 0, civil_second(), civil_second()}; const Transition* tr = std::upper_bound(begin, end, target, Transition::ByUnixTime()); for (; tr != end; ++tr) { // skip no-op transitions @@ -957,7 +956,7 @@ bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp, } unix_time += 1; // ceils } - const Transition target = { unix_time }; + const Transition target = {unix_time, 0, civil_second(), civil_second()}; const Transition* tr = std::lower_bound(begin, end, target, Transition::ByUnixTime()); for (; tr != begin; --tr) { // skip no-op transitions @@ -974,5 +973,5 @@ bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp, } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 e7a7d02f..c724fa8f 100644 --- a/absl/time/internal/cctz/src/time_zone_info.h +++ b/absl/time/internal/cctz/src/time_zone_info.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "tzfile.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -89,7 +89,7 @@ class TimeZoneInfo : public TimeZoneIf { std::size_t charcnt; // zone abbreviation characters std::size_t leapcnt; // leap seconds (we expect none) std::size_t ttisstdcnt; // UTC/local indicators (unused) - std::size_t ttisgmtcnt; // standard/wall indicators (unused) + std::size_t ttisutcnt; // standard/wall indicators (unused) bool Build(const tzhead& tzh); std::size_t DataLength(std::size_t time_len) const; @@ -132,7 +132,7 @@ class TimeZoneInfo : public TimeZoneIf { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 3dd75b50..cfe7d55c 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.cc +++ b/absl/time/internal/cctz/src/time_zone_libc.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,70 +21,87 @@ #include <chrono> #include <ctime> #include <limits> -#include <tuple> #include <utility> #include "absl/time/internal/cctz/include/cctz/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { namespace { -// .first is seconds east of UTC; .second is the time-zone abbreviation. -using OffsetAbbr = std::pair<int, const char*>; - -// Defines a function that can be called as follows: -// -// std::tm tm = ...; -// OffsetAbbr off_abbr = get_offset_abbr(tm); -// #if defined(_WIN32) || defined(_WIN64) // Uses the globals: '_timezone', '_dstbias' and '_tzname'. -OffsetAbbr get_offset_abbr(const std::tm& tm) { +auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) { const bool is_dst = tm.tm_isdst > 0; - const int off = _timezone + (is_dst ? _dstbias : 0); - const char* abbr = _tzname[is_dst]; - return {off, abbr}; + return _timezone + (is_dst ? _dstbias : 0); +} +auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) { + const bool is_dst = tm.tm_isdst > 0; + return _tzname[is_dst]; } #elif defined(__sun) // Uses the globals: 'timezone', 'altzone' and 'tzname'. -OffsetAbbr get_offset_abbr(const std::tm& tm) { +auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) { const bool is_dst = tm.tm_isdst > 0; - const int off = is_dst ? altzone : timezone; - const char* abbr = tzname[is_dst]; - return {off, abbr}; + return is_dst ? altzone : timezone; +} +auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) { + const bool is_dst = tm.tm_isdst > 0; + return tzname[is_dst]; } #elif defined(__native_client__) || defined(__myriad2__) || \ defined(__EMSCRIPTEN__) // Uses the globals: 'timezone' and 'tzname'. -OffsetAbbr get_offset_abbr(const std::tm& tm) { +auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) { + const bool is_dst = tm.tm_isdst > 0; + return _timezone + (is_dst ? 60 * 60 : 0); +} +auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) { const bool is_dst = tm.tm_isdst > 0; - const int off = _timezone + (is_dst ? 60 * 60 : 0); - const char* abbr = tzname[is_dst]; - return {off, abbr}; + return tzname[is_dst]; +} +#else +// Adapt to different spellings of the struct std::tm extension fields. +#if defined(tm_gmtoff) +auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) { + return tm.tm_gmtoff; +} +#elif defined(__tm_gmtoff) +auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) { + return tm.__tm_gmtoff; +} +#else +template <typename T> +auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) { + return tm.tm_gmtoff; +} +template <typename T> +auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) { + return tm.__tm_gmtoff; +} +#endif // tm_gmtoff +#if defined(tm_zone) +auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { + return tm.tm_zone; +} +#elif defined(__tm_zone) +auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) { + return tm.__tm_zone; } #else -// -// Returns an OffsetAbbr using std::tm fields with various spellings. -// -#if !defined(tm_gmtoff) && !defined(tm_zone) template <typename T> -OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::tm_gmtoff) = nullptr, - decltype(&T::tm_zone) = nullptr) { - return {tm.tm_gmtoff, tm.tm_zone}; +auto tm_zone(const T& tm) -> decltype(tm.tm_zone) { + return tm.tm_zone; } -#endif // !defined(tm_gmtoff) && !defined(tm_zone) -#if !defined(__tm_gmtoff) && !defined(__tm_zone) template <typename T> -OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::__tm_gmtoff) = nullptr, - decltype(&T::__tm_zone) = nullptr) { - return {tm.__tm_gmtoff, tm.__tm_zone}; +auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) { + return tm.__tm_zone; } -#endif // !defined(__tm_gmtoff) && !defined(__tm_zone) +#endif // tm_zone #endif inline std::tm* gm_time(const std::time_t *timep, std::tm *result) { @@ -127,7 +144,7 @@ bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) { return false; } } - *off = get_offset_abbr(tm).first; + *off = static_cast<int>(tm_gmtoff(tm)); return true; } @@ -138,7 +155,7 @@ std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) { while (lo + 1 != hi) { const std::time_t mid = lo + (hi - lo) / 2; if (std::tm* tmp = local_time(&mid, &tm)) { - if (get_offset_abbr(*tmp).first == offset) { + if (tm_gmtoff(*tmp) == offset) { hi = mid; } else { lo = mid; @@ -148,7 +165,7 @@ std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) { // ignoring all failed conversions. Slow, but never really happens. while (++lo != hi) { if (std::tm* tmp = local_time(&lo, &tm)) { - if (get_offset_abbr(*tmp).first == offset) break; + if (tm_gmtoff(*tmp) == offset) break; } } return lo; @@ -194,8 +211,8 @@ time_zone::absolute_lookup TimeZoneLibC::BreakTime( const year_t year = tmp->tm_year + year_t{1900}; al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); - std::tie(al.offset, al.abbr) = get_offset_abbr(*tmp); - if (!local_) al.abbr = "UTC"; // as expected by cctz + al.offset = static_cast<int>(tm_gmtoff(*tmp)); + al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz al.is_dst = tmp->tm_isdst > 0; return al; } @@ -268,13 +285,13 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } -bool TimeZoneLibC::NextTransition(const time_point<seconds>& tp, - time_zone::civil_transition* trans) const { +bool TimeZoneLibC::NextTransition(const time_point<seconds>&, + time_zone::civil_transition*) const { return false; } -bool TimeZoneLibC::PrevTransition(const time_point<seconds>& tp, - time_zone::civil_transition* trans) const { +bool TimeZoneLibC::PrevTransition(const time_point<seconds>&, + time_zone::civil_transition*) const { return false; } @@ -288,5 +305,5 @@ std::string TimeZoneLibC::Description() const { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 67372d45..4b1ccb06 100644 --- a/absl/time/internal/cctz/src/time_zone_libc.h +++ b/absl/time/internal/cctz/src/time_zone_libc.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include "time_zone_if.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -49,7 +49,7 @@ class TimeZoneLibC : public TimeZoneIf { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 711d705a..772a9375 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,10 +16,16 @@ #if defined(__ANDROID__) #include <sys/system_properties.h> -#if __ANDROID_API__ >= 21 +#if defined(__ANDROID_API__) && __ANDROID_API__ >= 21 #include <dlfcn.h> #endif #endif + +#if defined(__APPLE__) +#include <CoreFoundation/CFTimeZone.h> +#include <vector> +#endif + #include <cstdlib> #include <cstring> #include <string> @@ -28,11 +34,11 @@ #include "time_zone_impl.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { -#if defined(__ANDROID__) && __ANDROID_API__ >= 21 +#if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21 namespace { // Android 'L' removes __system_property_get() from the NDK, however // it is still a hidden symbol in libc so we use dlsym() to access it. @@ -117,6 +123,25 @@ time_zone fixed_time_zone(const seconds& offset) { time_zone local_time_zone() { const char* zone = ":localtime"; +#if defined(__ANDROID__) + char sysprop[PROP_VALUE_MAX]; + if (__system_property_get("persist.sys.timezone", sysprop) > 0) { + zone = sysprop; + } +#endif +#if defined(__APPLE__) + std::vector<char> buffer; + CFTimeZoneRef tz_default = CFTimeZoneCopyDefault(); + if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) { + CFStringEncoding encoding = kCFStringEncodingUTF8; + CFIndex length = CFStringGetLength(tz_name); + buffer.resize(CFStringGetMaximumSizeForEncoding(length, encoding) + 1); + if (CFStringGetCString(tz_name, &buffer[0], buffer.size(), encoding)) { + zone = &buffer[0]; + } + } + CFRelease(tz_default); +#endif // Allow ${TZ} to override to default zone. char* tz_env = nullptr; @@ -125,12 +150,6 @@ time_zone local_time_zone() { #else tz_env = std::getenv("TZ"); #endif -#if defined(__ANDROID__) - char sysprop[PROP_VALUE_MAX]; - if (tz_env == nullptr) - if (__system_property_get("persist.sys.timezone", sysprop) > 0) - tz_env = sysprop; -#endif if (tz_env) zone = tz_env; // We only support the "[:]<zone-name>" form. @@ -166,5 +185,5 @@ time_zone local_time_zone() { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 e0e355ee..1bef7708 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -29,7 +29,7 @@ namespace chrono = std::chrono; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -338,6 +338,7 @@ const char* const kTimeZoneNames[] = { "Asia/Pontianak", "Asia/Pyongyang", "Asia/Qatar", + "Asia/Qostanay", "Asia/Qyzylorda", "Asia/Rangoon", "Asia/Riyadh", @@ -667,6 +668,7 @@ int VersionCmp(time_zone tz, const std::string& target) { } // namespace +#if !defined(__EMSCRIPTEN__) TEST(TimeZones, LoadZonesConcurrently) { std::promise<void> ready_promise; std::shared_future<void> ready_future(ready_promise.get_future()); @@ -714,6 +716,7 @@ TEST(TimeZones, LoadZonesConcurrently) { } EXPECT_LE(failures.size(), max_failures) << testing::PrintToString(failures); } +#endif TEST(TimeZone, NamedTimeZones) { const time_zone utc = utc_time_zone(); @@ -834,7 +837,7 @@ TEST(BreakTime, LocalTimeInUTC) { const time_zone tz = utc_time_zone(); const auto tp = chrono::system_clock::from_time_t(0); ExpectTime(tp, tz, 1970, 1, 1, 0, 0, 0, 0, false, "UTC"); - EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz)))); + EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimeInUTCUnaligned) { @@ -842,7 +845,7 @@ TEST(BreakTime, LocalTimeInUTCUnaligned) { const auto tp = chrono::system_clock::from_time_t(0) - chrono::milliseconds(500); ExpectTime(tp, tz, 1969, 12, 31, 23, 59, 59, 0, false, "UTC"); - EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz)))); + EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimePosix) { @@ -850,7 +853,7 @@ TEST(BreakTime, LocalTimePosix) { const time_zone tz = utc_time_zone(); const auto tp = chrono::system_clock::from_time_t(536457599); ExpectTime(tp, tz, 1986, 12, 31, 23, 59, 59, 0, false, "UTC"); - EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz)))); + EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); } TEST(TimeZoneImpl, LocalTimeInFixed) { @@ -860,28 +863,28 @@ TEST(TimeZoneImpl, LocalTimeInFixed) { const auto tp = chrono::system_clock::from_time_t(0); ExpectTime(tp, tz, 1969, 12, 31, 15, 26, 13, offset.count(), false, "-083347"); - EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz)))); + EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimeInNewYork) { const time_zone tz = LoadZone("America/New_York"); const auto tp = chrono::system_clock::from_time_t(45); ExpectTime(tp, tz, 1969, 12, 31, 19, 0, 45, -5 * 60 * 60, false, "EST"); - EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz)))); + EXPECT_EQ(weekday::wednesday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimeInMTV) { const time_zone tz = LoadZone("America/Los_Angeles"); const auto tp = chrono::system_clock::from_time_t(1380855729); ExpectTime(tp, tz, 2013, 10, 3, 20, 2, 9, -7 * 60 * 60, true, "PDT"); - EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz)))); + EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz))); } TEST(BreakTime, LocalTimeInSydney) { const time_zone tz = LoadZone("Australia/Sydney"); const auto tp = chrono::system_clock::from_time_t(90); ExpectTime(tp, tz, 1970, 1, 1, 10, 1, 30, 10 * 60 * 60, false, "AEST"); - EXPECT_EQ(weekday::thursday, get_weekday(civil_day(convert(tp, tz)))); + EXPECT_EQ(weekday::thursday, get_weekday(convert(tp, tz))); } TEST(MakeTime, TimePointResolution) { @@ -1019,7 +1022,7 @@ TEST(MakeTime, LocalTimeLibC) { // 1) we know how to change the time zone used by localtime()/mktime(), // 2) cctz and localtime()/mktime() will use similar-enough tzdata, and // 3) we have some idea about how mktime() behaves during transitions. -#if defined(__linux__) +#if defined(__linux__) && !defined(__ANDROID__) const char* const ep = getenv("TZ"); std::string tz_name = (ep != nullptr) ? ep : ""; for (const char* const* np = kTimeZoneNames; *np != nullptr; ++np) { @@ -1272,10 +1275,10 @@ TEST(TimeZoneEdgeCase, PacificApia) { // 1325239200 == Sat, 31 Dec 2011 00:00:00 +1400 (+14) auto tp = convert(civil_second(2011, 12, 29, 23, 59, 59), tz); ExpectTime(tp, tz, 2011, 12, 29, 23, 59, 59, -10 * 3600, true, "-10"); - EXPECT_EQ(363, get_yearday(civil_day(convert(tp, tz)))); + EXPECT_EQ(363, get_yearday(convert(tp, tz))); tp += absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, 2011, 12, 31, 0, 0, 0, 14 * 3600, true, "+14"); - EXPECT_EQ(365, get_yearday(civil_day(convert(tp, tz)))); + EXPECT_EQ(365, get_yearday(convert(tp, tz))); } TEST(TimeZoneEdgeCase, AfricaCairo) { @@ -1397,10 +1400,10 @@ TEST(TimeZoneEdgeCase, NegativeYear) { const time_zone tz = utc_time_zone(); auto tp = convert(civil_second(0, 1, 1, 0, 0, 0), tz); ExpectTime(tp, tz, 0, 1, 1, 0, 0, 0, 0 * 3600, false, "UTC"); - EXPECT_EQ(weekday::saturday, get_weekday(civil_day(convert(tp, tz)))); + EXPECT_EQ(weekday::saturday, get_weekday(convert(tp, tz))); tp -= absl::time_internal::cctz::seconds(1); ExpectTime(tp, tz, -1, 12, 31, 23, 59, 59, 0 * 3600, false, "UTC"); - EXPECT_EQ(weekday::friday, get_weekday(civil_day(convert(tp, tz)))); + EXPECT_EQ(weekday::friday, get_weekday(convert(tp, tz))); } TEST(TimeZoneEdgeCase, UTC32bitLimit) { @@ -1431,5 +1434,5 @@ TEST(TimeZoneEdgeCase, UTC5DigitYear) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 960133d7..d1f8ecbe 100644 --- a/absl/time/internal/cctz/src/time_zone_posix.cc +++ b/absl/time/internal/cctz/src/time_zone_posix.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,7 +20,7 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -101,9 +101,9 @@ const char* ParseDateTime(const char* p, PosixTransition* res) { int weekday = 0; if ((p = ParseInt(p + 1, 0, 6, &weekday)) != nullptr) { res->date.fmt = PosixTransition::M; - res->date.m.month = static_cast<int_fast8_t>(month); - res->date.m.week = static_cast<int_fast8_t>(week); - res->date.m.weekday = static_cast<int_fast8_t>(weekday); + res->date.m.month = static_cast<std::int_fast8_t>(month); + res->date.m.week = static_cast<std::int_fast8_t>(week); + res->date.m.weekday = static_cast<std::int_fast8_t>(weekday); } } } @@ -111,13 +111,13 @@ const char* ParseDateTime(const char* p, PosixTransition* res) { int day = 0; if ((p = ParseInt(p + 1, 1, 365, &day)) != nullptr) { res->date.fmt = PosixTransition::J; - res->date.j.day = static_cast<int_fast16_t>(day); + res->date.j.day = static_cast<std::int_fast16_t>(day); } } else { int day = 0; if ((p = ParseInt(p, 0, 365, &day)) != nullptr) { res->date.fmt = PosixTransition::N; - res->date.j.day = static_cast<int_fast16_t>(day); + res->date.n.day = static_cast<std::int_fast16_t>(day); } } } @@ -153,5 +153,5 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) { } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // 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 84c39afb..07e3427a 100644 --- a/absl/time/internal/cctz/src/time_zone_posix.h +++ b/absl/time/internal/cctz/src/time_zone_posix.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -56,7 +56,7 @@ #include <string> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -124,7 +124,7 @@ bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res); } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_ diff --git a/absl/time/internal/cctz/src/tzfile.h b/absl/time/internal/cctz/src/tzfile.h index 4485ba55..51b1f1f3 100644 --- a/absl/time/internal/cctz/src/tzfile.h +++ b/absl/time/internal/cctz/src/tzfile.h @@ -33,6 +33,9 @@ #define TZDEFRULES "posixrules" #endif /* !defined TZDEFRULES */ + +/* See Internet RFC 8536 for more details about the following format. */ + /* ** Each file begins with. . . */ @@ -43,7 +46,7 @@ struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ char tzh_reserved[15]; /* reserved; must be zero */ - char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ + char tzh_ttisutcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_leapcnt[4]; /* coded number of leap seconds */ char tzh_timecnt[4]; /* coded number of transition times */ @@ -66,14 +69,15 @@ struct tzhead { ** one (char [4]) total correction after above ** tzh_ttisstdcnt (char)s indexed by type; if 1, transition ** time is standard time, if 0, -** transition time is wall clock time -** if absent, transition times are -** assumed to be wall clock time -** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition -** time is UT, if 0, -** transition time is local time -** if absent, transition times are +** transition time is local (wall clock) +** time; if absent, transition times are ** assumed to be local time +** tzh_ttisutcnt (char)s indexed by type; if 1, transition +** time is UT, if 0, transition time is +** local time; if absent, transition +** times are assumed to be local time. +** When this is 1, the corresponding +** std/wall indicator must also be 1. */ /* diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc index 61669e7a..b64950cb 100644 --- a/absl/time/internal/cctz/src/zone_info_source.cc +++ b/absl/time/internal/cctz/src/zone_info_source.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,7 @@ #include "absl/time/internal/cctz/include/cctz/zone_info_source.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz { @@ -25,11 +25,11 @@ std::string ZoneInfoSource::Version() const { return std::string(); } } // namespace cctz } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz_extension { @@ -49,35 +49,33 @@ std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory( // A "weak" definition for cctz_extension::zone_info_source_factory. // The user may override this with their own "strong" definition (see // zone_info_source.h). -#if defined(_MSC_VER) +#if !defined(__has_attribute) +#define __has_attribute(x) 0 +#endif +#if __has_attribute(weak) || defined(__GNUC__) +ZoneInfoSourceFactory zone_info_source_factory + __attribute__((weak)) = DefaultFactory; +#elif defined(_MSC_VER) && !defined(_LIBCPP_VERSION) extern ZoneInfoSourceFactory zone_info_source_factory; extern ZoneInfoSourceFactory default_factory; ZoneInfoSourceFactory default_factory = DefaultFactory; #if defined(_M_IX86) #pragma comment( \ linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@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_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@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_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@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_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@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) + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA=?default_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZA") +#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64) #pragma comment( \ linker, \ - "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2018_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@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_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@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_12_18@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@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_12_18@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2018_12_18@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA") + "/alternatename:?zone_info_source_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA=?default_factory@cctz_extension@time_internal@lts_2019_08_08@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@6@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@lts_2019_08_08@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@6@@ZEA") #else #error Unsupported MSVC platform -#endif -#else // _MSC_VER -#if !defined(__has_attribute) -#define __has_attribute(x) 0 -#endif -#if __has_attribute(weak) || defined(__GNUC__) -ZoneInfoSourceFactory zone_info_source_factory - __attribute__((weak)) = DefaultFactory; +#endif // _M_<PLATFORM> #else // Make it a "strong" definition if we have no other choice. ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory; #endif -#endif // _MSC_VER } // namespace cctz_extension } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version index ac954d74..8d4a43ba 100644 --- a/absl/time/internal/cctz/testdata/version +++ b/absl/time/internal/cctz/testdata/version @@ -1 +1 @@ -2018g-9-gf0d2759 +2019b diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra Binary files differindex eaaa818f..697b9933 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers Binary files differindex a5867a67..ae043423 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre Binary files differindex 31cfad77..52753c0f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura Binary files differindex 31cfad77..52753c0f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo Binary files differindex 0272fa1b..d3f81962 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca Binary files differindex 4c570548..245f4ebe 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta Binary files differindex dd75e3e6..850c8f06 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun Binary files differindex 0ea02535..a91f65f2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone Binary files differindex 31cfad77..52753c0f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare Binary files differindex 31cfad77..52753c0f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg Binary files differindex b8b9270a..b1c425da 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba Binary files differindex 83eca03a..625b1acc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum Binary files differindex 549dae27..8ee8cb92 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali Binary files differindex 31cfad77..52753c0f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi Binary files differindex 31cfad77..52753c0f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka Binary files differindex 31cfad77..52753c0f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo Binary files differindex 31cfad77..52753c0f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru Binary files differindex b8b9270a..b1c425da 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane Binary files differindex b8b9270a..b1c425da 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia Binary files differindex 2a154f46..6d688502 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena Binary files differindex 8779590e..a968845e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo Binary files differindex cbdc0450..0c80137c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome Binary files differindex d2a64bd1..59f3759c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli Binary files differindex bd885315..07b393bb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis Binary files differindex 0cd8ffba..427fa563 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek Binary files differindex 67661856..abecd137 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla b/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Anguilla diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua b/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Antigua diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina b/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina Binary files differindex bc9a5228..49381b41 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Araguaina diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires Binary files differindex dfebfb99..260f86a9 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Buenos_Aires diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca Binary files differindex b798105e..0ae222a2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Catamarca diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia Binary files differindex b798105e..0ae222a2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/ComodRivadavia diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba Binary files differindex 5df3cf6e..da4c23a5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Cordoba diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy Binary files differindex 7d2ba91c..604b8566 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Jujuy diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja Binary files differindex 7654aebf..2218e36b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/La_Rioja diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza Binary files differindex 10323564..f9e677f1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Mendoza diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos Binary files differindex 3c849fce..c36587e1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Rio_Gallegos diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta Binary files differindex a4b71c1f..0e797f22 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Salta diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan Binary files differindex 948a3901..2698495b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Juan diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis Binary files differindex acfbbe43..fe50f621 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/San_Luis diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman Binary files differindex 085fc9cc..c954000b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Tucuman diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia Binary files differindex 1fc32567..3643628a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Argentina/Ushuaia diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba b/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba Binary files differindex d3b318d2..f7ab6efc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Aruba diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion b/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion Binary files differindex 831bf843..2f3bbda6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Asuncion diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia Binary files differindex 143eafc2..15808d30 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas Binary files differindex cd531078..896af3f5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Bahia_Banderas diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados b/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados Binary files differindex 7bb7ac4d..9b90e306 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Barbados diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Belem b/absl/time/internal/cctz/testdata/zoneinfo/America/Belem Binary files differindex ab3c8a67..60b5924d 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Belem +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Belem diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Belize b/absl/time/internal/cctz/testdata/zoneinfo/America/Belize Binary files differindex fd693214..851051ae 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Belize +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Belize diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista b/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista Binary files differindex 69e17a00..978c3310 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Boa_Vista diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota b/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota Binary files differindex b7ded8e6..b2647d7a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Bogota diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires b/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires Binary files differindex dfebfb99..260f86a9 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Buenos_Aires diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande b/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande Binary files differindex 495ef456..81206247 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Campo_Grande diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun b/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun Binary files differindex de6930cd..f907f0a5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cancun diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas b/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas Binary files differindex 9abd028f..eedf725e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Caracas diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca b/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca Binary files differindex b798105e..0ae222a2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Catamarca diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne b/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne Binary files differindex ff596578..e5bc06fd 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cayenne diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman b/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman Binary files differindex 55b08346..9964b9a3 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cayman diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua b/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua Binary files differindex b2687241..8ed5f93b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Chihuahua diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba b/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba Binary files differindex 5df3cf6e..da4c23a5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cordoba diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica b/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica Binary files differindex 525a67ea..37cb85e4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Costa_Rica diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Creston b/absl/time/internal/cctz/testdata/zoneinfo/America/Creston Binary files differindex 0fba7417..ca648573 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Creston +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Creston diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba b/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba Binary files differindex 8a4ee7d0..9bea3d40 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Cuiaba diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao b/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao Binary files differindex d3b318d2..f7ab6efc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Curacao diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica b/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Dominica diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe b/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe Binary files differindex 99b7d06e..39d6daeb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Eirunepe diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador b/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador Binary files differindex ac774e83..e2f22304 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/El_Salvador diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza b/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza Binary files differindex e637170a..be57dc20 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Fortaleza diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk b/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk Binary files differindex 4597a621..b9bb063b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Grand_Turk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada b/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Grenada diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe b/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guadeloupe diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala b/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala Binary files differindex 6118b5ce..407138ca 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guatemala diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil b/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil Binary files differindex bf087a0e..0559a7a4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guayaquil diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana b/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana Binary files differindex d1dd2faf..d5dab149 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Guyana diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Havana b/absl/time/internal/cctz/testdata/zoneinfo/America/Havana Binary files differindex 8186060a..b69ac451 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Havana +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Havana diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo b/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo Binary files differindex 26c269d9..791a9fa2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Hermosillo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik b/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik Binary files differindex e107dc44..87bb3552 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Inuvik diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica b/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica Binary files differindex 162306f8..2a9b7fd5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Jamaica diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy b/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy Binary files differindex 7d2ba91c..604b8566 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Jujuy diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk b/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk Binary files differindex d3b318d2..f7ab6efc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Kralendijk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz b/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz Binary files differindex 5e5fec56..a1013724 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/La_Paz diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Lima b/absl/time/internal/cctz/testdata/zoneinfo/America/Lima Binary files differindex d9fec371..3c6529b7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Lima +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Lima diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes b/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes Binary files differindex d3b318d2..f7ab6efc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Lower_Princes diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio b/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio Binary files differindex fec8a8bf..bc8b951d 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Maceio diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Managua b/absl/time/internal/cctz/testdata/zoneinfo/America/Managua Binary files differindex 69256c63..e0242bff 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Managua +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Managua diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus b/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus Binary files differindex b10241e6..63d58f80 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Manaus diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot b/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Marigot diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique b/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique Binary files differindex 79716de5..8df43dcf 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Martinique diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros Binary files differindex 5c59984d..047968df 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Matamoros diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan b/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan Binary files differindex 43ee12d8..e4a78574 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Mazatlan diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza b/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza Binary files differindex 10323564..f9e677f1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Mendoza diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Merida b/absl/time/internal/cctz/testdata/zoneinfo/America/Merida Binary files differindex b46298e1..ea852da3 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Merida +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Merida diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla b/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla Binary files differindex 26356078..1e94be3d 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Metlakatla diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City b/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City Binary files differindex 1434ab08..e7fb6f29 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Mexico_City diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon b/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon Binary files differindex 06ceaadf..b924b710 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Miquelon diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey b/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey Binary files differindex 7dc50577..a8928c8d 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Monterrey diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo b/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo Binary files differindex 0d1e565c..2f357bcf 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Montevideo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat b/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Montserrat diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau Binary files differindex 5091eb5d..33cc6c62 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nassau diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha b/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha Binary files differindex 95ff8a25..f140726f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Noronha diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga Binary files differindex 37d78301..fc4a03e3 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Ojinaga diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Panama b/absl/time/internal/cctz/testdata/zoneinfo/America/Panama Binary files differindex 55b08346..9964b9a3 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Panama +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Panama diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo b/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo Binary files differindex b95c7842..bc8a6edf 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Paramaribo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix b/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix Binary files differindex 4d51271a..ac6bb0c7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Phoenix diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince b/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince Binary files differindex d9590103..287f1439 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Port-au-Prince diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain b/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Port_of_Spain diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre b/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre Binary files differindex 16b7f923..a374cb43 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Acre diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho b/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho Binary files differindex 10cb02b8..2e873a5a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Porto_Velho diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet b/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet Binary files differindex 61ff6fcb..3a705874 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rankin_Inlet diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Recife b/absl/time/internal/cctz/testdata/zoneinfo/America/Recife Binary files differindex c6d99b3a..d7abb168 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Recife +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Recife diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute b/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute Binary files differindex 4365a5c8..0a73b753 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Resolute diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco b/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco Binary files differindex 16b7f923..a374cb43 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rio_Branco diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario b/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario Binary files differindex 5df3cf6e..da4c23a5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Rosario diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem b/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem Binary files differindex 8080efab..c28f3606 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santarem diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo b/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo Binary files differindex 4e5eba52..4fe36fd4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Santo_Domingo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo b/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo Binary files differindex c417ba1d..13ff0838 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Sao_Paulo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Barthelemy diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Kitts diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Lucia diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Thomas diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/St_Vincent diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa b/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa Binary files differindex 477e9395..2adacb2e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tegucigalpa diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Thule b/absl/time/internal/cctz/testdata/zoneinfo/America/Thule Binary files differindex 2969ebe5..6f802f1c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Thule +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Thule diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola b/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Tortola diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin b/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin Binary files differindex bdedd1bd..697cf5bc 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Virgin diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg b/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg Binary files differindex 3718d47d..ac40299f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Winnipeg diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville Binary files differindex bd6563ec..a71b39c0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville +++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/DumontDUrville diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie Binary files differindex 83c308ad..616afd9c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie +++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Macquarie diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson Binary files differindex e1f0b09b..b32e7fd6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson +++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Mawson diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo Binary files differindex 60bcef68..6575fdce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/McMurdo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera Binary files differindex 7940e6ef..8b2430a2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera +++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Rothera diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole Binary files differindex 60bcef68..6575fdce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole +++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/South_Pole diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa Binary files differindex 4bb041a2..254af7d1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa +++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Syowa diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok Binary files differindex 5696abf5..72830530 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok +++ b/absl/time/internal/cctz/testdata/zoneinfo/Antarctica/Vostok diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen b/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen Binary files differindex c6842af8..15a34c3c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen +++ b/absl/time/internal/cctz/testdata/zoneinfo/Arctic/Longyearbyen diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden Binary files differindex b2f9a255..2aea25f8 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aden diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty Binary files differindex d93201cf..a4b00779 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Almaty diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman Binary files differindex 281b304e..c9e87079 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Amman diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr Binary files differindex 6a966013..6ed8b7cb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Anadyr diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau Binary files differindex 78cbcf0e..e2d0f919 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtau diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe Binary files differindex 7504052a..06f0a13a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Aqtobe diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat Binary files differindex 8d9e03c1..73891af1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashgabat diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad Binary files differindex 8d9e03c1..73891af1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ashkhabad diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau Binary files differindex 317466d1..8b5153e0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Atyrau diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad Binary files differindex 97fa6c73..f7162edf 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baghdad diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain Binary files differindex f5140926..63188b26 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bahrain diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku Binary files differindex 8a090d77..a0de74b9 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Baku diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok Binary files differindex 72496402..c292ac5b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bangkok diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul Binary files differindex 82cc49c4..759592a2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Barnaul diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut Binary files differindex efb24c27..fb266ede 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Beirut diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek Binary files differindex f7a7d548..f6e20dd3 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Bishkek diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei Binary files differindex 8624c7ae..3dab0abf 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Brunei diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta Binary files differindex e1cfcb8d..0014046d 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Calcutta diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita Binary files differindex 3baf7528..c4149c05 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chita diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan Binary files differindex 79b9d3c8..e48daa82 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Choibalsan diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing Binary files differindex ce9e00a5..3c0bef20 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking Binary files differindex ce9e00a5..3c0bef20 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo Binary files differindex 4fc96c89..62c64d85 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Colombo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca Binary files differindex 776f27da..b11c9284 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dacca diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus Binary files differindex 4b610b5a..d9104a7a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Damascus diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka Binary files differindex 776f27da..b11c9284 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dhaka diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili Binary files differindex f6ce91a1..30943bbd 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dili diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai Binary files differindex 7880d5d7..fc0a589e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dubai diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe Binary files differindex 694f6e6a..82d85b8c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Dushanbe diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza Binary files differindex cf54deb8..592b6326 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Gaza diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin Binary files differindex ce9e00a5..3c0bef20 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron Binary files differindex 09c876a6..ae82f9b5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hebron diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh Binary files differindex eab94fe8..e2934e37 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ho_Chi_Minh diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong Binary files differindex 8e5c5813..378a37f9 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hong_Kong diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd Binary files differindex 8eb5f647..4cb800a9 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Hovd diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk Binary files differindex e8c53c0d..4dcbbb7e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Irkutsk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul Binary files differindex 833d4eba..10d4b21b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Istanbul diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta Binary files differindex 673d4801..5baa3a8f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jakarta diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura Binary files differindex a4c08297..3002c820 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jayapura diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem Binary files differindex 2d14c999..440ef06b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Jerusalem diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul Binary files differindex a22cf592..d19b9bd5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kabul diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka Binary files differindex b9ed49ca..3e80b4e0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kamchatka diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi Binary files differindex 337e1d58..ba65c0e8 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Karachi diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar Binary files differindex 0342b433..faa14d92 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kashgar diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu Binary files differindex 2f810b12..a5d51075 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kathmandu diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu Binary files differindex 2f810b12..a5d51075 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Katmandu diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga Binary files differindex 2b2f5bfa..72bea64b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Khandyga diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata Binary files differindex e1cfcb8d..0014046d 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kolkata diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk Binary files differindex 59efd24c..30c6f165 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Krasnoyarsk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur Binary files differindex 6d7d47b9..612b01e7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuala_Lumpur diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching Binary files differindex 4878622d..c86750cb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuching diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait Binary files differindex b2f9a255..2aea25f8 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Kuwait diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao Binary files differindex d801000d..cac65063 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macao diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau Binary files differindex d801000d..cac65063 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Macau diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan Binary files differindex b20cc57e..b4fcac18 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Magadan diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar Binary files differindex ed55442e..556ba866 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Makassar diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila Binary files differindex 2c9220c9..f4f4b04e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Manila diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat Binary files differindex 7880d5d7..fc0a589e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Muscat diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk Binary files differindex 2576a3b0..d9832761 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novokuznetsk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk Binary files differindex 95e3c73b..e0ee5fce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Novosibirsk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk Binary files differindex d805e4f7..b29b7693 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Omsk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral Binary files differindex e36aec47..ad1f9ca1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Oral diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh Binary files differindex 72496402..c292ac5b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Phnom_Penh diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak Binary files differindex 9377d038..12ce24cb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pontianak diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang Binary files differindex dd54989f..7ad7e0b2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Pyongyang diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar Binary files differindex f5140926..63188b26 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qatar diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay Binary files differnew file mode 100644 index 00000000..73b9d963 --- /dev/null +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qostanay diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda Binary files differindex 00b27844..c2fe4c14 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Qyzylorda diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon Binary files differindex a00282de..dd77395b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Rangoon diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh Binary files differindex b2f9a255..2aea25f8 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Riyadh diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon Binary files differindex eab94fe8..e2934e37 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Saigon diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin Binary files differindex 9c94900c..485459ce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Sakhalin diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand Binary files differindex a5d1e970..030d47ce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Samarkand diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul Binary files differindex fa1cbd39..73182cf6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Seoul diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai Binary files differindex ce9e00a5..3c0bef20 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore Binary files differindex ebc4b0d9..2364b217 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Singapore diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk Binary files differindex f8b7bb21..261a9832 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Srednekolymsk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei Binary files differindex f9cbe672..24c43444 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Taipei diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent Binary files differindex e75bb365..32a9d7d0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tashkent diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi Binary files differindex 09bb06eb..b608d797 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tbilisi diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran Binary files differindex ad9058b4..8cec5ad7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv Binary files differindex 2d14c999..440ef06b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tel_Aviv diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu Binary files differindex 06d3324d..fe409c7a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimbu diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu Binary files differindex 06d3324d..fe409c7a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Thimphu diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk Binary files differindex 28da9c90..670e2ad2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tomsk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang Binary files differindex ed55442e..556ba866 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ujung_Pandang diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar Binary files differindex 82fd4760..2e20cc3a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulaanbaatar diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator Binary files differindex 82fd4760..2e20cc3a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ulan_Bator diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi Binary files differindex 0342b433..faa14d92 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Urumqi diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera Binary files differindex c0c3767e..9e4a78f6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Ust-Nera diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane Binary files differindex 72496402..c292ac5b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vientiane diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok Binary files differindex 15731abc..8ab253ce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Vladivostok diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk Binary files differindex 1f86e77f..c815e99b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yakutsk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon Binary files differindex a00282de..dd77395b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yangon diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg Binary files differindex fff9f3b1..6958d7ed 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yekaterinburg diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan Binary files differindex 409c3b17..250bfe02 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Yerevan diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda Binary files differindex 3a5c6dbf..419c660b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda +++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Bermuda diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen Binary files differindex c6842af8..15a34c3c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen +++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Jan_Mayen diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik Binary files differindex ac6bd697..10e0fc81 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik +++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Reykjavik diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia Binary files differindex b3311b63..44666086 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia +++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/South_Georgia diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena Binary files differindex 65d19ec2..28b32ab2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena +++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/St_Helena diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley Binary files differindex 2fd42a2c..88077f11 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley +++ b/absl/time/internal/cctz/testdata/zoneinfo/Atlantic/Stanley diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT b/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT Binary files differindex 4ed4467f..7636592a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/ACT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide Binary files differindex 190b0e33..0b1252ab 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Adelaide diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane Binary files differindex 26ffd9ac..3021bdb6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Brisbane diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill Binary files differindex 874c8650..1ac3fc8f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Broken_Hill diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra Binary files differindex 4ed4467f..7636592a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Canberra diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie Binary files differindex 865801e5..f65a990e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Currie diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin Binary files differindex cf42d1d8..1cf50298 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Darwin diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla Binary files differindex c49d499c..98ae5570 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Eucla diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart Binary files differindex 92d1215d..02b07ca2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Hobart diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI b/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI Binary files differindex 8c6c7dd0..9e04a80e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/LHI diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman Binary files differindex 8ee1a6f5..eab0fb99 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lindeman diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe Binary files differindex 8c6c7dd0..9e04a80e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Lord_Howe diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne Binary files differindex 3f2d3d7f..ba457338 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Melbourne diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW b/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW Binary files differindex 4ed4467f..7636592a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/NSW diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/North b/absl/time/internal/cctz/testdata/zoneinfo/Australia/North Binary files differindex cf42d1d8..1cf50298 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/North +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/North diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth Binary files differindex d38b67e2..a876b9e7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Perth diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland Binary files differindex 26ffd9ac..3021bdb6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Queensland diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/South b/absl/time/internal/cctz/testdata/zoneinfo/Australia/South Binary files differindex 190b0e33..0b1252ab 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/South +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/South diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney Binary files differindex 4ed4467f..7636592a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Sydney diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania Binary files differindex 92d1215d..02b07ca2 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Tasmania diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria Binary files differindex 3f2d3d7f..ba457338 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Victoria diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/West b/absl/time/internal/cctz/testdata/zoneinfo/Australia/West Binary files differindex d38b67e2..a876b9e7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/West +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/West diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna Binary files differindex 874c8650..1ac3fc8f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna +++ b/absl/time/internal/cctz/testdata/zoneinfo/Australia/Yancowinna diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre Binary files differindex 16b7f923..a374cb43 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre +++ b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/Acre diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha Binary files differindex 95ff8a25..f140726f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha +++ b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/DeNoronha diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East Binary files differindex c417ba1d..13ff0838 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East +++ b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/East diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West Binary files differindex b10241e6..63d58f80 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West +++ b/absl/time/internal/cctz/testdata/zoneinfo/Brazil/West diff --git a/absl/time/internal/cctz/testdata/zoneinfo/CET b/absl/time/internal/cctz/testdata/zoneinfo/CET Binary files differindex d585656f..122e9342 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/CET +++ b/absl/time/internal/cctz/testdata/zoneinfo/CET diff --git a/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT b/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT Binary files differindex 41c4136f..ca67929f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT +++ b/absl/time/internal/cctz/testdata/zoneinfo/CST6CDT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central Binary files differindex 3718d47d..ac40299f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central +++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Central diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Cuba b/absl/time/internal/cctz/testdata/zoneinfo/Cuba Binary files differindex 8186060a..b69ac451 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Cuba +++ b/absl/time/internal/cctz/testdata/zoneinfo/Cuba diff --git a/absl/time/internal/cctz/testdata/zoneinfo/EET b/absl/time/internal/cctz/testdata/zoneinfo/EET Binary files differindex d2f54c9b..cbdb71dd 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/EET +++ b/absl/time/internal/cctz/testdata/zoneinfo/EET diff --git a/absl/time/internal/cctz/testdata/zoneinfo/EST b/absl/time/internal/cctz/testdata/zoneinfo/EST Binary files differindex 074a4fc7..21ebc00b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/EST +++ b/absl/time/internal/cctz/testdata/zoneinfo/EST diff --git a/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT b/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT Binary files differindex 087b641d..9bce5007 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT +++ b/absl/time/internal/cctz/testdata/zoneinfo/EST5EDT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Egypt b/absl/time/internal/cctz/testdata/zoneinfo/Egypt Binary files differindex 0272fa1b..d3f81962 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Egypt +++ b/absl/time/internal/cctz/testdata/zoneinfo/Egypt diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Eire b/absl/time/internal/cctz/testdata/zoneinfo/Eire Binary files differindex 5c5a7a3b..1d994902 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Eire +++ b/absl/time/internal/cctz/testdata/zoneinfo/Eire diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+0 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 Binary files differindex 087d1f92..4dab6f90 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+1 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 Binary files differindex 6437c684..c749290a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+10 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 Binary files differindex 72a912e0..d9699823 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+11 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 Binary files differindex 6938a1af..cdeec909 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+12 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 Binary files differindex a3155777..fbd2a941 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+2 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 Binary files differindex ee776199..ee246ef5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+3 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 Binary files differindex 1ea7da29..5a25ff2a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+4 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 Binary files differindex dda1a9e1..c0b745f1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+5 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 Binary files differindex f4a03855..06e777d5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+6 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 Binary files differindex 2d2ccd00..4e0b53a0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+7 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 Binary files differindex 826c7700..714b0c56 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+8 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 Binary files differindex b125ad2b..78b9daa3 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT+9 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-0 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 Binary files differindex dde682d8..a838bebf 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-1 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 Binary files differindex 352ec08a..68ff77db 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-10 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 Binary files differindex dfa27fec..66af5a42 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-11 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 Binary files differindex eef949df..17ba5057 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-12 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 Binary files differindex f9363b24..5f3706ce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-13 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 Binary files differindex 35add05a..7e9f9c46 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-14 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 Binary files differindex 315cae4f..fcef6d9a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-2 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 Binary files differindex 7489a153..27973bc8 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-3 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 Binary files differindex 560243e8..1efd8412 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-4 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 Binary files differindex b2bbe977..1f761844 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-5 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 Binary files differindex b979dbbc..952681ed 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-6 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 Binary files differindex 365ab1f6..cefc9126 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-7 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 Binary files differindex 742082fc..afb093da 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-8 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 Binary files differindex abc0b275..9265fb7c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT-9 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/GMT0 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Greenwich diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT b/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT Binary files differindex a88c4b66..91558be0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/UCT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC b/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC Binary files differindex 5583f5b0..91558be0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/UTC diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal Binary files differindex 5583f5b0..91558be0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Universal diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu Binary files differindex 5583f5b0..91558be0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu +++ b/absl/time/internal/cctz/testdata/zoneinfo/Etc/Zulu diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam Binary files differindex ed064ed4..c3ff07b4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Amsterdam diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan Binary files differindex 5e069ea5..73a4d013 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Astrakhan diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast Binary files differindex a340326e..ac02a814 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belfast diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade Binary files differindex 32a57223..27de456f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Belgrade diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin Binary files differindex 7ddd510e..7f6d958f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Berlin diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava Binary files differindex 85036de3..ce8f433e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bratislava diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels Binary files differindex d0d0a08a..9613c981 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Brussels diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest Binary files differindex 4eb7ed0d..4303b903 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Bucharest diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest Binary files differindex dfdc6d24..6b94a4f3 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Budapest diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau Binary files differindex 5bc1bfeb..5ee23fe0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Chisinau diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen Binary files differindex cb2ec067..776be6e4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Copenhagen diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin Binary files differindex 5c5a7a3b..1d994902 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Dublin diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey Binary files differindex a340326e..ac02a814 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Guernsey diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man Binary files differindex a340326e..ac02a814 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Isle_of_Man diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul Binary files differindex 833d4eba..10d4b21b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Istanbul diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey Binary files differindex a340326e..ac02a814 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Jersey diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad Binary files differindex 982d82a3..f774ffdb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Kaliningrad diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana Binary files differindex 32a57223..27de456f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Ljubljana diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/London b/absl/time/internal/cctz/testdata/zoneinfo/Europe/London Binary files differindex a340326e..ac02a814 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/London +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/London diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg Binary files differindex 6c194a5c..c4ca733f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Luxembourg diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid Binary files differindex ccc9d857..16f6420a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Madrid diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk Binary files differindex 801aead7..453306c0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Minsk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo Binary files differindex c6842af8..15a34c3c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Oslo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica Binary files differindex 32a57223..27de456f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Podgorica diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague Binary files differindex 85036de3..ce8f433e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Prague diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga Binary files differindex 8495c506..8db477d0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Riga diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome Binary files differindex 78a131b9..ac4c1634 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Rome diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino b/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino Binary files differindex 78a131b9..ac4c1634 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/San_Marino diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo Binary files differindex 32a57223..27de456f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sarajevo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol Binary files differindex e82dbbc7..432e8315 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Simferopol diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje Binary files differindex 32a57223..27de456f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Skopje diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia Binary files differindex dcfdd082..0e4d8793 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Sofia diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn Binary files differindex 3a744cc6..b5acca3c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tallinn diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol Binary files differindex 5bc1bfeb..5ee23fe0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Tiraspol diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod Binary files differindex 677f0887..66ae8d69 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Uzhgorod diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican Binary files differindex 78a131b9..ac4c1634 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vatican diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna Binary files differindex 9e2d0c94..696f359d 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vienna diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius Binary files differindex 46ce484f..7abd63fa 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Vilnius diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd Binary files differindex 8f170dd9..d1cfac0e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Volgograd diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw Binary files differindex d6bb1561..e33cf671 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Warsaw diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb Binary files differindex 32a57223..27de456f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb +++ b/absl/time/internal/cctz/testdata/zoneinfo/Europe/Zagreb diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Factory b/absl/time/internal/cctz/testdata/zoneinfo/Factory Binary files differindex 95bc3a3b..60aa2a0d 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Factory +++ b/absl/time/internal/cctz/testdata/zoneinfo/Factory diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GB b/absl/time/internal/cctz/testdata/zoneinfo/GB Binary files differindex a340326e..ac02a814 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/GB +++ b/absl/time/internal/cctz/testdata/zoneinfo/GB diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire b/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire Binary files differindex a340326e..ac02a814 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire +++ b/absl/time/internal/cctz/testdata/zoneinfo/GB-Eire diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GMT b/absl/time/internal/cctz/testdata/zoneinfo/GMT Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/GMT +++ b/absl/time/internal/cctz/testdata/zoneinfo/GMT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 b/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 +++ b/absl/time/internal/cctz/testdata/zoneinfo/GMT+0 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 b/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 +++ b/absl/time/internal/cctz/testdata/zoneinfo/GMT-0 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/GMT0 b/absl/time/internal/cctz/testdata/zoneinfo/GMT0 Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/GMT0 +++ b/absl/time/internal/cctz/testdata/zoneinfo/GMT0 diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Greenwich b/absl/time/internal/cctz/testdata/zoneinfo/Greenwich Binary files differindex 2ee14295..c6347466 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Greenwich +++ b/absl/time/internal/cctz/testdata/zoneinfo/Greenwich diff --git a/absl/time/internal/cctz/testdata/zoneinfo/HST b/absl/time/internal/cctz/testdata/zoneinfo/HST Binary files differindex 616c31bc..cccd45eb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/HST +++ b/absl/time/internal/cctz/testdata/zoneinfo/HST diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Hongkong b/absl/time/internal/cctz/testdata/zoneinfo/Hongkong Binary files differindex 8e5c5813..378a37f9 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Hongkong +++ b/absl/time/internal/cctz/testdata/zoneinfo/Hongkong diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Iceland b/absl/time/internal/cctz/testdata/zoneinfo/Iceland Binary files differindex ac6bd697..10e0fc81 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Iceland +++ b/absl/time/internal/cctz/testdata/zoneinfo/Iceland diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Antananarivo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos Binary files differindex f609611c..93d6dda5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Chagos diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas Binary files differindex 6babdeea..d18c3810 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Christmas diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos Binary files differindex 58f80514..f8116e70 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Cocos diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Comoro diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen Binary files differindex 2cb6f3e3..cde4cf7e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Kerguelen diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe Binary files differindex 49e23e5a..cba7dfe7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mahe diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives Binary files differindex ffa33658..7c839cfa 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Maldives diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius Binary files differindex b23e2cee..17f26169 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mauritius diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte Binary files differindex 6e19601f..9a2918f4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Mayotte diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion Binary files differindex 11c6002e..dfe08313 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion +++ b/absl/time/internal/cctz/testdata/zoneinfo/Indian/Reunion diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Iran b/absl/time/internal/cctz/testdata/zoneinfo/Iran Binary files differindex ad9058b4..8cec5ad7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Iran +++ b/absl/time/internal/cctz/testdata/zoneinfo/Iran diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Israel b/absl/time/internal/cctz/testdata/zoneinfo/Israel Binary files differindex 2d14c999..440ef06b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Israel +++ b/absl/time/internal/cctz/testdata/zoneinfo/Israel diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Jamaica b/absl/time/internal/cctz/testdata/zoneinfo/Jamaica Binary files differindex 162306f8..2a9b7fd5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Jamaica +++ b/absl/time/internal/cctz/testdata/zoneinfo/Jamaica diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein b/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein Binary files differindex 54bd71ff..1a7975fa 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein +++ b/absl/time/internal/cctz/testdata/zoneinfo/Kwajalein diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Libya b/absl/time/internal/cctz/testdata/zoneinfo/Libya Binary files differindex bd885315..07b393bb 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Libya +++ b/absl/time/internal/cctz/testdata/zoneinfo/Libya diff --git a/absl/time/internal/cctz/testdata/zoneinfo/MET b/absl/time/internal/cctz/testdata/zoneinfo/MET Binary files differindex 388dd744..4a826bb1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/MET +++ b/absl/time/internal/cctz/testdata/zoneinfo/MET diff --git a/absl/time/internal/cctz/testdata/zoneinfo/MST b/absl/time/internal/cctz/testdata/zoneinfo/MST Binary files differindex da3e926d..c93a58ee 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/MST +++ b/absl/time/internal/cctz/testdata/zoneinfo/MST diff --git a/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT b/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT Binary files differindex ddca8d19..4506a6e1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT +++ b/absl/time/internal/cctz/testdata/zoneinfo/MST7MDT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur Binary files differindex 43ee12d8..e4a78574 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur +++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/BajaSur diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General Binary files differindex 1434ab08..e7fb6f29 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General +++ b/absl/time/internal/cctz/testdata/zoneinfo/Mexico/General diff --git a/absl/time/internal/cctz/testdata/zoneinfo/NZ b/absl/time/internal/cctz/testdata/zoneinfo/NZ Binary files differindex 60bcef68..6575fdce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/NZ +++ b/absl/time/internal/cctz/testdata/zoneinfo/NZ diff --git a/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT b/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT Binary files differindex abe09cb9..c0041098 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT +++ b/absl/time/internal/cctz/testdata/zoneinfo/NZ-CHAT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/PRC b/absl/time/internal/cctz/testdata/zoneinfo/PRC Binary files differindex ce9e00a5..3c0bef20 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/PRC +++ b/absl/time/internal/cctz/testdata/zoneinfo/PRC diff --git a/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT b/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT Binary files differindex d773e28f..99d246ba 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT +++ b/absl/time/internal/cctz/testdata/zoneinfo/PST8PDT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia Binary files differindex fd03ff76..dab1f3f6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Apia diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland Binary files differindex 60bcef68..6575fdce 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Auckland diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville Binary files differindex 6a6c2da2..2892d268 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Bougainville diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham Binary files differindex abe09cb9..c0041098 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chatham diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk Binary files differindex e79bca2d..07c84b71 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Chuuk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate Binary files differindex d650a056..60150175 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Efate diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury Binary files differindex 80873503..f0b82523 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Enderbury diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo Binary files differindex 4fa169f3..e40307f6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fakaofo diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji Binary files differindex 61a66953..6bf667d4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Fiji diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti Binary files differindex e6a15447..ea728637 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Funafuti diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos Binary files differindex 859b76d9..31f0921e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Galapagos diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier Binary files differindex 4e9e36c5..e1fc3daa 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Gambier diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal Binary files differindex 908ccc14..7e9d10a1 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guadalcanal diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam Binary files differindex ffdf8c24..66490d25 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Guam diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati Binary files differindex cf5b3bd3..7cae0cb7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kiritimati diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae Binary files differindex b6bd4b08..a584aae5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kosrae diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein Binary files differindex 54bd71ff..1a7975fa 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Kwajalein diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro Binary files differindex 53f32886..9ef8374d 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Majuro diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas Binary files differindex 5fad0e1b..74d6792b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Marquesas diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway Binary files differindex 72707b5e..cb56709a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Midway diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru Binary files differindex 7e7d920e..acec0429 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Nauru diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue Binary files differindex 1d58fe36..684b010e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Niue diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk Binary files differindex f630a65d..1f6d610e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Norfolk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea Binary files differindex 99f6bca2..931a1a30 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Noumea diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago Binary files differindex 72707b5e..cb56709a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pago_Pago diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau Binary files differindex 968f1956..146b3515 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Palau diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn Binary files differindex 9092e481..ef91b061 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pitcairn diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei Binary files differindex d3393a20..c298ddd4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Pohnpei diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape Binary files differindex d3393a20..c298ddd4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Ponape diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby Binary files differindex f6fd51cb..920ad27e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Port_Moresby diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga Binary files differindex 9708b870..da6b0fad 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Rarotonga diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan Binary files differindex ffdf8c24..66490d25 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Saipan diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa Binary files differindex 72707b5e..cb56709a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Samoa diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti Binary files differindex 37e4e883..442b8eb5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tahiti diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa Binary files differindex e23c0cd2..3db6c750 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tarawa diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu Binary files differindex 35c9e2c6..5553c600 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Tongatapu diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk Binary files differindex e79bca2d..07c84b71 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Truk diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake Binary files differindex 837ce1f5..c9e31067 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wake diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis Binary files differindex 8be9ac4d..b35344b3 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Wallis diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap Binary files differindex e79bca2d..07c84b71 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap +++ b/absl/time/internal/cctz/testdata/zoneinfo/Pacific/Yap diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Poland b/absl/time/internal/cctz/testdata/zoneinfo/Poland Binary files differindex d6bb1561..e33cf671 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Poland +++ b/absl/time/internal/cctz/testdata/zoneinfo/Poland diff --git a/absl/time/internal/cctz/testdata/zoneinfo/ROC b/absl/time/internal/cctz/testdata/zoneinfo/ROC Binary files differindex f9cbe672..24c43444 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/ROC +++ b/absl/time/internal/cctz/testdata/zoneinfo/ROC diff --git a/absl/time/internal/cctz/testdata/zoneinfo/ROK b/absl/time/internal/cctz/testdata/zoneinfo/ROK Binary files differindex fa1cbd39..73182cf6 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/ROK +++ b/absl/time/internal/cctz/testdata/zoneinfo/ROK diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Singapore b/absl/time/internal/cctz/testdata/zoneinfo/Singapore Binary files differindex ebc4b0d9..2364b217 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Singapore +++ b/absl/time/internal/cctz/testdata/zoneinfo/Singapore diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Turkey b/absl/time/internal/cctz/testdata/zoneinfo/Turkey Binary files differindex 833d4eba..10d4b21b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Turkey +++ b/absl/time/internal/cctz/testdata/zoneinfo/Turkey diff --git a/absl/time/internal/cctz/testdata/zoneinfo/UCT b/absl/time/internal/cctz/testdata/zoneinfo/UCT Binary files differindex a88c4b66..91558be0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/UCT +++ b/absl/time/internal/cctz/testdata/zoneinfo/UCT diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona b/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona Binary files differindex 4d51271a..ac6bb0c7 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona +++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Arizona diff --git a/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa b/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa Binary files differindex 72707b5e..cb56709a 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa +++ b/absl/time/internal/cctz/testdata/zoneinfo/US/Samoa diff --git a/absl/time/internal/cctz/testdata/zoneinfo/UTC b/absl/time/internal/cctz/testdata/zoneinfo/UTC Binary files differindex 5583f5b0..91558be0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/UTC +++ b/absl/time/internal/cctz/testdata/zoneinfo/UTC diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Universal b/absl/time/internal/cctz/testdata/zoneinfo/Universal Binary files differindex 5583f5b0..91558be0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Universal +++ b/absl/time/internal/cctz/testdata/zoneinfo/Universal diff --git a/absl/time/internal/cctz/testdata/zoneinfo/WET b/absl/time/internal/cctz/testdata/zoneinfo/WET Binary files differindex 9b03a17f..c27390b5 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/WET +++ b/absl/time/internal/cctz/testdata/zoneinfo/WET diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Zulu b/absl/time/internal/cctz/testdata/zoneinfo/Zulu Binary files differindex 5583f5b0..91558be0 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Zulu +++ b/absl/time/internal/cctz/testdata/zoneinfo/Zulu diff --git a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab index c2e0f8ea..a4ff61a4 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab +++ b/absl/time/internal/cctz/testdata/zoneinfo/iso3166.tab @@ -9,8 +9,8 @@ # All text uses UTF-8 encoding. The columns of the table are as follows: # # 1. ISO 3166-1 alpha-2 country code, current as of -# ISO 3166-1 N905 (2016-11-15). See: Updates on ISO 3166-1 -# http://isotc.iso.org/livelink/livelink/Open/16944257 +# ISO 3166-1 N976 (2018-11-06). See: Updates on ISO 3166-1 +# https://isotc.iso.org/livelink/livelink/Open/16944257 # 2. The usual English name for the coded region, # chosen so that alphabetic sorting of subsets produces helpful lists. # This is not the same as the English name in the ISO 3166 tables. @@ -166,7 +166,7 @@ ME Montenegro MF St Martin (French) MG Madagascar MH Marshall Islands -MK Macedonia +MK North Macedonia ML Mali MM Myanmar (Burma) MN Mongolia @@ -235,7 +235,7 @@ ST Sao Tome & Principe SV El Salvador SX St Maarten (Dutch) SY Syria -SZ Swaziland +SZ Eswatini (Swaziland) TC Turks & Caicos Is TD Chad TF French Southern & Antarctic Lands diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab index 2729e6e8..822ffa1f 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab +++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab @@ -211,6 +211,7 @@ KP +3901+12545 Asia/Pyongyang KR +3733+12658 Asia/Seoul KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda +KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay KZ +5017+05710 Asia/Aqtobe Aqtöbe/Aktobe KZ +4431+05016 Asia/Aqtau MangghystaÅ«/Mankistau KZ +4707+05156 Asia/Atyrau AtyraÅ«/Atirau/Gur'yev @@ -288,7 +289,8 @@ RO +4426+02606 Europe/Bucharest RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area -RU +4457+03406 Europe/Simferopol MSK+00 - Crimea +# Mention RU and UA alphabetically. See "territorial claims" above. +RU,UA +4457+03406 Europe/Simferopol MSK+00 - Crimea RU +5836+04939 Europe/Kirov MSK+00 - Kirov RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan RU +4844+04425 Europe/Volgograd MSK+01 - Volgograd diff --git a/absl/time/internal/get_current_time_chrono.inc b/absl/time/internal/get_current_time_chrono.inc index ba016e3e..d294b3a6 100644 --- a/absl/time/internal/get_current_time_chrono.inc +++ b/absl/time/internal/get_current_time_chrono.inc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,7 +16,7 @@ #include <cstdint> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { static int64_t GetCurrentTimeNanosFromSystem() { @@ -27,5 +27,5 @@ static int64_t GetCurrentTimeNanosFromSystem() { } } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/time/internal/get_current_time_posix.inc b/absl/time/internal/get_current_time_posix.inc index 3ff45c4a..0ce2a9a9 100644 --- a/absl/time/internal/get_current_time_posix.inc +++ b/absl/time/internal/get_current_time_posix.inc @@ -7,7 +7,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { static int64_t GetCurrentTimeNanosFromSystem() { @@ -20,5 +20,5 @@ static int64_t GetCurrentTimeNanosFromSystem() { } } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc index 69530e64..2bc449fe 100644 --- a/absl/time/internal/test_util.cc +++ b/absl/time/internal/test_util.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,7 @@ namespace cctz = absl::time_internal::cctz; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { TimeZone LoadTimeZone(const std::string& name) { @@ -34,11 +34,11 @@ TimeZone LoadTimeZone(const std::string& name) { } } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { namespace cctz_extension { namespace { @@ -123,5 +123,5 @@ ZoneInfoSourceFactory zone_info_source_factory = TestFactory; } // namespace cctz_extension } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/time/internal/test_util.h b/absl/time/internal/test_util.h index 31ec18e4..1a320cab 100644 --- a/absl/time/internal/test_util.h +++ b/absl/time/internal/test_util.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -20,14 +20,14 @@ #include "absl/time/time.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace time_internal { // Loads the named timezone, but dies on any failure. absl::TimeZone LoadTimeZone(const std::string& name); } // namespace time_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_TIME_INTERNAL_TEST_UTIL_H_ diff --git a/absl/time/time.cc b/absl/time/time.cc index e60857e2..2c7da3ad 100644 --- a/absl/time/time.cc +++ b/absl/time/time.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -33,6 +33,10 @@ #include "absl/time/time.h" +#if defined(_MSC_VER) +#include <winsock2.h> // for timeval +#endif + #include <cstring> #include <ctime> #include <limits> @@ -41,8 +45,9 @@ #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace cctz = absl::time_internal::cctz; + namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { @@ -453,8 +458,7 @@ struct tm ToTM(absl::Time t, absl::TimeZone tz) { tm.tm_year = static_cast<int>(cs.year() - 1900); } - const CivilDay cd(cs); - switch (GetWeekday(cd)) { + switch (GetWeekday(cs)) { case Weekday::sunday: tm.tm_wday = 0; break; @@ -477,11 +481,11 @@ struct tm ToTM(absl::Time t, absl::TimeZone tz) { tm.tm_wday = 6; break; } - tm.tm_yday = GetYearDay(cd) - 1; + tm.tm_yday = GetYearDay(cs) - 1; tm.tm_isdst = ci.is_dst ? 1 : 0; return tm; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/time/time.h b/absl/time/time.h index 3afec565..1f6500e8 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -58,7 +58,6 @@ // std::string s = absl::FormatTime( // "My flight will land in Sydney on %Y-%m-%d at %H:%M:%S", // landing, syd); -// #ifndef ABSL_TIME_TIME_H_ #define ABSL_TIME_TIME_H_ @@ -66,7 +65,14 @@ #if !defined(_MSC_VER) #include <sys/time.h> #else -#include <winsock2.h> +// We don't include `winsock2.h` because it drags in `windows.h` and friends, +// and they define conflicting macros like OPAQUE, ERROR, and more. This has the +// potential to break Abseil users. +// +// Instead we only forward declare `timeval` and require Windows users include +// `winsock2.h` themselves. This is both inconsistent and troublesome, but so is +// including 'windows.h' so we are picking the lesser of two evils here. +struct timeval; #endif #include <chrono> // NOLINT(build/c++11) #include <cmath> @@ -77,13 +83,12 @@ #include <type_traits> #include <utility> -#include "absl/base/port.h" // Needed for string vs std::string #include "absl/strings/string_view.h" #include "absl/time/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { class Duration; // Defined below class Time; // Defined below @@ -175,6 +180,7 @@ class Duration { Duration& operator%=(Duration rhs); // Overloads that forward to either the int64_t or double overloads above. + // Integer operands must be representable as int64_t. template <typename T> Duration& operator*=(T r) { int64_t x = r; @@ -217,6 +223,7 @@ inline Duration operator+(Duration lhs, Duration rhs) { return lhs += rhs; } inline Duration operator-(Duration lhs, Duration rhs) { return lhs -= rhs; } // Multiplicative Operators +// Integer operands must be representable as int64_t. template <typename T> Duration operator*(Duration lhs, T rhs) { return lhs *= rhs; @@ -371,7 +378,8 @@ constexpr Duration InfiniteDuration(); // Hours() // // Factory functions for constructing `Duration` values from an integral number -// of the unit indicated by the factory function's name. +// of the unit indicated by the factory function's name. The number must be +// representable as int64_t. // // Note: no "Days()" factory function exists because "a day" is ambiguous. // Civil days are not always 24 hours long, and a 24-hour duration often does @@ -571,7 +579,6 @@ std::string UnparseFlag(Duration d); // The `absl::Time` class represents an instant in time as a count of clock // ticks of some granularity (resolution) from some starting point (epoch). // -// // `absl::Time` uses a resolution that is high enough to avoid loss in // precision, and a range that is wide enough to avoid overflow, when // converting between tick counts in most Google time scales (i.e., resolution @@ -838,8 +845,8 @@ std::string UnparseFlag(Time t); // // See also: // - https://github.com/google/cctz -// - http://www.iana.org/time-zones -// - http://en.wikipedia.org/wiki/Zoneinfo +// - https://www.iana.org/time-zones +// - https://en.wikipedia.org/wiki/Zoneinfo class TimeZone { public: explicit TimeZone(time_internal::cctz::time_zone tz) : cz_(tz) {} @@ -1150,7 +1157,9 @@ TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour, // absl::Time t = absl::FromDateTime(2017, 9, 26, 9, 30, 0, lax); // // t = 2017-09-26 09:30:00 -0700 // -// Deprecated. Use `absl::TimeZone::At(CivilSecond).pre`. +// Deprecated. Use `absl::FromCivil(CivilSecond, TimeZone)`. Note that the +// behavior of `FromCivil()` differs from `FromDateTime()` for skipped civil +// times. If you care about that see `absl::TimeZone::At(absl::CivilSecond)`. inline Time FromDateTime(int64_t year, int mon, int day, int hour, int min, int sec, TimeZone tz) { return ConvertDateTime(year, mon, day, hour, min, sec, tz).pre; @@ -1221,7 +1230,7 @@ extern const char RFC1123_no_wday[]; // %d %b %E4Y %H:%M:%S %z // // absl::CivilSecond cs(2013, 1, 2, 3, 4, 5); // absl::Time t = absl::FromCivil(cs, lax); -// string f = absl::FormatTime("%H:%M:%S", t, lax); // "03:04:05" +// std::string f = absl::FormatTime("%H:%M:%S", t, lax); // "03:04:05" // f = absl::FormatTime("%H:%M:%E3S", t, lax); // "03:04:05.000" // // Note: If the given `absl::Time` is `absl::InfiniteFuture()`, the returned @@ -1442,14 +1451,15 @@ T ToChronoDuration(Duration d) { using Period = typename T::period; static_assert(IsValidRep64<Rep>(0), "duration::rep is invalid"); if (time_internal::IsInfiniteDuration(d)) - return d < ZeroDuration() ? T::min() : T::max(); + return d < ZeroDuration() ? (T::min)() : (T::max)(); const auto v = ToInt64(d, Period{}); - if (v > (std::numeric_limits<Rep>::max)()) return T::max(); - if (v < (std::numeric_limits<Rep>::min)()) return T::min(); + if (v > (std::numeric_limits<Rep>::max)()) return (T::max)(); + if (v < (std::numeric_limits<Rep>::min)()) return (T::min)(); return T{v}; } } // namespace time_internal + constexpr Duration Nanoseconds(int64_t n) { return time_internal::FromInt64(n, std::nano{}); } @@ -1555,7 +1565,7 @@ constexpr Time FromTimeT(time_t t) { return time_internal::FromUnixDuration(Seconds(t)); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_TIME_TIME_H_ diff --git a/absl/time/time_benchmark.cc b/absl/time/time_benchmark.cc index 9bbed6f8..99e62799 100644 --- a/absl/time/time_benchmark.cc +++ b/absl/time/time_benchmark.cc @@ -3,7 +3,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc index 4d710709..37af39d9 100644 --- a/absl/time/time_test.cc +++ b/absl/time/time_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -14,6 +14,10 @@ #include "absl/time/time.h" +#if defined(_MSC_VER) +#include <winsock2.h> // for timeval +#endif + #include <chrono> // NOLINT(build/c++11) #include <cstring> #include <ctime> @@ -28,7 +32,7 @@ namespace { -#if GTEST_USES_SIMPLE_RE +#if defined(GTEST_USES_SIMPLE_RE) && GTEST_USES_SIMPLE_RE const char kZoneAbbrRE[] = ".*"; // just punt #else const char kZoneAbbrRE[] = "[A-Za-z]{3,4}|[-+][0-9]{2}([0-9]{2})?"; @@ -108,7 +112,7 @@ TEST(Time, UnixEpoch) { const auto ci = absl::UTCTimeZone().At(absl::UnixEpoch()); EXPECT_EQ(absl::CivilSecond(1970, 1, 1, 0, 0, 0), ci.cs); EXPECT_EQ(absl::ZeroDuration(), ci.subsecond); - EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(absl::CivilDay(ci.cs))); + EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(ci.cs)); } TEST(Time, Breakdown) { @@ -119,14 +123,14 @@ TEST(Time, Breakdown) { auto ci = tz.At(t); EXPECT_CIVIL_INFO(ci, 1969, 12, 31, 19, 0, 0, -18000, false); EXPECT_EQ(absl::ZeroDuration(), ci.subsecond); - EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(absl::CivilDay(ci.cs))); + EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(ci.cs)); // Just before the epoch. t -= absl::Nanoseconds(1); ci = tz.At(t); EXPECT_CIVIL_INFO(ci, 1969, 12, 31, 18, 59, 59, -18000, false); EXPECT_EQ(absl::Nanoseconds(999999999), ci.subsecond); - EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(absl::CivilDay(ci.cs))); + EXPECT_EQ(absl::Weekday::wednesday, absl::GetWeekday(ci.cs)); // Some time later. t += absl::Hours(24) * 2735; @@ -135,7 +139,7 @@ TEST(Time, Breakdown) { ci = tz.At(t); EXPECT_CIVIL_INFO(ci, 1977, 6, 28, 14, 30, 15, -14400, true); EXPECT_EQ(8, ci.subsecond / absl::Nanoseconds(1)); - EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(absl::CivilDay(ci.cs))); + EXPECT_EQ(absl::Weekday::tuesday, absl::GetWeekday(ci.cs)); } TEST(Time, AdditiveOperators) { @@ -975,15 +979,15 @@ TEST(Time, ConversionSaturation) { EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::max(), 12, 31, 23, 59, 59, 0, false); EXPECT_EQ(absl::InfiniteDuration(), ci.subsecond); - EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(absl::CivilDay(ci.cs))); - EXPECT_EQ(365, absl::GetYearDay(absl::CivilDay(ci.cs))); + EXPECT_EQ(absl::Weekday::thursday, absl::GetWeekday(ci.cs)); + EXPECT_EQ(365, absl::GetYearDay(ci.cs)); EXPECT_STREQ("-00", ci.zone_abbr); // artifact of TimeZone::At() ci = utc.At(absl::InfinitePast()); EXPECT_CIVIL_INFO(ci, std::numeric_limits<int64_t>::min(), 1, 1, 0, 0, 0, 0, false); EXPECT_EQ(-absl::InfiniteDuration(), ci.subsecond); - EXPECT_EQ(absl::Weekday::sunday, absl::GetWeekday(absl::CivilDay(ci.cs))); - EXPECT_EQ(1, absl::GetYearDay(absl::CivilDay(ci.cs))); + EXPECT_EQ(absl::Weekday::sunday, absl::GetWeekday(ci.cs)); + EXPECT_EQ(1, absl::GetYearDay(ci.cs)); EXPECT_STREQ("-00", ci.zone_abbr); // artifact of TimeZone::At() // Approach the maximal Time value from below. diff --git a/absl/time/time_zone_test.cc b/absl/time/time_zone_test.cc index 43d91904..8f1e74ac 100644 --- a/absl/time/time_zone_test.cc +++ b/absl/time/time_zone_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index d56fea6e..66ecb044 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,11 +15,12 @@ # load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", - "ABSL_TEST_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_EXCEPTIONS_FLAG", "ABSL_EXCEPTIONS_FLAG_LINKOPTS", + "ABSL_TEST_COPTS", ) package(default_visibility = ["//visibility:public"]) @@ -30,6 +31,7 @@ cc_library( name = "any", hdrs = ["any.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bad_any_cast", "//absl/base:config", @@ -43,6 +45,7 @@ cc_library( name = "bad_any_cast", hdrs = ["bad_any_cast.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bad_any_cast_impl", "//absl/base:config", @@ -56,7 +59,7 @@ cc_library( "bad_any_cast.h", ], copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, visibility = ["//visibility:private"], deps = [ "//absl/base", @@ -71,7 +74,7 @@ cc_test( "any_test.cc", ], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":any", "//absl/base", @@ -89,6 +92,7 @@ cc_test( "any_test.cc", ], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":any", "//absl/base", @@ -103,7 +107,7 @@ cc_test( name = "any_exception_safety_test", srcs = ["any_exception_safety_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":any", "//absl/base:exception_safety_testing", @@ -113,8 +117,14 @@ cc_test( cc_library( name = "span", - hdrs = ["span.h"], + srcs = [ + "internal/span.h", + ], + hdrs = [ + "span.h", + ], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/algorithm", "//absl/base:core_headers", @@ -128,7 +138,7 @@ cc_test( size = "small", srcs = ["span_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":span", "//absl/base:config", @@ -147,6 +157,7 @@ cc_test( size = "small", srcs = ["span_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":span", "//absl/base:config", @@ -162,11 +173,13 @@ cc_test( cc_library( name = "optional", - srcs = ["optional.cc"], + srcs = ["internal/optional.h"], hdrs = ["optional.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bad_optional_access", + "//absl/base:base_internal", "//absl/base:config", "//absl/base:core_headers", "//absl/memory", @@ -180,7 +193,7 @@ cc_library( srcs = ["bad_optional_access.cc"], hdrs = ["bad_optional_access.h"], copts = ABSL_DEFAULT_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base", "//absl/base:config", @@ -192,7 +205,7 @@ cc_library( srcs = ["bad_variant_access.cc"], hdrs = ["bad_variant_access.h"], copts = ABSL_EXCEPTIONS_FLAG + ABSL_DEFAULT_COPTS, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base", "//absl/base:config", @@ -206,7 +219,7 @@ cc_test( "optional_test.cc", ], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":optional", "//absl/base", @@ -223,7 +236,7 @@ cc_test( "optional_exception_safety_test.cc", ], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":optional", "//absl/base:exception_safety_testing", @@ -236,6 +249,7 @@ cc_library( srcs = ["internal/variant.h"], hdrs = ["variant.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":bad_variant_access", "//absl/base:base_internal", @@ -251,7 +265,7 @@ cc_test( size = "small", srcs = ["variant_test.cc"], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":variant", "//absl/base:config", @@ -269,6 +283,7 @@ cc_test( "variant_benchmark.cc", ], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["benchmark"], deps = [ ":variant", @@ -284,7 +299,7 @@ cc_test( "variant_exception_safety_test.cc", ], copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG, - linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS, + linkopts = ABSL_EXCEPTIONS_FLAG_LINKOPTS + ABSL_DEFAULT_LINKOPTS, deps = [ ":variant", "//absl/base:config", @@ -293,3 +308,27 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "compare", + hdrs = ["compare.h"], + copts = ABSL_DEFAULT_COPTS, + deps = [ + "//absl/base:core_headers", + "//absl/meta:type_traits", + ], +) + +cc_test( + name = "compare_test", + size = "small", + srcs = [ + "compare_test.cc", + ], + copts = ABSL_TEST_COPTS, + deps = [ + ":compare", + "//absl/base", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index e8620766..4ce685da 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -13,216 +13,336 @@ # See the License for the specific language governing permissions and # limitations under the License. # - -list(APPEND TYPES_PUBLIC_HEADERS - "any.h" - "bad_any_cast.h" - "bad_optional_access.h" - "optional.h" - "span.h" - "variant.h" +absl_cc_library( + NAME + any + HDRS + "any.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::bad_any_cast + absl::config + absl::core_headers + absl::type_traits + absl::utility + PUBLIC ) +absl_cc_library( + NAME + bad_any_cast + HDRS + "bad_any_cast.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::bad_any_cast_impl + absl::config + PUBLIC +) -# any library -absl_header_library( - TARGET - absl_any - PUBLIC_LIBRARIES - absl::bad_any_cast - absl::base - absl::meta - absl::utility - PRIVATE_COMPILE_FLAGS +absl_cc_library( + NAME + bad_any_cast_impl + SRCS + "bad_any_cast.h" + "bad_any_cast.cc" + COPTS + ${ABSL_DEFAULT_COPTS} ${ABSL_EXCEPTIONS_FLAG} - EXPORT_NAME - any + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::base + absl::config ) -# span library -absl_header_library( - TARGET - absl_span - PUBLIC_LIBRARIES - absl::utility - EXPORT_NAME - span +absl_cc_test( + NAME + any_test + SRCS + "any_test.cc" + COPTS + ${ABSL_TEST_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::any + absl::base + absl::config + absl::exception_testing + absl::test_instance_tracker + gmock_main ) +absl_cc_test( + NAME + any_test_noexceptions + SRCS + "any_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::any + absl::base + absl::config + absl::exception_testing + absl::test_instance_tracker + gmock_main +) -# bad_any_cast library -list(APPEND BAD_ANY_CAST_SRC - "bad_any_cast.cc" - ${TYPES_PUBLIC_HEADERS} +absl_cc_test( + NAME + any_exception_safety_test + SRCS + "any_exception_safety_test.cc" + COPTS + ${ABSL_TEST_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::any + absl::exception_safety_testing + gmock_main ) -absl_library( - TARGET - absl_bad_any_cast - SOURCES - ${BAD_ANY_CAST_SRC} - PUBLIC_LIBRARIES - EXPORT_NAME - bad_any_cast +absl_cc_library( + NAME + span + HDRS + "span.h" + SRCS + "internal/span.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::algorithm + absl::core_headers + absl::throw_delegate + absl::type_traits + PUBLIC ) +absl_cc_test( + NAME + span_test + SRCS + "span_test.cc" + COPTS + ${ABSL_TEST_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::span + absl::base + absl::config + absl::core_headers + absl::exception_testing + absl::fixed_array + absl::inlined_vector + absl::hash_testing + absl::strings + gmock_main +) -# optional library -list(APPEND OPTIONAL_SRC - "optional.cc" +absl_cc_test( + NAME + span_test_noexceptions + SRCS + "span_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::span + absl::base + absl::config + absl::core_headers + absl::exception_testing + absl::fixed_array + absl::inlined_vector + absl::hash_testing + absl::strings + gmock_main ) -absl_library( - TARGET - absl_optional - SOURCES - ${OPTIONAL_SRC} - PUBLIC_LIBRARIES +absl_cc_library( + NAME + optional + HDRS + "optional.h" + SRCS + "internal/optional.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS absl::bad_optional_access - absl::base + absl::base_internal + absl::config + absl::core_headers absl::memory - absl::meta + absl::type_traits absl::utility - EXPORT_NAME - optional + PUBLIC ) - -set(BAD_OPTIONAL_ACCESS_SRC "bad_optional_access.cc") -set(BAD_OPTIONAL_ACCESS_LIBRARIES absl::base) - -absl_library( - TARGET - absl_bad_optional_access - SOURCES - ${BAD_OPTIONAL_ACCESS_SRC} - PUBLIC_LIBRARIES - ${BAD_OPTIONAL_ACCESS_PUBLIC_LIBRARIES} - EXPORT_NAME +absl_cc_library( + NAME bad_optional_access -) - -# variant library -absl_library( - TARGET - absl_variant - SOURCES - "bad_variant_access.h" "bad_variant_access.cc" "variant.h" "internal/variant.h" - PUBLIC_LIBRARIES - absl::base absl::meta absl::utility - PRIVATE_COMPILE_FLAGS + HDRS + "bad_optional_access.h" + SRCS + "bad_optional_access.cc" + COPTS + ${ABSL_DEFAULT_COPTS} ${ABSL_EXCEPTIONS_FLAG} - EXPORT_NAME - variant + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::base + absl::config + PUBLIC ) -# -## TESTS -# - - -# test any_test -set(ANY_TEST_SRC "any_test.cc") -set(ANY_TEST_PUBLIC_LIBRARIES absl::base absl_internal_throw_delegate absl::any absl::bad_any_cast absl::test_instance_tracker) - -absl_test( - TARGET - any_test - SOURCES - ${ANY_TEST_SRC} - PUBLIC_LIBRARIES - ${ANY_TEST_PUBLIC_LIBRARIES} - PRIVATE_COMPILE_FLAGS +absl_cc_library( + NAME + bad_variant_access + HDRS + "bad_variant_access.h" + SRCS + "bad_variant_access.cc" + COPTS + ${ABSL_DEFAULT_COPTS} ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::base + absl::config + PUBLIC ) - -# test any_test_noexceptions -absl_test( - TARGET - any_test_noexceptions - SOURCES - ${ANY_TEST_SRC} - PUBLIC_LIBRARIES - ${ANY_TEST_PUBLIC_LIBRARIES} -) - -# test any_exception_safety_test -set(ANY_EXCEPTION_SAFETY_TEST_SRC "any_exception_safety_test.cc") -set(ANY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES - absl::any - absl::base - absl_internal_exception_safety_testing -) - -absl_test( - TARGET - any_exception_safety_test - SOURCES - ${ANY_EXCEPTION_SAFETY_TEST_SRC} - PUBLIC_LIBRARIES - ${ANY_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES} - PRIVATE_COMPILE_FLAGS +absl_cc_test( + NAME + optional_test + SRCS + "optional_test.cc" + COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::optional + absl::base + absl::config + absl::type_traits + absl::strings + gmock_main ) - -# test span_test -set(SPAN_TEST_SRC "span_test.cc") -set(SPAN_TEST_PUBLIC_LIBRARIES absl::base absl::strings absl_internal_throw_delegate absl::span absl::test_instance_tracker) - -absl_test( - TARGET - span_test - SOURCES - ${SPAN_TEST_SRC} - PUBLIC_LIBRARIES - ${SPAN_TEST_PUBLIC_LIBRARIES} - PRIVATE_COMPILE_FLAGS +absl_cc_test( + NAME + optional_exception_safety_test + SRCS + "optional_exception_safety_test.cc" + COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::optional + absl::exception_safety_testing + gmock_main ) - -# test span_test_noexceptions -absl_test( - TARGET - span_test_noexceptions - SOURCES - ${SPAN_TEST_SRC} - PUBLIC_LIBRARIES - ${SPAN_TEST_PUBLIC_LIBRARIES} +absl_cc_library( + NAME + variant + HDRS + "variant.h" + SRCS + "internal/variant.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::bad_variant_access + absl::base_internal + absl::config + absl::core_headers + absl::type_traits + absl::utility + PUBLIC ) - - -# test optional_test -set(OPTIONAL_TEST_SRC "optional_test.cc") -set(OPTIONAL_TEST_PUBLIC_LIBRARIES absl::base absl_internal_throw_delegate absl::optional absl_bad_optional_access) - -absl_test( - TARGET - optional_test - SOURCES - ${OPTIONAL_TEST_SRC} - PUBLIC_LIBRARIES - ${OPTIONAL_TEST_PUBLIC_LIBRARIES} +absl_cc_test( + NAME + variant_test + SRCS + "variant_test.cc" + COPTS + ${ABSL_TEST_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::variant + absl::config + absl::core_headers + absl::memory + absl::type_traits + absl::strings + gmock_main ) +absl_cc_library( + NAME + compare + HDRS + "compare.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + absl::type_traits + PUBLIC +) -# test optional_exception_safety_test -set(OPTIONAL_EXCEPTION_SAFETY_TEST_SRC "optional_exception_safety_test.cc") -set(OPTIONAL_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES - absl::optional - absl_internal_exception_safety_testing +absl_cc_test( + NAME + compare_test + SRCS + "compare_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::base + absl::compare + gmock_main ) -absl_test( - TARGET - optional_exception_safety_test - SOURCES - ${OPTIONAL_EXCEPTION_SAFETY_TEST_SRC} - PUBLIC_LIBRARIES - ${OPTIONAL_EXCEPTION_SAFETY_TEST_PUBLIC_LIBRARIES} - PRIVATE_COMPILE_FLAGS +# TODO(cohenjon,zhangxy) Figure out why this test is failing on gcc 4.8 +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) +else() +absl_cc_test( + NAME + variant_exception_safety_test + SRCS + "variant_exception_safety_test.cc" + COPTS + ${ABSL_TEST_COPTS} ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::variant + absl::config + absl::exception_safety_testing + absl::memory + gmock_main ) +endif() diff --git a/absl/types/any.h b/absl/types/any.h index dc3bfcfe..7cae9dd0 100644 --- a/absl/types/any.h +++ b/absl/types/any.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -58,15 +58,15 @@ #ifdef ABSL_HAVE_STD_ANY -#include <any> +#include <any> // IWYU pragma: export namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { using std::any; using std::any_cast; using std::bad_any_cast; using std::make_any; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else // ABSL_HAVE_STD_ANY @@ -93,7 +93,7 @@ using std::make_any; #endif // !defined(__GNUC__) || defined(__GXX_RTTI) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace any_internal { @@ -533,7 +533,7 @@ T* any_cast(any* operand) noexcept { : nullptr; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #undef ABSL_ANY_DETAIL_HAS_RTTI diff --git a/absl/types/any_exception_safety_test.cc b/absl/types/any_exception_safety_test.cc index f9dd8c48..5d7d8a5c 100644 --- a/absl/types/any_exception_safety_test.cc +++ b/absl/types/any_exception_safety_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -135,6 +135,7 @@ TEST(AnyExceptionSafety, Assignment) { EXPECT_TRUE(strong_empty_any_tester.Test(assign_val)); EXPECT_TRUE(strong_empty_any_tester.Test(move)); } + // libstdc++ std::any fails this test #if !defined(ABSL_HAVE_STD_ANY) TEST(AnyExceptionSafety, Emplace) { diff --git a/absl/types/any_test.cc b/absl/types/any_test.cc index 115e78df..87104721 100644 --- a/absl/types/any_test.cc +++ b/absl/types/any_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -154,6 +154,14 @@ TEST(AnyTest, InPlaceConstruction) { EXPECT_EQ(5, v.value); } +TEST(AnyTest, InPlaceConstructionVariableTemplate) { + const CopyOnly copy_only{}; + absl::any o(absl::in_place_type<IntMoveOnlyCopyOnly>, 5, MoveOnly(), + copy_only); + auto& v = absl::any_cast<IntMoveOnlyCopyOnly&>(o); + EXPECT_EQ(5, v.value); +} + TEST(AnyTest, InPlaceConstructionWithCV) { const CopyOnly copy_only{}; absl::any o(absl::in_place_type_t<const volatile IntMoveOnlyCopyOnly>(), 5, @@ -162,12 +170,26 @@ TEST(AnyTest, InPlaceConstructionWithCV) { EXPECT_EQ(5, v.value); } +TEST(AnyTest, InPlaceConstructionWithCVVariableTemplate) { + const CopyOnly copy_only{}; + absl::any o(absl::in_place_type<const volatile IntMoveOnlyCopyOnly>, 5, + MoveOnly(), copy_only); + auto& v = absl::any_cast<IntMoveOnlyCopyOnly&>(o); + EXPECT_EQ(5, v.value); +} + TEST(AnyTest, InPlaceConstructionWithFunction) { absl::any o(absl::in_place_type_t<FunctionType>(), FunctionToEmplace); FunctionType*& construction_result = absl::any_cast<FunctionType*&>(o); EXPECT_EQ(&FunctionToEmplace, construction_result); } +TEST(AnyTest, InPlaceConstructionWithFunctionVariableTemplate) { + absl::any o(absl::in_place_type<FunctionType>, FunctionToEmplace); + auto& construction_result = absl::any_cast<FunctionType*&>(o); + EXPECT_EQ(&FunctionToEmplace, construction_result); +} + TEST(AnyTest, InPlaceConstructionWithArray) { ArrayType ar = {5, 42}; absl::any o(absl::in_place_type_t<ArrayType>(), ar); @@ -175,6 +197,13 @@ TEST(AnyTest, InPlaceConstructionWithArray) { EXPECT_EQ(&ar[0], construction_result); } +TEST(AnyTest, InPlaceConstructionWithArrayVariableTemplate) { + ArrayType ar = {5, 42}; + absl::any o(absl::in_place_type<ArrayType>, ar); + auto& construction_result = absl::any_cast<DecayedArray&>(o); + EXPECT_EQ(&ar[0], construction_result); +} + TEST(AnyTest, InPlaceConstructionIlist) { const CopyOnly copy_only{}; absl::any o(absl::in_place_type_t<ListMoveOnlyCopyOnly>(), {1, 2, 3, 4}, @@ -184,6 +213,15 @@ TEST(AnyTest, InPlaceConstructionIlist) { EXPECT_EQ(expected_values, v.values); } +TEST(AnyTest, InPlaceConstructionIlistVariableTemplate) { + const CopyOnly copy_only{}; + absl::any o(absl::in_place_type<ListMoveOnlyCopyOnly>, {1, 2, 3, 4}, + MoveOnly(), copy_only); + auto& v = absl::any_cast<ListMoveOnlyCopyOnly&>(o); + std::vector<int> expected_values = {1, 2, 3, 4}; + EXPECT_EQ(expected_values, v.values); +} + TEST(AnyTest, InPlaceConstructionIlistWithCV) { const CopyOnly copy_only{}; absl::any o(absl::in_place_type_t<const volatile ListMoveOnlyCopyOnly>(), @@ -193,11 +231,25 @@ TEST(AnyTest, InPlaceConstructionIlistWithCV) { EXPECT_EQ(expected_values, v.values); } +TEST(AnyTest, InPlaceConstructionIlistWithCVVariableTemplate) { + const CopyOnly copy_only{}; + absl::any o(absl::in_place_type<const volatile ListMoveOnlyCopyOnly>, + {1, 2, 3, 4}, MoveOnly(), copy_only); + auto& v = absl::any_cast<ListMoveOnlyCopyOnly&>(o); + std::vector<int> expected_values = {1, 2, 3, 4}; + EXPECT_EQ(expected_values, v.values); +} + TEST(AnyTest, InPlaceNoArgs) { absl::any o(absl::in_place_type_t<int>{}); EXPECT_EQ(0, absl::any_cast<int&>(o)); } +TEST(AnyTest, InPlaceNoArgsVariableTemplate) { + absl::any o(absl::in_place_type<int>); + EXPECT_EQ(0, absl::any_cast<int&>(o)); +} + template <typename Enabler, typename T, typename... Args> struct CanEmplaceAnyImpl : std::false_type {}; @@ -501,7 +553,7 @@ TEST(AnyTest, Copy) { InstanceTracker tracker_raii; { - absl::any o(absl::in_place_type_t<CopyableOnlyInstance>{}, 123); + absl::any o(absl::in_place_type<CopyableOnlyInstance>, 123); CopyableOnlyInstance* f1 = absl::any_cast<CopyableOnlyInstance>(&o); absl::any o2(o); @@ -622,7 +674,7 @@ TEST(AnyTest, ThrowBadAlloc) { } { - absl::any a(absl::in_place_type_t<int>{}); + absl::any a(absl::in_place_type<int>); ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<float&>(a)); ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<const float&>(a)); ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(absl::any_cast<float&&>(absl::any{})); @@ -665,7 +717,7 @@ TEST(AnyTest, FailedCopy) { } { - absl::any src(absl::in_place_type_t<BadCopyable>{}); + absl::any src(absl::in_place_type<BadCopyable>); ABSL_ANY_TEST_EXPECT_BAD_COPY(absl::any{src}); } @@ -677,21 +729,21 @@ TEST(AnyTest, FailedCopy) { { BadCopyable bad; - absl::any target(absl::in_place_type_t<BadCopyable>{}); + absl::any target(absl::in_place_type<BadCopyable>); ABSL_ANY_TEST_EXPECT_BAD_COPY(target = bad); EXPECT_TRUE(target.has_value()); } { - absl::any src(absl::in_place_type_t<BadCopyable>{}); + absl::any src(absl::in_place_type<BadCopyable>); absl::any target; ABSL_ANY_TEST_EXPECT_BAD_COPY(target = src); EXPECT_FALSE(target.has_value()); } { - absl::any src(absl::in_place_type_t<BadCopyable>{}); - absl::any target(absl::in_place_type_t<BadCopyable>{}); + absl::any src(absl::in_place_type<BadCopyable>); + absl::any target(absl::in_place_type<BadCopyable>); ABSL_ANY_TEST_EXPECT_BAD_COPY(target = src); EXPECT_TRUE(target.has_value()); } @@ -707,7 +759,7 @@ TEST(AnyTest, FailedEmplace) { { BadCopyable bad; - absl::any target(absl::in_place_type_t<int>{}); + absl::any target(absl::in_place_type<int>); ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace<BadCopyable>(bad)); #if defined(ABSL_HAVE_STD_ANY) && defined(__GLIBCXX__) // libstdc++ std::any::emplace() implementation (as of 7.2) has a bug: if an diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc index 6244d09e..062675df 100644 --- a/absl/types/bad_any_cast.cc +++ b/absl/types/bad_any_cast.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { bad_any_cast::~bad_any_cast() = default; @@ -40,7 +40,7 @@ void ThrowBadAnyCast() { } } // namespace any_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_STD_ANY diff --git a/absl/types/bad_any_cast.h b/absl/types/bad_any_cast.h index 7df9afbb..8a8476ce 100644 --- a/absl/types/bad_any_cast.h +++ b/absl/types/bad_any_cast.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,15 +30,15 @@ #include <any> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { using std::bad_any_cast; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else // ABSL_HAVE_STD_ANY namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // bad_any_cast @@ -67,7 +67,7 @@ namespace any_internal { [[noreturn]] void ThrowBadAnyCast(); } // namespace any_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_STD_ANY diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc index 2dc74d3b..440adac2 100644 --- a/absl/types/bad_optional_access.cc +++ b/absl/types/bad_optional_access.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { bad_optional_access::~bad_optional_access() = default; @@ -42,7 +42,7 @@ void throw_bad_optional_access() { } } // namespace optional_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_STD_OPTIONAL diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h index 1f56ff64..585c7c77 100644 --- a/absl/types/bad_optional_access.h +++ b/absl/types/bad_optional_access.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,15 +30,15 @@ #include <optional> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { using std::bad_optional_access; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else // ABSL_HAVE_STD_OPTIONAL namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // bad_optional_access @@ -70,7 +70,7 @@ namespace optional_internal { [[noreturn]] void throw_bad_optional_access(); } // namespace optional_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_STD_OPTIONAL diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc index a646ff53..d60dae9a 100644 --- a/absl/types/bad_variant_access.cc +++ b/absl/types/bad_variant_access.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +23,7 @@ #include "absl/base/internal/raw_logging.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { ////////////////////////// // [variant.bad.access] // @@ -58,7 +58,7 @@ void Rethrow() { } } // namespace variant_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_STD_VARIANT diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h index e0490842..8d635b57 100644 --- a/absl/types/bad_variant_access.h +++ b/absl/types/bad_variant_access.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -30,15 +30,15 @@ #include <variant> namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { using std::bad_variant_access; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else // ABSL_HAVE_STD_VARIANT namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // bad_variant_access @@ -74,7 +74,7 @@ namespace variant_internal { [[noreturn]] void Rethrow(); } // namespace variant_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_HAVE_STD_VARIANT diff --git a/absl/types/compare.h b/absl/types/compare.h new file mode 100644 index 00000000..6f371be7 --- /dev/null +++ b/absl/types/compare.h @@ -0,0 +1,510 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// compare.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::weak_equality`, `absl::strong_equality`, +// `absl::partial_ordering`, `absl::weak_ordering`, and `absl::strong_ordering` +// types for storing the results of three way comparisons. +// +// Example: +// absl::weak_ordering compare(const std::string& a, const std::string& b); +// +// These are C++11 compatible versions of the C++20 corresponding types +// (`std::weak_equality`, etc.) and are designed to be drop-in replacements +// for code compliant with C++20. + +#ifndef ABSL_TYPES_COMPARE_H_ +#define ABSL_TYPES_COMPARE_H_ + +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <type_traits> + +#include "absl/base/attributes.h" +#include "absl/meta/type_traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace compare_internal { + +using value_type = int8_t; + +template <typename T> +struct Fail { + static_assert(sizeof(T) < 0, "Only literal `0` is allowed."); +}; + +// We need the NullPtrT template to avoid triggering the modernize-use-nullptr +// ClangTidy warning in user code. +template <typename NullPtrT = std::nullptr_t> +struct OnlyLiteralZero { + constexpr OnlyLiteralZero(NullPtrT) noexcept {} // NOLINT + + // Fails compilation when `nullptr` or integral type arguments other than + // `int` are passed. This constructor doesn't accept `int` because literal `0` + // has type `int`. Literal `0` arguments will be implicitly converted to + // `std::nullptr_t` and accepted by the above constructor, while other `int` + // arguments will fail to be converted and cause compilation failure. + template < + typename T, + typename = typename std::enable_if< + std::is_same<T, std::nullptr_t>::value || + (std::is_integral<T>::value && !std::is_same<T, int>::value)>::type, + typename = typename Fail<T>::type> + OnlyLiteralZero(T); // NOLINT +}; + +enum class eq : value_type { + equal = 0, + equivalent = equal, + nonequal = 1, + nonequivalent = nonequal, +}; + +enum class ord : value_type { less = -1, greater = 1 }; + +enum class ncmp : value_type { unordered = -127 }; + +// These template base classes allow for defining the values of the constants +// in the header file (for performance) without using inline variables (which +// aren't available in C++11). +template <typename T> +struct weak_equality_base { + ABSL_CONST_INIT static const T equivalent; + ABSL_CONST_INIT static const T nonequivalent; +}; +template <typename T> +const T weak_equality_base<T>::equivalent(eq::equivalent); +template <typename T> +const T weak_equality_base<T>::nonequivalent(eq::nonequivalent); + +template <typename T> +struct strong_equality_base { + ABSL_CONST_INIT static const T equal; + ABSL_CONST_INIT static const T nonequal; + ABSL_CONST_INIT static const T equivalent; + ABSL_CONST_INIT static const T nonequivalent; +}; +template <typename T> +const T strong_equality_base<T>::equal(eq::equal); +template <typename T> +const T strong_equality_base<T>::nonequal(eq::nonequal); +template <typename T> +const T strong_equality_base<T>::equivalent(eq::equivalent); +template <typename T> +const T strong_equality_base<T>::nonequivalent(eq::nonequivalent); + +template <typename T> +struct partial_ordering_base { + ABSL_CONST_INIT static const T less; + ABSL_CONST_INIT static const T equivalent; + ABSL_CONST_INIT static const T greater; + ABSL_CONST_INIT static const T unordered; +}; +template <typename T> +const T partial_ordering_base<T>::less(ord::less); +template <typename T> +const T partial_ordering_base<T>::equivalent(eq::equivalent); +template <typename T> +const T partial_ordering_base<T>::greater(ord::greater); +template <typename T> +const T partial_ordering_base<T>::unordered(ncmp::unordered); + +template <typename T> +struct weak_ordering_base { + ABSL_CONST_INIT static const T less; + ABSL_CONST_INIT static const T equivalent; + ABSL_CONST_INIT static const T greater; +}; +template <typename T> +const T weak_ordering_base<T>::less(ord::less); +template <typename T> +const T weak_ordering_base<T>::equivalent(eq::equivalent); +template <typename T> +const T weak_ordering_base<T>::greater(ord::greater); + +template <typename T> +struct strong_ordering_base { + ABSL_CONST_INIT static const T less; + ABSL_CONST_INIT static const T equal; + ABSL_CONST_INIT static const T equivalent; + ABSL_CONST_INIT static const T greater; +}; +template <typename T> +const T strong_ordering_base<T>::less(ord::less); +template <typename T> +const T strong_ordering_base<T>::equal(eq::equal); +template <typename T> +const T strong_ordering_base<T>::equivalent(eq::equivalent); +template <typename T> +const T strong_ordering_base<T>::greater(ord::greater); + +} // namespace compare_internal + +class weak_equality + : public compare_internal::weak_equality_base<weak_equality> { + explicit constexpr weak_equality(compare_internal::eq v) noexcept + : value_(static_cast<compare_internal::value_type>(v)) {} + friend struct compare_internal::weak_equality_base<weak_equality>; + + public: + // Comparisons + friend constexpr bool operator==( + weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ == 0; + } + friend constexpr bool operator!=( + weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ != 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + weak_equality v) noexcept { + return 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + weak_equality v) noexcept { + return 0 != v.value_; + } + + private: + compare_internal::value_type value_; +}; + +class strong_equality + : public compare_internal::strong_equality_base<strong_equality> { + explicit constexpr strong_equality(compare_internal::eq v) noexcept + : value_(static_cast<compare_internal::value_type>(v)) {} + friend struct compare_internal::strong_equality_base<strong_equality>; + + public: + // Conversion + constexpr operator weak_equality() const noexcept { // NOLINT + return value_ == 0 ? weak_equality::equivalent + : weak_equality::nonequivalent; + } + // Comparisons + friend constexpr bool operator==( + strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ == 0; + } + friend constexpr bool operator!=( + strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ != 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + strong_equality v) noexcept { + return 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + strong_equality v) noexcept { + return 0 != v.value_; + } + + private: + compare_internal::value_type value_; +}; + +class partial_ordering + : public compare_internal::partial_ordering_base<partial_ordering> { + explicit constexpr partial_ordering(compare_internal::eq v) noexcept + : value_(static_cast<compare_internal::value_type>(v)) {} + explicit constexpr partial_ordering(compare_internal::ord v) noexcept + : value_(static_cast<compare_internal::value_type>(v)) {} + explicit constexpr partial_ordering(compare_internal::ncmp v) noexcept + : value_(static_cast<compare_internal::value_type>(v)) {} + friend struct compare_internal::partial_ordering_base<partial_ordering>; + + constexpr bool is_ordered() const noexcept { + return value_ != + compare_internal::value_type(compare_internal::ncmp::unordered); + } + + public: + // Conversion + constexpr operator weak_equality() const noexcept { // NOLINT + return value_ == 0 ? weak_equality::equivalent + : weak_equality::nonequivalent; + } + // Comparisons + friend constexpr bool operator==( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ == 0; + } + friend constexpr bool operator!=( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return !v.is_ordered() || v.value_ != 0; + } + friend constexpr bool operator<( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ < 0; + } + friend constexpr bool operator<=( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ <= 0; + } + friend constexpr bool operator>( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ > 0; + } + friend constexpr bool operator>=( + partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.is_ordered() && v.value_ >= 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return !v.is_ordered() || 0 != v.value_; + } + friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 < v.value_; + } + friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 <= v.value_; + } + friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 > v.value_; + } + friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, + partial_ordering v) noexcept { + return v.is_ordered() && 0 >= v.value_; + } + + private: + compare_internal::value_type value_; +}; + +class weak_ordering + : public compare_internal::weak_ordering_base<weak_ordering> { + explicit constexpr weak_ordering(compare_internal::eq v) noexcept + : value_(static_cast<compare_internal::value_type>(v)) {} + explicit constexpr weak_ordering(compare_internal::ord v) noexcept + : value_(static_cast<compare_internal::value_type>(v)) {} + friend struct compare_internal::weak_ordering_base<weak_ordering>; + + public: + // Conversions + constexpr operator weak_equality() const noexcept { // NOLINT + return value_ == 0 ? weak_equality::equivalent + : weak_equality::nonequivalent; + } + constexpr operator partial_ordering() const noexcept { // NOLINT + return value_ == 0 ? partial_ordering::equivalent + : (value_ < 0 ? partial_ordering::less + : partial_ordering::greater); + } + // Comparisons + friend constexpr bool operator==( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ == 0; + } + friend constexpr bool operator!=( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ != 0; + } + friend constexpr bool operator<( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ < 0; + } + friend constexpr bool operator<=( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ <= 0; + } + friend constexpr bool operator>( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ > 0; + } + friend constexpr bool operator>=( + weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ >= 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 != v.value_; + } + friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 < v.value_; + } + friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 <= v.value_; + } + friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 > v.value_; + } + friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, + weak_ordering v) noexcept { + return 0 >= v.value_; + } + + private: + compare_internal::value_type value_; +}; + +class strong_ordering + : public compare_internal::strong_ordering_base<strong_ordering> { + explicit constexpr strong_ordering(compare_internal::eq v) noexcept + : value_(static_cast<compare_internal::value_type>(v)) {} + explicit constexpr strong_ordering(compare_internal::ord v) noexcept + : value_(static_cast<compare_internal::value_type>(v)) {} + friend struct compare_internal::strong_ordering_base<strong_ordering>; + + public: + // Conversions + constexpr operator weak_equality() const noexcept { // NOLINT + return value_ == 0 ? weak_equality::equivalent + : weak_equality::nonequivalent; + } + constexpr operator strong_equality() const noexcept { // NOLINT + return value_ == 0 ? strong_equality::equal : strong_equality::nonequal; + } + constexpr operator partial_ordering() const noexcept { // NOLINT + return value_ == 0 ? partial_ordering::equivalent + : (value_ < 0 ? partial_ordering::less + : partial_ordering::greater); + } + constexpr operator weak_ordering() const noexcept { // NOLINT + return value_ == 0 + ? weak_ordering::equivalent + : (value_ < 0 ? weak_ordering::less : weak_ordering::greater); + } + // Comparisons + friend constexpr bool operator==( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ == 0; + } + friend constexpr bool operator!=( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ != 0; + } + friend constexpr bool operator<( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ < 0; + } + friend constexpr bool operator<=( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ <= 0; + } + friend constexpr bool operator>( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ > 0; + } + friend constexpr bool operator>=( + strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { + return v.value_ >= 0; + } + friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 == v.value_; + } + friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 != v.value_; + } + friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 < v.value_; + } + friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 <= v.value_; + } + friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 > v.value_; + } + friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, + strong_ordering v) noexcept { + return 0 >= v.value_; + } + + private: + compare_internal::value_type value_; +}; + +namespace compare_internal { +// We also provide these comparator adapter functions for internal absl use. + +// Helper functions to do a boolean comparison of two keys given a boolean +// or three-way comparator. +// SFINAE prevents implicit conversions to bool (such as from int). +template <typename Bool, + absl::enable_if_t<std::is_same<bool, Bool>::value, int> = 0> +constexpr bool compare_result_as_less_than(const Bool r) { return r; } +constexpr bool compare_result_as_less_than(const absl::weak_ordering r) { + return r < 0; +} + +template <typename Compare, typename K, typename LK> +constexpr bool do_less_than_comparison(const Compare &compare, const K &x, + const LK &y) { + return compare_result_as_less_than(compare(x, y)); +} + +// Helper functions to do a three-way comparison of two keys given a boolean or +// three-way comparator. +// SFINAE prevents implicit conversions to int (such as from bool). +template <typename Int, + absl::enable_if_t<std::is_same<int, Int>::value, int> = 0> +constexpr absl::weak_ordering compare_result_as_ordering(const Int c) { + return c < 0 ? absl::weak_ordering::less + : c == 0 ? absl::weak_ordering::equivalent + : absl::weak_ordering::greater; +} +constexpr absl::weak_ordering compare_result_as_ordering( + const absl::weak_ordering c) { + return c; +} + +template < + typename Compare, typename K, typename LK, + absl::enable_if_t<!std::is_same<bool, absl::result_of_t<Compare( + const K &, const LK &)>>::value, + int> = 0> +constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare, + const K &x, const LK &y) { + return compare_result_as_ordering(compare(x, y)); +} +template < + typename Compare, typename K, typename LK, + absl::enable_if_t<std::is_same<bool, absl::result_of_t<Compare( + const K &, const LK &)>>::value, + int> = 0> +constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare, + const K &x, const LK &y) { + return compare(x, y) ? absl::weak_ordering::less + : compare(y, x) ? absl::weak_ordering::greater + : absl::weak_ordering::equivalent; +} + +} // namespace compare_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_TYPES_COMPARE_H_ diff --git a/absl/types/compare_test.cc b/absl/types/compare_test.cc new file mode 100644 index 00000000..11b3ad40 --- /dev/null +++ b/absl/types/compare_test.cc @@ -0,0 +1,313 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/types/compare.h" + +#include "gtest/gtest.h" +#include "absl/base/casts.h" + +namespace absl { +inline namespace lts_2019_08_08 { +namespace { + +// This is necessary to avoid a bunch of lint warnings suggesting that we use +// EXPECT_EQ/etc., which doesn't work in this case because they convert the `0` +// to an int, which can't be converted to the unspecified zero type. +bool Identity(bool b) { return b; } + +TEST(Compare, WeakEquality) { + EXPECT_TRUE(Identity(weak_equality::equivalent == 0)); + EXPECT_TRUE(Identity(0 == weak_equality::equivalent)); + EXPECT_TRUE(Identity(weak_equality::nonequivalent != 0)); + EXPECT_TRUE(Identity(0 != weak_equality::nonequivalent)); +} + +TEST(Compare, StrongEquality) { + EXPECT_TRUE(Identity(strong_equality::equal == 0)); + EXPECT_TRUE(Identity(0 == strong_equality::equal)); + EXPECT_TRUE(Identity(strong_equality::nonequal != 0)); + EXPECT_TRUE(Identity(0 != strong_equality::nonequal)); + EXPECT_TRUE(Identity(strong_equality::equivalent == 0)); + EXPECT_TRUE(Identity(0 == strong_equality::equivalent)); + EXPECT_TRUE(Identity(strong_equality::nonequivalent != 0)); + EXPECT_TRUE(Identity(0 != strong_equality::nonequivalent)); +} + +TEST(Compare, PartialOrdering) { + EXPECT_TRUE(Identity(partial_ordering::less < 0)); + EXPECT_TRUE(Identity(0 > partial_ordering::less)); + EXPECT_TRUE(Identity(partial_ordering::less <= 0)); + EXPECT_TRUE(Identity(0 >= partial_ordering::less)); + EXPECT_TRUE(Identity(partial_ordering::equivalent == 0)); + EXPECT_TRUE(Identity(0 == partial_ordering::equivalent)); + EXPECT_TRUE(Identity(partial_ordering::greater > 0)); + EXPECT_TRUE(Identity(0 < partial_ordering::greater)); + EXPECT_TRUE(Identity(partial_ordering::greater >= 0)); + EXPECT_TRUE(Identity(0 <= partial_ordering::greater)); + EXPECT_TRUE(Identity(partial_ordering::unordered != 0)); + EXPECT_TRUE(Identity(0 != partial_ordering::unordered)); + EXPECT_FALSE(Identity(partial_ordering::unordered < 0)); + EXPECT_FALSE(Identity(0 < partial_ordering::unordered)); + EXPECT_FALSE(Identity(partial_ordering::unordered <= 0)); + EXPECT_FALSE(Identity(0 <= partial_ordering::unordered)); + EXPECT_FALSE(Identity(partial_ordering::unordered > 0)); + EXPECT_FALSE(Identity(0 > partial_ordering::unordered)); + EXPECT_FALSE(Identity(partial_ordering::unordered >= 0)); + EXPECT_FALSE(Identity(0 >= partial_ordering::unordered)); +} + +TEST(Compare, WeakOrdering) { + EXPECT_TRUE(Identity(weak_ordering::less < 0)); + EXPECT_TRUE(Identity(0 > weak_ordering::less)); + EXPECT_TRUE(Identity(weak_ordering::less <= 0)); + EXPECT_TRUE(Identity(0 >= weak_ordering::less)); + EXPECT_TRUE(Identity(weak_ordering::equivalent == 0)); + EXPECT_TRUE(Identity(0 == weak_ordering::equivalent)); + EXPECT_TRUE(Identity(weak_ordering::greater > 0)); + EXPECT_TRUE(Identity(0 < weak_ordering::greater)); + EXPECT_TRUE(Identity(weak_ordering::greater >= 0)); + EXPECT_TRUE(Identity(0 <= weak_ordering::greater)); +} + +TEST(Compare, StrongOrdering) { + EXPECT_TRUE(Identity(strong_ordering::less < 0)); + EXPECT_TRUE(Identity(0 > strong_ordering::less)); + EXPECT_TRUE(Identity(strong_ordering::less <= 0)); + EXPECT_TRUE(Identity(0 >= strong_ordering::less)); + EXPECT_TRUE(Identity(strong_ordering::equal == 0)); + EXPECT_TRUE(Identity(0 == strong_ordering::equal)); + EXPECT_TRUE(Identity(strong_ordering::equivalent == 0)); + EXPECT_TRUE(Identity(0 == strong_ordering::equivalent)); + EXPECT_TRUE(Identity(strong_ordering::greater > 0)); + EXPECT_TRUE(Identity(0 < strong_ordering::greater)); + EXPECT_TRUE(Identity(strong_ordering::greater >= 0)); + EXPECT_TRUE(Identity(0 <= strong_ordering::greater)); +} + +TEST(Compare, Conversions) { + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(strong_equality::equal) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(strong_equality::nonequal) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(strong_equality::equivalent) == 0)); + EXPECT_TRUE(Identity( + implicit_cast<weak_equality>(strong_equality::nonequivalent) != 0)); + + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(partial_ordering::less) != 0)); + EXPECT_TRUE(Identity( + implicit_cast<weak_equality>(partial_ordering::equivalent) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(partial_ordering::greater) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(partial_ordering::unordered) != 0)); + + EXPECT_TRUE(implicit_cast<weak_equality>(weak_ordering::less) != 0); + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(weak_ordering::equivalent) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(weak_ordering::greater) != 0)); + + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(weak_ordering::less) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(weak_ordering::less) < 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(weak_ordering::less) <= 0)); + EXPECT_TRUE(Identity( + implicit_cast<partial_ordering>(weak_ordering::equivalent) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(weak_ordering::greater) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(weak_ordering::greater) > 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(weak_ordering::greater) >= 0)); + + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(strong_ordering::less) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(strong_ordering::equal) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(strong_ordering::equivalent) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_equality>(strong_ordering::greater) != 0)); + + EXPECT_TRUE( + Identity(implicit_cast<strong_equality>(strong_ordering::less) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<strong_equality>(strong_ordering::equal) == 0)); + EXPECT_TRUE(Identity( + implicit_cast<strong_equality>(strong_ordering::equivalent) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<strong_equality>(strong_ordering::greater) != 0)); + + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(strong_ordering::less) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(strong_ordering::less) < 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(strong_ordering::less) <= 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(strong_ordering::equal) == 0)); + EXPECT_TRUE(Identity( + implicit_cast<partial_ordering>(strong_ordering::equivalent) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(strong_ordering::greater) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(strong_ordering::greater) > 0)); + EXPECT_TRUE( + Identity(implicit_cast<partial_ordering>(strong_ordering::greater) >= 0)); + + EXPECT_TRUE( + Identity(implicit_cast<weak_ordering>(strong_ordering::less) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_ordering>(strong_ordering::less) < 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_ordering>(strong_ordering::less) <= 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_ordering>(strong_ordering::equal) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_ordering>(strong_ordering::equivalent) == 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_ordering>(strong_ordering::greater) != 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_ordering>(strong_ordering::greater) > 0)); + EXPECT_TRUE( + Identity(implicit_cast<weak_ordering>(strong_ordering::greater) >= 0)); +} + +struct WeakOrderingLess { + template <typename T> + absl::weak_ordering operator()(const T &a, const T &b) const { + return a < b ? absl::weak_ordering::less + : a == b ? absl::weak_ordering::equivalent + : absl::weak_ordering::greater; + } +}; + +TEST(CompareResultAsLessThan, SanityTest) { + EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than(false)); + EXPECT_TRUE(absl::compare_internal::compare_result_as_less_than(true)); + + EXPECT_TRUE( + absl::compare_internal::compare_result_as_less_than(weak_ordering::less)); + EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than( + weak_ordering::equivalent)); + EXPECT_FALSE(absl::compare_internal::compare_result_as_less_than( + weak_ordering::greater)); +} + +TEST(DoLessThanComparison, SanityTest) { + std::less<int> less; + WeakOrderingLess weak; + + EXPECT_TRUE(absl::compare_internal::do_less_than_comparison(less, -1, 0)); + EXPECT_TRUE(absl::compare_internal::do_less_than_comparison(weak, -1, 0)); + + EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(less, 10, 10)); + EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(weak, 10, 10)); + + EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(less, 10, 5)); + EXPECT_FALSE(absl::compare_internal::do_less_than_comparison(weak, 10, 5)); +} + +TEST(CompareResultAsOrdering, SanityTest) { + EXPECT_TRUE(Identity( + absl::compare_internal::compare_result_as_ordering(-1) < 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::compare_result_as_ordering(-1) == 0)); + EXPECT_FALSE( + Identity(absl::compare_internal::compare_result_as_ordering(-1) > 0)); + EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering( + weak_ordering::less) < 0)); + EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( + weak_ordering::less) == 0)); + EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( + weak_ordering::less) > 0)); + + EXPECT_FALSE(Identity( + absl::compare_internal::compare_result_as_ordering(0) < 0)); + EXPECT_TRUE(Identity( + absl::compare_internal::compare_result_as_ordering(0) == 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::compare_result_as_ordering(0) > 0)); + EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( + weak_ordering::equivalent) < 0)); + EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering( + weak_ordering::equivalent) == 0)); + EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( + weak_ordering::equivalent) > 0)); + + EXPECT_FALSE(Identity( + absl::compare_internal::compare_result_as_ordering(1) < 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::compare_result_as_ordering(1) == 0)); + EXPECT_TRUE(Identity( + absl::compare_internal::compare_result_as_ordering(1) > 0)); + EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( + weak_ordering::greater) < 0)); + EXPECT_FALSE(Identity(absl::compare_internal::compare_result_as_ordering( + weak_ordering::greater) == 0)); + EXPECT_TRUE(Identity(absl::compare_internal::compare_result_as_ordering( + weak_ordering::greater) > 0)); +} + +TEST(DoThreeWayComparison, SanityTest) { + std::less<int> less; + WeakOrderingLess weak; + + EXPECT_TRUE(Identity( + absl::compare_internal::do_three_way_comparison(less, -1, 0) < 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(less, -1, 0) == 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(less, -1, 0) > 0)); + EXPECT_TRUE(Identity( + absl::compare_internal::do_three_way_comparison(weak, -1, 0) < 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(weak, -1, 0) == 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(weak, -1, 0) > 0)); + + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(less, 10, 10) < 0)); + EXPECT_TRUE(Identity( + absl::compare_internal::do_three_way_comparison(less, 10, 10) == 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(less, 10, 10) > 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(weak, 10, 10) < 0)); + EXPECT_TRUE(Identity( + absl::compare_internal::do_three_way_comparison(weak, 10, 10) == 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(weak, 10, 10) > 0)); + + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(less, 10, 5) < 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(less, 10, 5) == 0)); + EXPECT_TRUE(Identity( + absl::compare_internal::do_three_way_comparison(less, 10, 5) > 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(weak, 10, 5) < 0)); + EXPECT_FALSE(Identity( + absl::compare_internal::do_three_way_comparison(weak, 10, 5) == 0)); + EXPECT_TRUE(Identity( + absl::compare_internal::do_three_way_comparison(weak, 10, 5) > 0)); +} + +} // namespace +} // inline namespace lts_2019_08_08 +} // namespace absl diff --git a/absl/types/internal/optional.h b/absl/types/internal/optional.h new file mode 100644 index 00000000..3c8e7cca --- /dev/null +++ b/absl/types/internal/optional.h @@ -0,0 +1,396 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef ABSL_TYPES_INTERNAL_OPTIONAL_H_ +#define ABSL_TYPES_INTERNAL_OPTIONAL_H_ + +#include <functional> +#include <new> +#include <type_traits> +#include <utility> + +#include "absl/base/internal/inline_variable.h" +#include "absl/memory/memory.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" + +// ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS +// +// Inheriting constructors is supported in GCC 4.8+, Clang 3.3+ and MSVC 2015. +// __cpp_inheriting_constructors is a predefined macro and a recommended way to +// check for this language feature, but GCC doesn't support it until 5.0 and +// Clang doesn't support it until 3.6. +// Also, MSVC 2015 has a bug: it doesn't inherit the constexpr template +// constructor. For example, the following code won't work on MSVC 2015 Update3: +// struct Base { +// int t; +// template <typename T> +// constexpr Base(T t_) : t(t_) {} +// }; +// struct Foo : Base { +// using Base::Base; +// } +// constexpr Foo foo(0); // doesn't work on MSVC 2015 +#if defined(__clang__) +#if __has_feature(cxx_inheriting_constructors) +#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 +#endif +#elif (defined(__GNUC__) && \ + (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 8)) || \ + (__cpp_inheriting_constructors >= 200802) || \ + (defined(_MSC_VER) && _MSC_VER >= 1910) +#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 +#endif + +namespace absl { +inline namespace lts_2019_08_08 { + +// Forward declaration +template <typename T> +class optional; + +namespace optional_internal { + +// This tag type is used as a constructor parameter type for `nullopt_t`. +struct init_t { + explicit init_t() = default; +}; + +struct empty_struct {}; + +// This class stores the data in optional<T>. +// It is specialized based on whether T is trivially destructible. +// This is the specialization for non trivially destructible type. +template <typename T, bool unused = std::is_trivially_destructible<T>::value> +class optional_data_dtor_base { + struct dummy_type { + static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); + // Use an array to avoid GCC 6 placement-new warning. + empty_struct data[sizeof(T) / sizeof(empty_struct)]; + }; + + protected: + // Whether there is data or not. + bool engaged_; + // Data storage + union { + dummy_type dummy_; + T data_; + }; + + void destruct() noexcept { + if (engaged_) { + data_.~T(); + engaged_ = false; + } + } + + // dummy_ must be initialized for constexpr constructor. + constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} + + template <typename... Args> + constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) + : engaged_(true), data_(absl::forward<Args>(args)...) {} + + ~optional_data_dtor_base() { destruct(); } +}; + +// Specialization for trivially destructible type. +template <typename T> +class optional_data_dtor_base<T, true> { + struct dummy_type { + static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); + // Use array to avoid GCC 6 placement-new warning. + empty_struct data[sizeof(T) / sizeof(empty_struct)]; + }; + + protected: + // Whether there is data or not. + bool engaged_; + // Data storage + union { + dummy_type dummy_; + T data_; + }; + void destruct() noexcept { engaged_ = false; } + + // dummy_ must be initialized for constexpr constructor. + constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} + + template <typename... Args> + constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) + : engaged_(true), data_(absl::forward<Args>(args)...) {} +}; + +template <typename T> +class optional_data_base : public optional_data_dtor_base<T> { + protected: + using base = optional_data_dtor_base<T>; +#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS + using base::base; +#else + optional_data_base() = default; + + template <typename... Args> + constexpr explicit optional_data_base(in_place_t t, Args&&... args) + : base(t, absl::forward<Args>(args)...) {} +#endif + + template <typename... Args> + void construct(Args&&... args) { + // Use dummy_'s address to work around casting cv-qualified T* to void*. + ::new (static_cast<void*>(&this->dummy_)) T(std::forward<Args>(args)...); + this->engaged_ = true; + } + + template <typename U> + void assign(U&& u) { + if (this->engaged_) { + this->data_ = std::forward<U>(u); + } else { + construct(std::forward<U>(u)); + } + } +}; + +// TODO(absl-team): Add another class using +// std::is_trivially_move_constructible trait when available to match +// http://cplusplus.github.io/LWG/lwg-defects.html#2900, for types that +// have trivial move but nontrivial copy. +// Also, we should be checking is_trivially_copyable here, which is not +// supported now, so we use is_trivially_* traits instead. +template <typename T, + bool unused = absl::is_trivially_copy_constructible<T>::value&& + absl::is_trivially_copy_assignable<typename std::remove_cv< + T>::type>::value&& std::is_trivially_destructible<T>::value> +class optional_data; + +// Trivially copyable types +template <typename T> +class optional_data<T, true> : public optional_data_base<T> { + protected: +#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS + using optional_data_base<T>::optional_data_base; +#else + optional_data() = default; + + template <typename... Args> + constexpr explicit optional_data(in_place_t t, Args&&... args) + : optional_data_base<T>(t, absl::forward<Args>(args)...) {} +#endif +}; + +template <typename T> +class optional_data<T, false> : public optional_data_base<T> { + protected: +#ifdef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS + using optional_data_base<T>::optional_data_base; +#else + template <typename... Args> + constexpr explicit optional_data(in_place_t t, Args&&... args) + : optional_data_base<T>(t, absl::forward<Args>(args)...) {} +#endif + + optional_data() = default; + + optional_data(const optional_data& rhs) : optional_data_base<T>() { + if (rhs.engaged_) { + this->construct(rhs.data_); + } + } + + optional_data(optional_data&& rhs) noexcept( + absl::default_allocator_is_nothrow::value || + std::is_nothrow_move_constructible<T>::value) + : optional_data_base<T>() { + if (rhs.engaged_) { + this->construct(std::move(rhs.data_)); + } + } + + optional_data& operator=(const optional_data& rhs) { + if (rhs.engaged_) { + this->assign(rhs.data_); + } else { + this->destruct(); + } + return *this; + } + + optional_data& operator=(optional_data&& rhs) noexcept( + std::is_nothrow_move_assignable<T>::value&& + std::is_nothrow_move_constructible<T>::value) { + if (rhs.engaged_) { + this->assign(std::move(rhs.data_)); + } else { + this->destruct(); + } + return *this; + } +}; + +// Ordered by level of restriction, from low to high. +// Copyable implies movable. +enum class copy_traits { copyable = 0, movable = 1, non_movable = 2 }; + +// Base class for enabling/disabling copy/move constructor. +template <copy_traits> +class optional_ctor_base; + +template <> +class optional_ctor_base<copy_traits::copyable> { + public: + constexpr optional_ctor_base() = default; + optional_ctor_base(const optional_ctor_base&) = default; + optional_ctor_base(optional_ctor_base&&) = default; + optional_ctor_base& operator=(const optional_ctor_base&) = default; + optional_ctor_base& operator=(optional_ctor_base&&) = default; +}; + +template <> +class optional_ctor_base<copy_traits::movable> { + public: + constexpr optional_ctor_base() = default; + optional_ctor_base(const optional_ctor_base&) = delete; + optional_ctor_base(optional_ctor_base&&) = default; + optional_ctor_base& operator=(const optional_ctor_base&) = default; + optional_ctor_base& operator=(optional_ctor_base&&) = default; +}; + +template <> +class optional_ctor_base<copy_traits::non_movable> { + public: + constexpr optional_ctor_base() = default; + optional_ctor_base(const optional_ctor_base&) = delete; + optional_ctor_base(optional_ctor_base&&) = delete; + optional_ctor_base& operator=(const optional_ctor_base&) = default; + optional_ctor_base& operator=(optional_ctor_base&&) = default; +}; + +// Base class for enabling/disabling copy/move assignment. +template <copy_traits> +class optional_assign_base; + +template <> +class optional_assign_base<copy_traits::copyable> { + public: + constexpr optional_assign_base() = default; + optional_assign_base(const optional_assign_base&) = default; + optional_assign_base(optional_assign_base&&) = default; + optional_assign_base& operator=(const optional_assign_base&) = default; + optional_assign_base& operator=(optional_assign_base&&) = default; +}; + +template <> +class optional_assign_base<copy_traits::movable> { + public: + constexpr optional_assign_base() = default; + optional_assign_base(const optional_assign_base&) = default; + optional_assign_base(optional_assign_base&&) = default; + optional_assign_base& operator=(const optional_assign_base&) = delete; + optional_assign_base& operator=(optional_assign_base&&) = default; +}; + +template <> +class optional_assign_base<copy_traits::non_movable> { + public: + constexpr optional_assign_base() = default; + optional_assign_base(const optional_assign_base&) = default; + optional_assign_base(optional_assign_base&&) = default; + optional_assign_base& operator=(const optional_assign_base&) = delete; + optional_assign_base& operator=(optional_assign_base&&) = delete; +}; + +template <typename T> +struct ctor_copy_traits { + static constexpr copy_traits traits = + std::is_copy_constructible<T>::value + ? copy_traits::copyable + : std::is_move_constructible<T>::value ? copy_traits::movable + : copy_traits::non_movable; +}; + +template <typename T> +struct assign_copy_traits { + static constexpr copy_traits traits = + absl::is_copy_assignable<T>::value && std::is_copy_constructible<T>::value + ? copy_traits::copyable + : absl::is_move_assignable<T>::value && + std::is_move_constructible<T>::value + ? copy_traits::movable + : copy_traits::non_movable; +}; + +// Whether T is constructible or convertible from optional<U>. +template <typename T, typename U> +struct is_constructible_convertible_from_optional + : std::integral_constant< + bool, std::is_constructible<T, optional<U>&>::value || + std::is_constructible<T, optional<U>&&>::value || + std::is_constructible<T, const optional<U>&>::value || + std::is_constructible<T, const optional<U>&&>::value || + std::is_convertible<optional<U>&, T>::value || + std::is_convertible<optional<U>&&, T>::value || + std::is_convertible<const optional<U>&, T>::value || + std::is_convertible<const optional<U>&&, T>::value> {}; + +// Whether T is constructible or convertible or assignable from optional<U>. +template <typename T, typename U> +struct is_constructible_convertible_assignable_from_optional + : std::integral_constant< + bool, is_constructible_convertible_from_optional<T, U>::value || + std::is_assignable<T&, optional<U>&>::value || + std::is_assignable<T&, optional<U>&&>::value || + std::is_assignable<T&, const optional<U>&>::value || + std::is_assignable<T&, const optional<U>&&>::value> {}; + +// Helper function used by [optional.relops], [optional.comp_with_t], +// for checking whether an expression is convertible to bool. +bool convertible_to_bool(bool); + +// Base class for std::hash<absl::optional<T>>: +// If std::hash<std::remove_const_t<T>> is enabled, it provides operator() to +// compute the hash; Otherwise, it is disabled. +// Reference N4659 23.14.15 [unord.hash]. +template <typename T, typename = size_t> +struct optional_hash_base { + optional_hash_base() = delete; + optional_hash_base(const optional_hash_base&) = delete; + optional_hash_base(optional_hash_base&&) = delete; + optional_hash_base& operator=(const optional_hash_base&) = delete; + optional_hash_base& operator=(optional_hash_base&&) = delete; +}; + +template <typename T> +struct optional_hash_base<T, decltype(std::hash<absl::remove_const_t<T> >()( + std::declval<absl::remove_const_t<T> >()))> { + using argument_type = absl::optional<T>; + using result_type = size_t; + size_t operator()(const absl::optional<T>& opt) const { + absl::type_traits_internal::AssertHashEnabled<absl::remove_const_t<T>>(); + if (opt) { + return std::hash<absl::remove_const_t<T> >()(*opt); + } else { + return static_cast<size_t>(0x297814aaad196e6dULL); + } + } +}; + +} // namespace optional_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS + +#endif // ABSL_TYPES_INTERNAL_OPTIONAL_H_ diff --git a/absl/types/internal/span.h b/absl/types/internal/span.h new file mode 100644 index 00000000..873ae160 --- /dev/null +++ b/absl/types/internal/span.h @@ -0,0 +1,128 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef ABSL_TYPES_INTERNAL_SPAN_H_ +#define ABSL_TYPES_INTERNAL_SPAN_H_ + +#include <algorithm> +#include <cstddef> +#include <string> +#include <type_traits> + +#include "absl/algorithm/algorithm.h" +#include "absl/base/internal/throw_delegate.h" +#include "absl/meta/type_traits.h" + +namespace absl { +inline namespace lts_2019_08_08 { + +namespace span_internal { +// A constexpr min function +constexpr size_t Min(size_t a, size_t b) noexcept { return a < b ? a : b; } + +// Wrappers for access to container data pointers. +template <typename C> +constexpr auto GetDataImpl(C& c, char) noexcept // NOLINT(runtime/references) + -> decltype(c.data()) { + return c.data(); +} + +// Before C++17, std::string::data returns a const char* in all cases. +inline char* GetDataImpl(std::string& s, // NOLINT(runtime/references) + int) noexcept { + return &s[0]; +} + +template <typename C> +constexpr auto GetData(C& c) noexcept // NOLINT(runtime/references) + -> decltype(GetDataImpl(c, 0)) { + return GetDataImpl(c, 0); +} + +// Detection idioms for size() and data(). +template <typename C> +using HasSize = + std::is_integral<absl::decay_t<decltype(std::declval<C&>().size())>>; + +// We want to enable conversion from vector<T*> to Span<const T* const> but +// disable conversion from vector<Derived> to Span<Base>. Here we use +// the fact that U** is convertible to Q* const* if and only if Q is the same +// type or a more cv-qualified version of U. We also decay the result type of +// data() to avoid problems with classes which have a member function data() +// which returns a reference. +template <typename T, typename C> +using HasData = + std::is_convertible<absl::decay_t<decltype(GetData(std::declval<C&>()))>*, + T* const*>; + +// Extracts value type from a Container +template <typename C> +struct ElementType { + using type = typename absl::remove_reference_t<C>::value_type; +}; + +template <typename T, size_t N> +struct ElementType<T (&)[N]> { + using type = T; +}; + +template <typename C> +using ElementT = typename ElementType<C>::type; + +template <typename T> +using EnableIfMutable = + typename std::enable_if<!std::is_const<T>::value, int>::type; + +template <template <typename> class SpanT, typename T> +bool EqualImpl(SpanT<T> a, SpanT<T> b) { + static_assert(std::is_const<T>::value, ""); + return absl::equal(a.begin(), a.end(), b.begin(), b.end()); +} + +template <template <typename> class SpanT, typename T> +bool LessThanImpl(SpanT<T> a, SpanT<T> b) { + // We can't use value_type since that is remove_cv_t<T>, so we go the long way + // around. + static_assert(std::is_const<T>::value, ""); + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); +} + +// The `IsConvertible` classes here are needed because of the +// `std::is_convertible` bug in libcxx when compiled with GCC. This build +// configuration is used by Android NDK toolchain. Reference link: +// https://bugs.llvm.org/show_bug.cgi?id=27538. +template <typename From, typename To> +struct IsConvertibleHelper { + private: + static std::true_type testval(To); + static std::false_type testval(...); + + public: + using type = decltype(testval(std::declval<From>())); +}; + +template <typename From, typename To> +struct IsConvertible : IsConvertibleHelper<From, To>::type {}; + +// TODO(zhangxy): replace `IsConvertible` with `std::is_convertible` once the +// older version of libcxx is not supported. +template <typename From, typename To> +using EnableIfConvertibleTo = + typename std::enable_if<IsConvertible<From, To>::value>::type; +} // namespace span_internal +} // inline namespace lts_2019_08_08 +} // namespace absl + +#endif // ABSL_TYPES_INTERNAL_SPAN_H_ diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h index 875f88e0..4f29f617 100644 --- a/absl/types/internal/variant.h +++ b/absl/types/internal/variant.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,7 +15,6 @@ // Implementation details of absl/types/variant.h, pulled into a // separate file to avoid cluttering the top of the API header with // implementation details. -// #ifndef ABSL_TYPES_variant_internal_H_ #define ABSL_TYPES_variant_internal_H_ @@ -41,7 +40,7 @@ #if !defined(ABSL_HAVE_STD_VARIANT) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { template <class... Types> class variant; @@ -206,7 +205,7 @@ template <class Op, class... Vs> using VisitIndicesResultT = typename VisitIndicesResultImpl<Op, Vs...>::type; template <class ReturnType, class FunctionObject, class EndIndices, - std::size_t... BoundIndices> + class BoundIndices> struct MakeVisitationMatrix; template <class ReturnType, class FunctionObject, std::size_t... Indices> @@ -220,7 +219,7 @@ constexpr ReturnType call_with_indices(FunctionObject&& function) { template <class ReturnType, class FunctionObject, std::size_t... BoundIndices> struct MakeVisitationMatrix<ReturnType, FunctionObject, index_sequence<>, - BoundIndices...> { + index_sequence<BoundIndices...>> { using ResultType = ReturnType (*)(FunctionObject&&); static constexpr ResultType Run() { return &call_with_indices<ReturnType, FunctionObject, @@ -228,24 +227,34 @@ struct MakeVisitationMatrix<ReturnType, FunctionObject, index_sequence<>, } }; +template <typename Is, std::size_t J> +struct AppendToIndexSequence; + +template <typename Is, std::size_t J> +using AppendToIndexSequenceT = typename AppendToIndexSequence<Is, J>::type; + +template <std::size_t... Is, std::size_t J> +struct AppendToIndexSequence<index_sequence<Is...>, J> { + using type = index_sequence<Is..., J>; +}; + template <class ReturnType, class FunctionObject, class EndIndices, - class CurrIndices, std::size_t... BoundIndices> + class CurrIndices, class BoundIndices> struct MakeVisitationMatrixImpl; -template <class ReturnType, class FunctionObject, std::size_t... EndIndices, - std::size_t... CurrIndices, std::size_t... BoundIndices> -struct MakeVisitationMatrixImpl< - ReturnType, FunctionObject, index_sequence<EndIndices...>, - index_sequence<CurrIndices...>, BoundIndices...> { +template <class ReturnType, class FunctionObject, class EndIndices, + std::size_t... CurrIndices, class BoundIndices> +struct MakeVisitationMatrixImpl<ReturnType, FunctionObject, EndIndices, + index_sequence<CurrIndices...>, BoundIndices> { using ResultType = SimpleArray< - typename MakeVisitationMatrix<ReturnType, FunctionObject, - index_sequence<EndIndices...>>::ResultType, + typename MakeVisitationMatrix<ReturnType, FunctionObject, EndIndices, + index_sequence<>>::ResultType, sizeof...(CurrIndices)>; static constexpr ResultType Run() { - return {{MakeVisitationMatrix<ReturnType, FunctionObject, - index_sequence<EndIndices...>, - BoundIndices..., CurrIndices>::Run()...}}; + return {{MakeVisitationMatrix< + ReturnType, FunctionObject, EndIndices, + AppendToIndexSequenceT<BoundIndices, CurrIndices>>::Run()...}}; } }; @@ -253,10 +262,11 @@ template <class ReturnType, class FunctionObject, std::size_t HeadEndIndex, std::size_t... TailEndIndices, std::size_t... BoundIndices> struct MakeVisitationMatrix<ReturnType, FunctionObject, index_sequence<HeadEndIndex, TailEndIndices...>, - BoundIndices...> - : MakeVisitationMatrixImpl< - ReturnType, FunctionObject, index_sequence<TailEndIndices...>, - absl::make_index_sequence<HeadEndIndex>, BoundIndices...> {}; + index_sequence<BoundIndices...>> + : MakeVisitationMatrixImpl<ReturnType, FunctionObject, + index_sequence<TailEndIndices...>, + absl::make_index_sequence<HeadEndIndex>, + index_sequence<BoundIndices...>> {}; struct UnreachableSwitchCase { template <class Op> @@ -425,7 +435,8 @@ struct VisitIndicesFallback { static VisitIndicesResultT<Op, SizeT...> Run(Op&& op, SizeT... indices) { return AccessSimpleArray( MakeVisitationMatrix<VisitIndicesResultT<Op, SizeT...>, Op, - index_sequence<(EndIndices + 1)...>>::Run(), + index_sequence<(EndIndices + 1)...>, + index_sequence<>>::Run(), (indices + 1)...)(absl::forward<Op>(op)); } }; @@ -839,8 +850,8 @@ struct ImaginaryFun<variant<H, T...>, I> : ImaginaryFun<variant<T...>, I + 1> { // NOTE: const& and && are used instead of by-value due to lack of guaranteed // move elision of C++17. This may have other minor differences, but tests // pass. - static SizeT<I> Run(const H&); - static SizeT<I> Run(H&&); + static SizeT<I> Run(const H&, SizeT<I>); + static SizeT<I> Run(H&&, SizeT<I>); }; // The following metafunctions are used in constructor and assignment @@ -862,7 +873,8 @@ struct ConversionIsPossibleImpl : std::false_type {}; template <class Variant, class T> struct ConversionIsPossibleImpl< - Variant, T, void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>()))>> + Variant, T, + void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {}))>> : std::true_type {}; template <class Variant, class T> @@ -870,8 +882,9 @@ struct ConversionIsPossible : ConversionIsPossibleImpl<Variant, T>::type {}; template <class Variant, class T> struct IndexOfConstructedType< - Variant, T, void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>()))>> - : decltype(ImaginaryFun<Variant>::Run(std::declval<T>())) {}; + Variant, T, + void_t<decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {}))>> + : decltype(ImaginaryFun<Variant>::Run(std::declval<T>(), {})) {}; template <std::size_t... Is> struct ContainsVariantNPos @@ -1550,8 +1563,8 @@ struct SwapSameIndex { variant<Types...>* w; template <std::size_t I> void operator()(SizeT<I>) const { - using std::swap; - swap(VariantCoreAccess::Access<I>(*v), VariantCoreAccess::Access<I>(*w)); + type_traits_internal::Swap(VariantCoreAccess::Access<I>(*v), + VariantCoreAccess::Access<I>(*w)); } void operator()(SizeT<variant_npos>) const {} @@ -1606,11 +1619,12 @@ struct VariantHashVisitor { template <typename Variant, typename... Ts> struct VariantHashBase<Variant, absl::enable_if_t<absl::conjunction< - type_traits_internal::IsHashEnabled<Ts>...>::value>, + type_traits_internal::IsHashable<Ts>...>::value>, Ts...> { using argument_type = Variant; using result_type = size_t; size_t operator()(const Variant& var) const { + type_traits_internal::AssertHashEnabled<Ts...>(); if (var.valueless_by_exception()) { return 239799884; } @@ -1625,7 +1639,7 @@ struct VariantHashBase<Variant, }; } // namespace variant_internal -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // !defined(ABSL_HAVE_STD_VARIANT) diff --git a/absl/types/optional.h b/absl/types/optional.h index 1ca8dec6..6614d7bd 100644 --- a/absl/types/optional.h +++ b/absl/types/optional.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -35,21 +35,21 @@ #ifndef ABSL_TYPES_OPTIONAL_H_ #define ABSL_TYPES_OPTIONAL_H_ -#include "absl/base/config.h" +#include "absl/base/config.h" // TODO(calabrese) IWYU removal? #include "absl/utility/utility.h" #ifdef ABSL_HAVE_STD_OPTIONAL -#include <optional> +#include <optional> // IWYU pragma: export namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { using std::bad_optional_access; using std::optional; using std::make_optional; using std::nullopt_t; using std::nullopt; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else // ABSL_HAVE_STD_OPTIONAL @@ -57,45 +57,33 @@ using std::nullopt; #include <cassert> #include <functional> #include <initializer_list> -#include <new> #include <type_traits> #include <utility> #include "absl/base/attributes.h" -#include "absl/memory/memory.h" +#include "absl/base/internal/inline_variable.h" #include "absl/meta/type_traits.h" #include "absl/types/bad_optional_access.h" +#include "absl/types/internal/optional.h" -// ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS +namespace absl { +inline namespace lts_2019_08_08 { + +// nullopt_t // -// Inheriting constructors is supported in GCC 4.8+, Clang 3.3+ and MSVC 2015. -// __cpp_inheriting_constructors is a predefined macro and a recommended way to -// check for this language feature, but GCC doesn't support it until 5.0 and -// Clang doesn't support it until 3.6. -// Also, MSVC 2015 has a bug: it doesn't inherit the constexpr template -// constructor. For example, the following code won't work on MSVC 2015 Update3: -// struct Base { -// int t; -// template <typename T> -// constexpr Base(T t_) : t(t_) {} -// }; -// struct Foo : Base { -// using Base::Base; -// } -// constexpr Foo foo(0); // doesn't work on MSVC 2015 -#if defined(__clang__) -#if __has_feature(cxx_inheriting_constructors) -#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 -#endif -#elif (defined(__GNUC__) && \ - (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 8)) || \ - (__cpp_inheriting_constructors >= 200802) || \ - (defined(_MSC_VER) && _MSC_VER >= 1910) -#define ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 -#endif +// Class type for `absl::nullopt` used to indicate an `absl::optional<T>` type +// that does not contain a value. +struct nullopt_t { + // It must not be default-constructible to avoid ambiguity for opt = {}. + explicit constexpr nullopt_t(optional_internal::init_t) noexcept {} +}; -namespace absl { -inline namespace lts_2018_12_18 { +// nullopt +// +// A tag constant of type `absl::nullopt_t` used to indicate an empty +// `absl::optional` in certain functions, such as construction or assignment. +ABSL_INTERNAL_INLINE_CONSTEXPR(nullopt_t, nullopt, + nullopt_t(optional_internal::init_t())); // ----------------------------------------------------------------------------- // absl::optional @@ -117,10 +105,6 @@ inline namespace lts_2018_12_18 { // need the inline variable support in C++17 for external linkage. // * Throws `absl::bad_optional_access` instead of // `std::bad_optional_access`. -// * `optional::swap()` and `absl::swap()` relies on -// `std::is_(nothrow_)swappable()`, which has been introduced in C++17. -// As a workaround, we assume `is_swappable()` is always `true` -// and `is_nothrow_swappable()` is the same as `std::is_trivial()`. // * `make_optional()` cannot be declared `constexpr` due to the absence of // guaranteed copy elision. // * The move constructor's `noexcept` specification is stronger, i.e. if the @@ -130,366 +114,13 @@ inline namespace lts_2018_12_18 { // a) move constructors should only throw due to allocation failure and // b) if T's move constructor allocates, it uses the same allocation // function as the default allocator. -template <typename T> -class optional; - -// nullopt_t -// -// Class type for `absl::nullopt` used to indicate an `absl::optional<T>` type -// that does not contain a value. -struct nullopt_t { - struct init_t {}; - static init_t init; - - // It must not be default-constructible to avoid ambiguity for opt = {}. - // Note the non-const reference, which is to eliminate ambiguity for code - // like: - // - // struct S { int value; }; - // - // void Test() { - // optional<S> opt; - // opt = {{}}; - // } - explicit constexpr nullopt_t(init_t& /*unused*/) {} -}; - -// nullopt // -// A tag constant of type `absl::nullopt_t` used to indicate an empty -// `absl::optional` in certain functions, such as construction or assignment. -extern const nullopt_t nullopt; - -namespace optional_internal { - -struct empty_struct {}; -// This class stores the data in optional<T>. -// It is specialized based on whether T is trivially destructible. -// This is the specialization for non trivially destructible type. -template <typename T, bool unused = std::is_trivially_destructible<T>::value> -class optional_data_dtor_base { - struct dummy_type { - static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); - // Use an array to avoid GCC 6 placement-new warning. - empty_struct data[sizeof(T) / sizeof(empty_struct)]; - }; - - protected: - // Whether there is data or not. - bool engaged_; - // Data storage - union { - dummy_type dummy_; - T data_; - }; - - void destruct() noexcept { - if (engaged_) { - data_.~T(); - engaged_ = false; - } - } - - // dummy_ must be initialized for constexpr constructor. - constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} - - template <typename... Args> - constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) - : engaged_(true), data_(absl::forward<Args>(args)...) {} - - ~optional_data_dtor_base() { destruct(); } -}; - -// Specialization for trivially destructible type. -template <typename T> -class optional_data_dtor_base<T, true> { - struct dummy_type { - static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); - // Use array to avoid GCC 6 placement-new warning. - empty_struct data[sizeof(T) / sizeof(empty_struct)]; - }; - - protected: - // Whether there is data or not. - bool engaged_; - // Data storage - union { - dummy_type dummy_; - T data_; - }; - void destruct() noexcept { engaged_ = false; } - - // dummy_ must be initialized for constexpr constructor. - constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} - - template <typename... Args> - constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) - : engaged_(true), data_(absl::forward<Args>(args)...) {} -}; - -template <typename T> -class optional_data_base : public optional_data_dtor_base<T> { - protected: - using base = optional_data_dtor_base<T>; -#if ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS - using base::base; -#else - optional_data_base() = default; - - template <typename... Args> - constexpr explicit optional_data_base(in_place_t t, Args&&... args) - : base(t, absl::forward<Args>(args)...) {} -#endif - - template <typename... Args> - void construct(Args&&... args) { - // Use dummy_'s address to work around casting cv-qualified T* to void*. - ::new (static_cast<void*>(&this->dummy_)) T(std::forward<Args>(args)...); - this->engaged_ = true; - } - - template <typename U> - void assign(U&& u) { - if (this->engaged_) { - this->data_ = std::forward<U>(u); - } else { - construct(std::forward<U>(u)); - } - } -}; - -// TODO(absl-team): Add another class using -// std::is_trivially_move_constructible trait when available to match -// http://cplusplus.github.io/LWG/lwg-defects.html#2900, for types that -// have trivial move but nontrivial copy. -// Also, we should be checking is_trivially_copyable here, which is not -// supported now, so we use is_trivially_* traits instead. -template <typename T, - bool unused = absl::is_trivially_copy_constructible<T>::value&& - absl::is_trivially_copy_assignable<typename std::remove_cv< - T>::type>::value&& std::is_trivially_destructible<T>::value> -class optional_data; - -// Trivially copyable types -template <typename T> -class optional_data<T, true> : public optional_data_base<T> { - protected: -#if ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS - using optional_data_base<T>::optional_data_base; -#else - optional_data() = default; - - template <typename... Args> - constexpr explicit optional_data(in_place_t t, Args&&... args) - : optional_data_base<T>(t, absl::forward<Args>(args)...) {} -#endif -}; - -template <typename T> -class optional_data<T, false> : public optional_data_base<T> { - protected: -#if ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS - using optional_data_base<T>::optional_data_base; -#else - template <typename... Args> - constexpr explicit optional_data(in_place_t t, Args&&... args) - : optional_data_base<T>(t, absl::forward<Args>(args)...) {} -#endif - - optional_data() = default; - - optional_data(const optional_data& rhs) { - if (rhs.engaged_) { - this->construct(rhs.data_); - } - } - - optional_data(optional_data&& rhs) noexcept( - absl::default_allocator_is_nothrow::value || - std::is_nothrow_move_constructible<T>::value) { - if (rhs.engaged_) { - this->construct(std::move(rhs.data_)); - } - } - - optional_data& operator=(const optional_data& rhs) { - if (rhs.engaged_) { - this->assign(rhs.data_); - } else { - this->destruct(); - } - return *this; - } - - optional_data& operator=(optional_data&& rhs) noexcept( - std::is_nothrow_move_assignable<T>::value&& - std::is_nothrow_move_constructible<T>::value) { - if (rhs.engaged_) { - this->assign(std::move(rhs.data_)); - } else { - this->destruct(); - } - return *this; - } -}; - -// Ordered by level of restriction, from low to high. -// Copyable implies movable. -enum class copy_traits { copyable = 0, movable = 1, non_movable = 2 }; - -// Base class for enabling/disabling copy/move constructor. -template <copy_traits> -class optional_ctor_base; - -template <> -class optional_ctor_base<copy_traits::copyable> { - public: - constexpr optional_ctor_base() = default; - optional_ctor_base(const optional_ctor_base&) = default; - optional_ctor_base(optional_ctor_base&&) = default; - optional_ctor_base& operator=(const optional_ctor_base&) = default; - optional_ctor_base& operator=(optional_ctor_base&&) = default; -}; - -template <> -class optional_ctor_base<copy_traits::movable> { - public: - constexpr optional_ctor_base() = default; - optional_ctor_base(const optional_ctor_base&) = delete; - optional_ctor_base(optional_ctor_base&&) = default; - optional_ctor_base& operator=(const optional_ctor_base&) = default; - optional_ctor_base& operator=(optional_ctor_base&&) = default; -}; - -template <> -class optional_ctor_base<copy_traits::non_movable> { - public: - constexpr optional_ctor_base() = default; - optional_ctor_base(const optional_ctor_base&) = delete; - optional_ctor_base(optional_ctor_base&&) = delete; - optional_ctor_base& operator=(const optional_ctor_base&) = default; - optional_ctor_base& operator=(optional_ctor_base&&) = default; -}; - -// Base class for enabling/disabling copy/move assignment. -template <copy_traits> -class optional_assign_base; - -template <> -class optional_assign_base<copy_traits::copyable> { - public: - constexpr optional_assign_base() = default; - optional_assign_base(const optional_assign_base&) = default; - optional_assign_base(optional_assign_base&&) = default; - optional_assign_base& operator=(const optional_assign_base&) = default; - optional_assign_base& operator=(optional_assign_base&&) = default; -}; - -template <> -class optional_assign_base<copy_traits::movable> { - public: - constexpr optional_assign_base() = default; - optional_assign_base(const optional_assign_base&) = default; - optional_assign_base(optional_assign_base&&) = default; - optional_assign_base& operator=(const optional_assign_base&) = delete; - optional_assign_base& operator=(optional_assign_base&&) = default; -}; - -template <> -class optional_assign_base<copy_traits::non_movable> { - public: - constexpr optional_assign_base() = default; - optional_assign_base(const optional_assign_base&) = default; - optional_assign_base(optional_assign_base&&) = default; - optional_assign_base& operator=(const optional_assign_base&) = delete; - optional_assign_base& operator=(optional_assign_base&&) = delete; -}; - -template <typename T> -constexpr copy_traits get_ctor_copy_traits() { - return std::is_copy_constructible<T>::value - ? copy_traits::copyable - : std::is_move_constructible<T>::value ? copy_traits::movable - : copy_traits::non_movable; -} - -template <typename T> -constexpr copy_traits get_assign_copy_traits() { - return absl::is_copy_assignable<T>::value && - std::is_copy_constructible<T>::value - ? copy_traits::copyable - : absl::is_move_assignable<T>::value && - std::is_move_constructible<T>::value - ? copy_traits::movable - : copy_traits::non_movable; -} - -// Whether T is constructible or convertible from optional<U>. -template <typename T, typename U> -struct is_constructible_convertible_from_optional - : std::integral_constant< - bool, std::is_constructible<T, optional<U>&>::value || - std::is_constructible<T, optional<U>&&>::value || - std::is_constructible<T, const optional<U>&>::value || - std::is_constructible<T, const optional<U>&&>::value || - std::is_convertible<optional<U>&, T>::value || - std::is_convertible<optional<U>&&, T>::value || - std::is_convertible<const optional<U>&, T>::value || - std::is_convertible<const optional<U>&&, T>::value> {}; - -// Whether T is constructible or convertible or assignable from optional<U>. -template <typename T, typename U> -struct is_constructible_convertible_assignable_from_optional - : std::integral_constant< - bool, is_constructible_convertible_from_optional<T, U>::value || - std::is_assignable<T&, optional<U>&>::value || - std::is_assignable<T&, optional<U>&&>::value || - std::is_assignable<T&, const optional<U>&>::value || - std::is_assignable<T&, const optional<U>&&>::value> {}; - -// Helper function used by [optional.relops], [optional.comp_with_t], -// for checking whether an expression is convertible to bool. -bool convertible_to_bool(bool); - -// Base class for std::hash<absl::optional<T>>: -// If std::hash<std::remove_const_t<T>> is enabled, it provides operator() to -// compute the hash; Otherwise, it is disabled. -// Reference N4659 23.14.15 [unord.hash]. -template <typename T, typename = size_t> -struct optional_hash_base { - optional_hash_base() = delete; - optional_hash_base(const optional_hash_base&) = delete; - optional_hash_base(optional_hash_base&&) = delete; - optional_hash_base& operator=(const optional_hash_base&) = delete; - optional_hash_base& operator=(optional_hash_base&&) = delete; -}; - -template <typename T> -struct optional_hash_base<T, decltype(std::hash<absl::remove_const_t<T> >()( - std::declval<absl::remove_const_t<T> >()))> { - using argument_type = absl::optional<T>; - using result_type = size_t; - size_t operator()(const absl::optional<T>& opt) const { - if (opt) { - return std::hash<absl::remove_const_t<T> >()(*opt); - } else { - return static_cast<size_t>(0x297814aaad196e6dULL); - } - } -}; - -} // namespace optional_internal - -// ----------------------------------------------------------------------------- -// absl::optional class definition -// ----------------------------------------------------------------------------- - template <typename T> class optional : private optional_internal::optional_data<T>, private optional_internal::optional_ctor_base< - optional_internal::get_ctor_copy_traits<T>()>, + optional_internal::ctor_copy_traits<T>::traits>, private optional_internal::optional_assign_base< - optional_internal::get_assign_copy_traits<T>()> { + optional_internal::assign_copy_traits<T>::traits> { using data_base = optional_internal::optional_data<T>; public: @@ -514,10 +145,11 @@ class optional : private optional_internal::optional_data<T>, // the arguments `std::forward<Args>(args)...` within the `optional`. // (The `in_place_t` is a tag used to indicate that the contained object // should be constructed in-place.) - // - // TODO(absl-team): Add std::is_constructible<T, Args&&...> SFINAE. - template <typename... Args> - constexpr explicit optional(in_place_t, Args&&... args) + template <typename InPlaceT, typename... Args, + absl::enable_if_t<absl::conjunction< + std::is_same<InPlaceT, in_place_t>, + std::is_constructible<T, Args&&...> >::value>* = nullptr> + constexpr explicit optional(InPlaceT, Args&&... args) : data_base(in_place_t(), absl::forward<Args>(args)...) {} // Constructs a non-empty `optional` direct-initialized value of type `T` from @@ -753,11 +385,10 @@ class optional : private optional_internal::optional_data<T>, // Swap, standard semantics void swap(optional& rhs) noexcept( std::is_nothrow_move_constructible<T>::value&& - std::is_trivial<T>::value) { + type_traits_internal::IsNothrowSwappable<T>::value) { if (*this) { if (rhs) { - using std::swap; - swap(**this, *rhs); + type_traits_internal::Swap(**this, *rhs); } else { rhs.construct(std::move(**this)); this->destruct(); @@ -793,7 +424,9 @@ class optional : private optional_internal::optional_data<T>, // // Accesses the underlying `T` value of an `optional`. If the `optional` is // empty, behavior is undefined. - constexpr const T& operator*() const & { return reference(); } + constexpr const T& operator*() const& { + return ABSL_ASSERT(this->engaged_), reference(); + } T& operator*() & { assert(this->engaged_); return reference(); @@ -869,7 +502,7 @@ class optional : private optional_internal::optional_data<T>, template <typename U> constexpr T value_or(U&& v) const& { static_assert(std::is_copy_constructible<value_type>::value, - "optional<T>::value_or: T must by copy constructible"); + "optional<T>::value_or: T must be copy constructible"); static_assert(std::is_convertible<U&&, value_type>::value, "optional<T>::value_or: U must be convertible to T"); return static_cast<bool>(*this) @@ -879,7 +512,7 @@ class optional : private optional_internal::optional_data<T>, template <typename U> T value_or(U&& v) && { // NOLINT(build/c++11) static_assert(std::is_move_constructible<value_type>::value, - "optional<T>::value_or: T must by copy constructible"); + "optional<T>::value_or: T must be move constructible"); static_assert(std::is_convertible<U&&, value_type>::value, "optional<T>::value_or: U must be convertible to T"); return static_cast<bool>(*this) ? std::move(**this) @@ -909,12 +542,10 @@ class optional : private optional_internal::optional_data<T>, // // Performs a swap between two `absl::optional` objects, using standard // semantics. -// -// NOTE: we assume `is_swappable()` is always `true`. A compile error will -// result if this is not the case. -template <typename T, - typename std::enable_if<std::is_move_constructible<T>::value, - bool>::type = false> +template <typename T, typename std::enable_if< + std::is_move_constructible<T>::value && + type_traits_internal::IsSwappable<T>::value, + bool>::type = false> void swap(optional<T>& a, optional<T>& b) noexcept(noexcept(a.swap(b))) { a.swap(b); } @@ -1126,7 +757,7 @@ constexpr auto operator>=(const U& v, const optional<T>& x) return static_cast<bool>(x) ? static_cast<bool>(v >= *x) : true; } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl namespace std { @@ -1138,7 +769,6 @@ struct hash<absl::optional<T> > } // namespace std -#undef ABSL_OPTIONAL_USE_INHERITING_CONSTRUCTORS #undef ABSL_MSVC_CONSTEXPR_BUG_IN_UNION_LIKE_CLASS #endif // ABSL_HAVE_STD_OPTIONAL diff --git a/absl/types/optional_exception_safety_test.cc b/absl/types/optional_exception_safety_test.cc index 313891f7..056ced42 100644 --- a/absl/types/optional_exception_safety_test.cc +++ b/absl/types/optional_exception_safety_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,7 +18,7 @@ #include "absl/base/internal/exception_safety_testing.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { @@ -280,5 +280,5 @@ TEST(OptionalExceptionSafety, NothrowMoveAssign) { } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc index fc4f00a4..e6a36eb8 100644 --- a/absl/types/optional_test.cc +++ b/absl/types/optional_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -157,6 +157,16 @@ struct NonMovable { NonMovable& operator=(NonMovable&&) = delete; }; +struct NoDefault { + NoDefault() = delete; + NoDefault(const NoDefault&) {} + NoDefault& operator=(const NoDefault&) { return *this; } +}; + +struct ConvertsFromInPlaceT { + ConvertsFromInPlaceT(absl::in_place_t) {} // NOLINT +}; + TEST(optionalTest, DefaultConstructor) { absl::optional<int> empty; EXPECT_FALSE(empty); @@ -169,15 +179,7 @@ TEST(optionalTest, DefaultConstructor) { TEST(optionalTest, nulloptConstructor) { absl::optional<int> empty(absl::nullopt); EXPECT_FALSE(empty); - -#ifdef ABSL_HAVE_STD_OPTIONAL constexpr absl::optional<int> cempty{absl::nullopt}; -#else - // Creating a temporary absl::nullopt_t object instead of using absl::nullopt - // because absl::nullopt cannot be constexpr and have external linkage at the - // same time. - constexpr absl::optional<int> cempty{absl::nullopt_t(absl::nullopt_t::init)}; -#endif static_assert(!cempty.has_value(), ""); EXPECT_TRUE((std::is_nothrow_constructible<absl::optional<int>, absl::nullopt_t>::value)); @@ -337,16 +339,18 @@ TEST(optionalTest, InPlaceConstructor) { static_assert((*opt2).x == ConstexprType::kCtorInitializerList, ""); #endif - // TODO(absl-team): uncomment these when std::is_constructible<T, Args&&...> - // SFINAE is added to optional::optional(absl::in_place_t, Args&&...). - // struct I { - // I(absl::in_place_t); - // }; + EXPECT_FALSE((std::is_constructible<absl::optional<ConvertsFromInPlaceT>, + absl::in_place_t>::value)); + EXPECT_FALSE((std::is_constructible<absl::optional<ConvertsFromInPlaceT>, + const absl::in_place_t&>::value)); + EXPECT_TRUE( + (std::is_constructible<absl::optional<ConvertsFromInPlaceT>, + absl::in_place_t, absl::in_place_t>::value)); - // EXPECT_FALSE((std::is_constructible<absl::optional<I>, - // absl::in_place_t>::value)); - // EXPECT_FALSE((std::is_constructible<absl::optional<I>, const - // absl::in_place_t&>::value)); + EXPECT_FALSE((std::is_constructible<absl::optional<NoDefault>, + absl::in_place_t>::value)); + EXPECT_FALSE((std::is_constructible<absl::optional<NoDefault>, + absl::in_place_t&&>::value)); } // template<U=T> optional(U&&); @@ -1476,8 +1480,8 @@ TEST(optionalTest, MoveAssignRegression) { TEST(optionalTest, ValueType) { EXPECT_TRUE((std::is_same<absl::optional<int>::value_type, int>::value)); - EXPECT_TRUE( - (std::is_same<absl::optional<std::string>::value_type, std::string>::value)); + EXPECT_TRUE((std::is_same<absl::optional<std::string>::value_type, + std::string>::value)); EXPECT_FALSE( (std::is_same<absl::optional<int>::value_type, absl::nullopt_t>::value)); } @@ -1504,18 +1508,19 @@ TEST(optionalTest, Hash) { static_assert(is_hash_enabled_for<absl::optional<int>>::value, ""); static_assert(is_hash_enabled_for<absl::optional<Hashable>>::value, ""); + static_assert( + absl::type_traits_internal::IsHashable<absl::optional<int>>::value, ""); + static_assert( + absl::type_traits_internal::IsHashable<absl::optional<Hashable>>::value, + ""); + absl::type_traits_internal::AssertHashEnabled<absl::optional<int>>(); + absl::type_traits_internal::AssertHashEnabled<absl::optional<Hashable>>(); -#if defined(_MSC_VER) || (defined(_LIBCPP_VERSION) && \ - _LIBCPP_VERSION < 4000 && _LIBCPP_STD_VER > 11) - // For MSVC and libc++ (< 4.0 and c++14), std::hash primary template has a - // static_assert to catch any user-defined type that doesn't provide a hash - // specialization. So instantiating std::hash<absl::optional<T>> will result - // in a hard error which is not SFINAE friendly. -#define ABSL_STD_HASH_NOT_SFINAE_FRIENDLY 1 -#endif - -#ifndef ABSL_STD_HASH_NOT_SFINAE_FRIENDLY +#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ static_assert(!is_hash_enabled_for<absl::optional<NonHashable>>::value, ""); + static_assert(!absl::type_traits_internal::IsHashable< + absl::optional<NonHashable>>::value, + ""); #endif // libstdc++ std::optional is missing remove_const_t, i.e. it's using @@ -1623,4 +1628,29 @@ TEST(optionalTest, AssignmentConstraints) { EXPECT_TRUE(absl::is_copy_assignable<absl::optional<AnyLike>>::value); } +#if !defined(__EMSCRIPTEN__) +struct NestedClassBug { + struct Inner { + bool dummy = false; + }; + absl::optional<Inner> value; +}; + +TEST(optionalTest, InPlaceTSFINAEBug) { + NestedClassBug b; + ((void)b); + using Inner = NestedClassBug::Inner; + + EXPECT_TRUE((std::is_default_constructible<Inner>::value)); + EXPECT_TRUE((std::is_constructible<Inner>::value)); + EXPECT_TRUE( + (std::is_constructible<absl::optional<Inner>, absl::in_place_t>::value)); + + absl::optional<Inner> o(absl::in_place); + EXPECT_TRUE(o.has_value()); + o.emplace(); + EXPECT_TRUE(o.has_value()); +} +#endif // !defined(__EMSCRIPTEN__) + } // namespace diff --git a/absl/types/span.h b/absl/types/span.h index 99b6765b..98c6cdc8 100644 --- a/absl/types/span.h +++ b/absl/types/span.h @@ -5,7 +5,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -60,115 +60,18 @@ #include <cstddef> #include <initializer_list> #include <iterator> -#include <string> #include <type_traits> #include <utility> -#include "absl/algorithm/algorithm.h" #include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" #include "absl/base/optimization.h" -#include "absl/base/port.h" +#include "absl/base/port.h" // TODO(strel): remove this include #include "absl/meta/type_traits.h" +#include "absl/types/internal/span.h" namespace absl { -inline namespace lts_2018_12_18 { - -template <typename T> -class Span; - -namespace span_internal { -// A constexpr min function -constexpr size_t Min(size_t a, size_t b) noexcept { return a < b ? a : b; } - -// Wrappers for access to container data pointers. -template <typename C> -constexpr auto GetDataImpl(C& c, char) noexcept // NOLINT(runtime/references) - -> decltype(c.data()) { - return c.data(); -} - -// Before C++17, string::data returns a const char* in all cases. -inline char* GetDataImpl(std::string& s, // NOLINT(runtime/references) - int) noexcept { - return &s[0]; -} - -template <typename C> -constexpr auto GetData(C& c) noexcept // NOLINT(runtime/references) - -> decltype(GetDataImpl(c, 0)) { - return GetDataImpl(c, 0); -} - -// Detection idioms for size() and data(). -template <typename C> -using HasSize = - std::is_integral<absl::decay_t<decltype(std::declval<C&>().size())>>; - -// We want to enable conversion from vector<T*> to Span<const T* const> but -// disable conversion from vector<Derived> to Span<Base>. Here we use -// the fact that U** is convertible to Q* const* if and only if Q is the same -// type or a more cv-qualified version of U. We also decay the result type of -// data() to avoid problems with classes which have a member function data() -// which returns a reference. -template <typename T, typename C> -using HasData = - std::is_convertible<absl::decay_t<decltype(GetData(std::declval<C&>()))>*, - T* const*>; - -// Extracts value type from a Container -template <typename C> -struct ElementType { - using type = typename absl::remove_reference_t<C>::value_type; -}; - -template <typename T, size_t N> -struct ElementType<T (&)[N]> { - using type = T; -}; - -template <typename C> -using ElementT = typename ElementType<C>::type; - -template <typename T> -using EnableIfMutable = - typename std::enable_if<!std::is_const<T>::value, int>::type; - -template <typename T> -bool EqualImpl(Span<T> a, Span<T> b) { - static_assert(std::is_const<T>::value, ""); - return absl::equal(a.begin(), a.end(), b.begin(), b.end()); -} - -template <typename T> -bool LessThanImpl(Span<T> a, Span<T> b) { - static_assert(std::is_const<T>::value, ""); - return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); -} - -// The `IsConvertible` classes here are needed because of the -// `std::is_convertible` bug in libcxx when compiled with GCC. This build -// configuration is used by Android NDK toolchain. Reference link: -// https://bugs.llvm.org/show_bug.cgi?id=27538. -template <typename From, typename To> -struct IsConvertibleHelper { - private: - static std::true_type testval(To); - static std::false_type testval(...); - - public: - using type = decltype(testval(std::declval<From>())); -}; - -template <typename From, typename To> -struct IsConvertible : IsConvertibleHelper<From, To>::type {}; - -// TODO(zhangxy): replace `IsConvertible` with `std::is_convertible` once the -// older version of libcxx is not supported. -template <typename From, typename To> -using EnableIfConvertibleToSpanConst = - typename std::enable_if<IsConvertible<From, Span<const To>>::value>::type; -} // namespace span_internal +inline namespace lts_2019_08_08 { //------------------------------------------------------------------------------ // Span @@ -486,6 +389,40 @@ class Span { : (base_internal::ThrowStdOutOfRange("pos > size()"), Span()); } + // Span::first() + // + // Returns a `Span` containing first `len` elements. Parameter `len` is of + // type `size_type` and thus non-negative. `len` value must be <= size(). + // + // Examples: + // + // std::vector<int> vec = {10, 11, 12, 13}; + // absl::MakeSpan(vec).first(1); // {10} + // absl::MakeSpan(vec).first(3); // {10, 11, 12} + // absl::MakeSpan(vec).first(5); // throws std::out_of_range + constexpr Span first(size_type len) const { + return (len <= size()) + ? Span(data(), len) + : (base_internal::ThrowStdOutOfRange("len > size()"), Span()); + } + + // Span::last() + // + // Returns a `Span` containing last `len` elements. Parameter `len` is of + // type `size_type` and thus non-negative. `len` value must be <= size(). + // + // Examples: + // + // std::vector<int> vec = {10, 11, 12, 13}; + // absl::MakeSpan(vec).last(1); // {13} + // absl::MakeSpan(vec).last(3); // {11, 12, 13} + // absl::MakeSpan(vec).last(5); // throws std::out_of_range + constexpr Span last(size_type len) const { + return (len <= size()) + ? Span(size() - len + data(), len) + : (base_internal::ThrowStdOutOfRange("len > size()"), Span()); + } + // Support for absl::Hash. template <typename H> friend H AbslHashValue(H h, Span v) { @@ -518,25 +455,27 @@ const typename Span<T>::size_type Span<T>::npos; // operator== template <typename T> bool operator==(Span<T> a, Span<T> b) { - return span_internal::EqualImpl<const T>(a, b); + return span_internal::EqualImpl<Span, const T>(a, b); } template <typename T> bool operator==(Span<const T> a, Span<T> b) { - return span_internal::EqualImpl<const T>(a, b); + return span_internal::EqualImpl<Span, const T>(a, b); } template <typename T> bool operator==(Span<T> a, Span<const T> b) { - return span_internal::EqualImpl<const T>(a, b); + return span_internal::EqualImpl<Span, const T>(a, b); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator==(const U& a, Span<T> b) { - return span_internal::EqualImpl<const T>(a, b); + return span_internal::EqualImpl<Span, const T>(a, b); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator==(Span<T> a, const U& b) { - return span_internal::EqualImpl<const T>(a, b); + return span_internal::EqualImpl<Span, const T>(a, b); } // operator!= @@ -552,13 +491,15 @@ template <typename T> bool operator!=(Span<T> a, Span<const T> b) { return !(a == b); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator!=(const U& a, Span<T> b) { return !(a == b); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator!=(Span<T> a, const U& b) { return !(a == b); } @@ -566,25 +507,27 @@ bool operator!=(Span<T> a, const U& b) { // operator< template <typename T> bool operator<(Span<T> a, Span<T> b) { - return span_internal::LessThanImpl<const T>(a, b); + return span_internal::LessThanImpl<Span, const T>(a, b); } template <typename T> bool operator<(Span<const T> a, Span<T> b) { - return span_internal::LessThanImpl<const T>(a, b); + return span_internal::LessThanImpl<Span, const T>(a, b); } template <typename T> bool operator<(Span<T> a, Span<const T> b) { - return span_internal::LessThanImpl<const T>(a, b); + return span_internal::LessThanImpl<Span, const T>(a, b); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator<(const U& a, Span<T> b) { - return span_internal::LessThanImpl<const T>(a, b); + return span_internal::LessThanImpl<Span, const T>(a, b); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator<(Span<T> a, const U& b) { - return span_internal::LessThanImpl<const T>(a, b); + return span_internal::LessThanImpl<Span, const T>(a, b); } // operator> @@ -600,13 +543,15 @@ template <typename T> bool operator>(Span<T> a, Span<const T> b) { return b < a; } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator>(const U& a, Span<T> b) { return b < a; } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator>(Span<T> a, const U& b) { return b < a; } @@ -624,13 +569,15 @@ template <typename T> bool operator<=(Span<T> a, Span<const T> b) { return !(b < a); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator<=(const U& a, Span<T> b) { return !(b < a); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator<=(Span<T> a, const U& b) { return !(b < a); } @@ -648,13 +595,15 @@ template <typename T> bool operator>=(Span<T> a, Span<const T> b) { return !(a < b); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator>=(const U& a, Span<T> b) { return !(a < b); } -template <typename T, typename U, - typename = span_internal::EnableIfConvertibleToSpanConst<U, T>> +template < + typename T, typename U, + typename = span_internal::EnableIfConvertibleTo<U, absl::Span<const T>>> bool operator>=(Span<T> a, const U& b) { return !(a < b); } @@ -759,6 +708,6 @@ template <int&... ExplicitArgumentBarrier, typename T, size_t N> constexpr Span<const T> MakeConstSpan(const T (&array)[N]) noexcept { return Span<const T>(array, N); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_TYPES_SPAN_H_ diff --git a/absl/types/span_test.cc b/absl/types/span_test.cc index bd739ff2..9269f911 100644 --- a/absl/types/span_test.cc +++ b/absl/types/span_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -139,8 +139,10 @@ TEST(CharSpan, StringCtor) { EXPECT_THAT(s_const_abc, SpanIs(abc)); EXPECT_FALSE((std::is_constructible<absl::Span<int>, std::string>::value)); - EXPECT_FALSE((std::is_constructible<absl::Span<const int>, std::string>::value)); - EXPECT_TRUE((std::is_convertible<std::string, absl::Span<const char>>::value)); + EXPECT_FALSE( + (std::is_constructible<absl::Span<const int>, std::string>::value)); + EXPECT_TRUE( + (std::is_convertible<std::string, absl::Span<const char>>::value)); } TEST(IntSpan, FromConstPointer) { @@ -293,6 +295,38 @@ TEST(IntSpan, Subspan) { #endif } +TEST(IntSpan, First) { + std::vector<int> empty; + EXPECT_THAT(absl::MakeSpan(empty).first(0), SpanIs(empty)); + + auto ramp = MakeRamp(10); + EXPECT_THAT(absl::MakeSpan(ramp).first(0), SpanIs(ramp.data(), 0)); + EXPECT_THAT(absl::MakeSpan(ramp).first(10), SpanIs(ramp)); + EXPECT_THAT(absl::MakeSpan(ramp).first(3), SpanIs(ramp.data(), 3)); + +#ifdef ABSL_HAVE_EXCEPTIONS + EXPECT_THROW(absl::MakeSpan(ramp).first(11), std::out_of_range); +#else + EXPECT_DEATH_IF_SUPPORTED(absl::MakeSpan(ramp).first(11), ""); +#endif +} + +TEST(IntSpan, Last) { + std::vector<int> empty; + EXPECT_THAT(absl::MakeSpan(empty).last(0), SpanIs(empty)); + + auto ramp = MakeRamp(10); + EXPECT_THAT(absl::MakeSpan(ramp).last(0), SpanIs(ramp.data() + 10, 0)); + EXPECT_THAT(absl::MakeSpan(ramp).last(10), SpanIs(ramp)); + EXPECT_THAT(absl::MakeSpan(ramp).last(3), SpanIs(ramp.data() + 7, 3)); + +#ifdef ABSL_HAVE_EXCEPTIONS + EXPECT_THROW(absl::MakeSpan(ramp).last(11), std::out_of_range); +#else + EXPECT_DEATH_IF_SUPPORTED(absl::MakeSpan(ramp).last(11), ""); +#endif +} + TEST(IntSpan, MakeSpanPtrLength) { std::vector<int> empty; auto s_empty = absl::MakeSpan(empty.data(), empty.size()); @@ -767,6 +801,8 @@ TEST(ConstIntSpan, ConstexprTest) { ABSL_TEST_CONSTEXPR(span.begin()); ABSL_TEST_CONSTEXPR(span.cbegin()); ABSL_TEST_CONSTEXPR(span.subspan(0, 0)); + ABSL_TEST_CONSTEXPR(span.first(1)); + ABSL_TEST_CONSTEXPR(span.last(1)); ABSL_TEST_CONSTEXPR(span[0]); } @@ -779,4 +815,19 @@ TEST(Span, SpanSize) { EXPECT_LE(sizeof(absl::Span<BigStruct>), 2 * sizeof(void*)); } +TEST(Span, Hash) { + int array[] = {1, 2, 3, 4}; + int array2[] = {1, 2, 3}; + using T = absl::Span<const int>; + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + {// Empties + T(), T(nullptr, 0), T(array, 0), T(array2, 0), + // Different array with same value + T(array, 3), T(array2), T({1, 2, 3}), + // Same array, but different length + T(array, 1), T(array, 2), + // Same length, but different array + T(array + 1, 2), T(array + 2, 2)})); +} + } // namespace diff --git a/absl/types/variant.h b/absl/types/variant.h index 4ae4e00d..1c1962b1 100644 --- a/absl/types/variant.h +++ b/absl/types/variant.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -47,10 +47,10 @@ #ifdef ABSL_HAVE_STD_VARIANT -#include <variant> +#include <variant> // IWYU pragma: export namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { using std::bad_variant_access; using std::get; using std::get_if; @@ -63,7 +63,7 @@ using std::variant_npos; using std::variant_size; using std::variant_size_v; using std::visit; -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #else // ABSL_HAVE_STD_VARIANT @@ -79,7 +79,7 @@ using std::visit; #include "absl/types/internal/variant.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // ----------------------------------------------------------------------------- // absl::variant @@ -132,7 +132,12 @@ class variant; // type (in which case, they will be swapped) or to two different types (in // which case the values will need to be moved). // -template <typename... Ts> +template < + typename... Ts, + absl::enable_if_t< + absl::conjunction<std::is_move_constructible<Ts>..., + type_traits_internal::IsSwappable<Ts>...>::value, + int> = 0> void swap(variant<Ts...>& v, variant<Ts...>& w) noexcept(noexcept(v.swap(w))) { v.swap(w); } @@ -691,12 +696,12 @@ class variant<T0, Tn...> : private variant_internal::VariantBase<T0, Tn...> { // // Swaps the values of two variant objects. // - // TODO(calabrese) - // `variant::swap()` and `swap()` rely on `std::is_(nothrow)_swappable()` - // which is introduced in C++17. So we assume `is_swappable()` is always - // true and `is_nothrow_swappable()` is same as `std::is_trivial()`. void swap(variant& rhs) noexcept( - absl::conjunction<std::is_trivial<T0>, std::is_trivial<Tn>...>::value) { + absl::conjunction< + std::is_nothrow_move_constructible<T0>, + std::is_nothrow_move_constructible<Tn>..., + type_traits_internal::IsNothrowSwappable<T0>, + type_traits_internal::IsNothrowSwappable<Tn>...>::value) { return variant_internal::VisitIndices<sizeof...(Tn) + 1>::Run( variant_internal::Swap<T0, Tn...>{this, &rhs}, rhs.index()); } @@ -793,7 +798,7 @@ operator>=(const variant<Types...>& a, const variant<Types...>& b) { a.index()); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl namespace std { @@ -814,7 +819,7 @@ struct hash<absl::variant<T...>> #endif // ABSL_HAVE_STD_VARIANT namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace variant_internal { // Helper visitor for converting a variant<Ts...>` into another type (mostly @@ -850,7 +855,7 @@ To ConvertVariantTo(Variant&& variant) { std::forward<Variant>(variant)); } -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_TYPES_VARIANT_H_ diff --git a/absl/types/variant_benchmark.cc b/absl/types/variant_benchmark.cc index 854f1448..efe02310 100644 --- a/absl/types/variant_benchmark.cc +++ b/absl/types/variant_benchmark.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,7 +28,7 @@ #include "absl/utility/utility.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { template <std::size_t I> @@ -218,5 +218,5 @@ BENCHMARK_TEMPLATE(BM_RedundantVisit, 4, 2) ->DenseRange(0, integral_pow(4, 2) - 1); } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc index ff166051..31662545 100644 --- a/absl/types/variant_exception_safety_test.cc +++ b/absl/types/variant_exception_safety_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -24,11 +24,12 @@ #include "absl/base/config.h" #include "absl/base/internal/exception_safety_testing.h" #include "absl/memory/memory.h" + // See comment in absl/base/config.h #if !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { using ::testing::MakeExceptionSafetyTester; @@ -316,6 +317,12 @@ TEST(VariantExceptionSafetyTest, MoveAssign) { EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); } { + // libstdc++ introduced a regression between 2018-09-25 and 2019-01-06. + // The fix is targeted for gcc-9. + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87431#c7 + // https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=267614 +#if !(defined(ABSL_HAVE_STD_VARIANT) && \ + defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) // - otherwise (index() != j), equivalent to // emplace<j>(get<j>(std::move(rhs))) // - If an exception is thrown during the call to Tj's move construction @@ -331,6 +338,8 @@ TEST(VariantExceptionSafetyTest, MoveAssign) { auto copy = rhs; *lhs = std::move(copy); })); +#endif // !(defined(ABSL_HAVE_STD_VARIANT) && + // defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) } } @@ -510,7 +519,7 @@ TEST(VariantExceptionSafetyTest, Swap) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl #endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc index 59223ea7..ff0f187a 100644 --- a/absl/types/variant_test.cc +++ b/absl/types/variant_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -67,7 +67,7 @@ struct hash<Hashable> { struct NonHashable {}; namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { namespace { using ::testing::DoubleEq; @@ -258,7 +258,7 @@ class NonCopyable { // each type. template <typename T> class VariantTypesTest : public ::testing::Test {}; -TYPED_TEST_CASE(VariantTypesTest, VariantTypes); +TYPED_TEST_SUITE(VariantTypesTest, VariantTypes); //////////////////// // [variant.ctor] // @@ -385,7 +385,7 @@ struct MoveOnly { TEST(VariantTest, TestMoveConstruct) { using V = variant<MoveOnly<class A>, MoveOnly<class B>, MoveOnly<class C>>; - V v(in_place_index_t<1>{}, 10); + V v(in_place_index<1>, 10); V v2 = absl::move(v); EXPECT_EQ(10, absl::get<1>(v2).value); } @@ -461,6 +461,11 @@ TYPED_TEST(VariantTypesTest, TestValueCtor) { EXPECT_EQ(value.value, mutable_valptr->value); } +TEST(VariantTest, AmbiguousValueConstructor) { + EXPECT_FALSE((std::is_convertible<int, absl::variant<int, int>>::value)); + EXPECT_FALSE((std::is_constructible<absl::variant<int, int>, int>::value)); +} + TEST(VariantTest, InPlaceType) { using Var = variant<int, std::string, NonCopyable, std::vector<int>>; @@ -484,14 +489,47 @@ TEST(VariantTest, InPlaceType) { EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3)); } +TEST(VariantTest, InPlaceTypeVariableTemplate) { + using Var = variant<int, std::string, NonCopyable, std::vector<int>>; + + Var v1(in_place_type<int>, 7); + ASSERT_TRUE(absl::holds_alternative<int>(v1)); + EXPECT_EQ(7, absl::get<int>(v1)); + + Var v2(in_place_type<std::string>, "ABC"); + ASSERT_TRUE(absl::holds_alternative<std::string>(v2)); + EXPECT_EQ("ABC", absl::get<std::string>(v2)); + + Var v3(in_place_type<std::string>, "ABC", 2); + ASSERT_TRUE(absl::holds_alternative<std::string>(v3)); + EXPECT_EQ("AB", absl::get<std::string>(v3)); + + Var v4(in_place_type<NonCopyable>); + ASSERT_TRUE(absl::holds_alternative<NonCopyable>(v4)); + + Var v5(in_place_type<std::vector<int>>, {1, 2, 3}); + ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5)); + EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3)); +} + TEST(VariantTest, InPlaceTypeInitializerList) { - using Var = variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; + using Var = + variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; Var v1(in_place_type_t<MoveOnlyWithListConstructor>(), {1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); } +TEST(VariantTest, InPlaceTypeInitializerListVariabletemplate) { + using Var = + variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; + + Var v1(in_place_type<MoveOnlyWithListConstructor>, {1, 2, 3, 4, 5}, 6); + ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); + EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); +} + TEST(VariantTest, InPlaceIndex) { using Var = variant<int, std::string, NonCopyable, std::vector<int>>; @@ -519,14 +557,51 @@ TEST(VariantTest, InPlaceIndex) { EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3)); } +TEST(VariantTest, InPlaceIndexVariableTemplate) { + using Var = variant<int, std::string, NonCopyable, std::vector<int>>; + + Var v1(in_place_index<0>, 7); + ASSERT_TRUE(absl::holds_alternative<int>(v1)); + EXPECT_EQ(7, absl::get<int>(v1)); + + Var v2(in_place_index<1>, "ABC"); + ASSERT_TRUE(absl::holds_alternative<std::string>(v2)); + EXPECT_EQ("ABC", absl::get<std::string>(v2)); + + Var v3(in_place_index<1>, "ABC", 2); + ASSERT_TRUE(absl::holds_alternative<std::string>(v3)); + EXPECT_EQ("AB", absl::get<std::string>(v3)); + + Var v4(in_place_index<2>); + EXPECT_TRUE(absl::holds_alternative<NonCopyable>(v4)); + + // Verify that a variant with only non-copyables can still be constructed. + EXPECT_TRUE(absl::holds_alternative<NonCopyable>( + variant<NonCopyable>(in_place_index<0>))); + + Var v5(in_place_index<3>, {1, 2, 3}); + ASSERT_TRUE(absl::holds_alternative<std::vector<int>>(v5)); + EXPECT_THAT(absl::get<std::vector<int>>(v5), ::testing::ElementsAre(1, 2, 3)); +} + TEST(VariantTest, InPlaceIndexInitializerList) { - using Var = variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; + using Var = + variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; Var v1(in_place_index_t<3>(), {1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); } +TEST(VariantTest, InPlaceIndexInitializerListVariableTemplate) { + using Var = + variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; + + Var v1(in_place_index<3>, {1, 2, 3, 4, 5}, 6); + ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); + EXPECT_EQ(6, absl::get<MoveOnlyWithListConstructor>(v1).value); +} + //////////////////// // [variant.dtor] // //////////////////// @@ -560,6 +635,7 @@ TEST(VariantTest, TestDtor) { } #ifdef ABSL_HAVE_EXCEPTIONS + // See comment in absl/base/config.h #if defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) TEST(VariantTest, DISABLED_TestDtorValuelessByException) @@ -574,7 +650,7 @@ TEST(VariantTest, TestDtorValuelessByException) { using Variant = VariantFactory<IncrementInDtor>::Type; - Variant v(in_place_index_t<0>(), counter_adjuster); + Variant v(in_place_index<0>, counter_adjuster); EXPECT_EQ(0, counter); ToValuelessByException(v); @@ -808,7 +884,7 @@ TEST(VariantTest, TestBackupAssign) { TEST(VariantTest, TestEmplaceBasic) { using Variant = variant<int, char>; - Variant v(absl::in_place_index_t<0>{}, 0); + Variant v(absl::in_place_index<0>, 0); { char& emplace_result = v.emplace<char>(); @@ -832,9 +908,10 @@ TEST(VariantTest, TestEmplaceBasic) { } TEST(VariantTest, TestEmplaceInitializerList) { - using Var = variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; + using Var = + variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; - Var v1(absl::in_place_index_t<0>{}, 555); + Var v1(absl::in_place_index<0>, 555); MoveOnlyWithListConstructor& emplace_result = v1.emplace<MoveOnlyWithListConstructor>({1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); @@ -845,7 +922,7 @@ TEST(VariantTest, TestEmplaceInitializerList) { TEST(VariantTest, TestEmplaceIndex) { using Variant = variant<int, char>; - Variant v(absl::in_place_index_t<0>{}, 555); + Variant v(absl::in_place_index<0>, 555); { char& emplace_result = v.emplace<1>(); @@ -869,9 +946,10 @@ TEST(VariantTest, TestEmplaceIndex) { } TEST(VariantTest, TestEmplaceIndexInitializerList) { - using Var = variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; + using Var = + variant<int, std::string, NonCopyable, MoveOnlyWithListConstructor>; - Var v1(absl::in_place_index_t<0>{}, 555); + Var v1(absl::in_place_index<0>, 555); MoveOnlyWithListConstructor& emplace_result = v1.emplace<3>({1, 2, 3, 4, 5}, 6); ASSERT_TRUE(absl::holds_alternative<MoveOnlyWithListConstructor>(v1)); @@ -920,7 +998,7 @@ TEST(VariantTest, NotValuelessByException) { TEST(VariantTest, IndexValuelessByException) { using Var = variant<MoveCanThrow, std::string, double>; - Var v(absl::in_place_index_t<0>{}); + Var v(absl::in_place_index<0>); EXPECT_EQ(0, v.index()); ToValuelessByException(v); EXPECT_EQ(absl::variant_npos, v.index()); @@ -931,7 +1009,7 @@ TEST(VariantTest, IndexValuelessByException) { TEST(VariantTest, ValuelessByException) { using Var = variant<MoveCanThrow, std::string, double>; - Var v(absl::in_place_index_t<0>{}); + Var v(absl::in_place_index<0>); EXPECT_FALSE(v.valueless_by_exception()); ToValuelessByException(v); EXPECT_TRUE(v.valueless_by_exception()); @@ -962,7 +1040,7 @@ TEST(VariantTest, MemberSwap) { using V = variant<MoveCanThrow, std::string, int>; int i = 33; std::string s = "abc"; - V valueless(in_place_index_t<0>{}); + V valueless(in_place_index<0>); ToValuelessByException(valueless); { // lhs and rhs holds different alternative @@ -1123,7 +1201,7 @@ TEST(VariantTest, GetIndex) { using Var = variant<int, std::string, double, int>; { - Var v(absl::in_place_index_t<0>{}, 0); + Var v(absl::in_place_index<0>, 0); using LValueGetType = decltype(absl::get<0>(v)); using RValueGetType = decltype(absl::get<0>(absl::move(v))); @@ -1183,7 +1261,7 @@ TEST(VariantTest, GetIndex) { } { - Var v(absl::in_place_index_t<0>{}, 0); + Var v(absl::in_place_index<0>, 0); v.emplace<3>(1); using LValueGetType = decltype(absl::get<3>(v)); @@ -1307,7 +1385,8 @@ TEST(VariantTest, BadGetType) { absl::get<std::string>(std::move(v))); const Var& const_v = v; - ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS(absl::get<std::string>(const_v)); + ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( + absl::get<std::string>(const_v)); ABSL_VARIANT_TEST_EXPECT_BAD_VARIANT_ACCESS( absl::get<std::string>(std::move(const_v))); // NOLINT } @@ -1329,7 +1408,7 @@ TEST(VariantTest, GetIfIndex) { using Var = variant<int, std::string, double, int>; { - Var v(absl::in_place_index_t<0>{}, 0); + Var v(absl::in_place_index<0>, 0); EXPECT_TRUE(noexcept(absl::get_if<0>(&v))); { @@ -1364,7 +1443,8 @@ TEST(VariantTest, GetIfIndex) { EXPECT_EQ(*elem, 0); { auto* bad_elem = absl::get_if<1>(&const_v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const std::string*>::value)); + EXPECT_TRUE( + (std::is_same<decltype(bad_elem), const std::string*>::value)); EXPECT_EQ(bad_elem, nullptr); } { @@ -1473,7 +1553,8 @@ TEST(VariantTest, GetIfIndex) { } { auto* bad_elem = absl::get_if<1>(&const_v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const std::string*>::value)); + EXPECT_TRUE( + (std::is_same<decltype(bad_elem), const std::string*>::value)); EXPECT_EQ(bad_elem, nullptr); } { @@ -1485,7 +1566,7 @@ TEST(VariantTest, GetIfIndex) { } { - Var v(absl::in_place_index_t<0>{}, 0); + Var v(absl::in_place_index<0>, 0); v.emplace<3>(1); EXPECT_TRUE(noexcept(absl::get_if<3>(&v))); @@ -1526,7 +1607,8 @@ TEST(VariantTest, GetIfIndex) { } { auto* bad_elem = absl::get_if<1>(&const_v); - EXPECT_TRUE((std::is_same<decltype(bad_elem), const std::string*>::value)); + EXPECT_TRUE( + (std::is_same<decltype(bad_elem), const std::string*>::value)); EXPECT_EQ(bad_elem, nullptr); } { @@ -1630,8 +1712,8 @@ TEST(VariantTest, OperatorRelational) { TEST(VariantTest, ValuelessOperatorEquals) { variant<MoveCanThrow, std::string> int_v(1), string_v("Hello"), - valueless(absl::in_place_index_t<0>{}), - other_valueless(absl::in_place_index_t<0>{}); + valueless(absl::in_place_index<0>), + other_valueless(absl::in_place_index<0>); ToValuelessByException(valueless); ToValuelessByException(other_valueless); @@ -1652,8 +1734,8 @@ TEST(VariantTest, ValuelessOperatorEquals) { TEST(VariantTest, ValuelessOperatorRelational) { variant<MoveCanThrow, std::string> int_v(1), string_v("Hello"), - valueless(absl::in_place_index_t<0>{}), - other_valueless(absl::in_place_index_t<0>{}); + valueless(absl::in_place_index<0>), + other_valueless(absl::in_place_index<0>); ToValuelessByException(valueless); ToValuelessByException(other_valueless); @@ -1712,8 +1794,8 @@ TEST(VariantTest, VisitSimple) { EXPECT_EQ("B", piece); struct StrLen { - int operator()(const std::string& s) const { return s.size(); } int operator()(const char* s) const { return strlen(s); } + int operator()(const std::string& s) const { return s.size(); } }; v = "SomeStr"; @@ -1729,9 +1811,13 @@ TEST(VariantTest, VisitRValue) { bool operator()(std::string&&) const { return true; } // NOLINT int operator()(const std::string&, const std::string&) const { return 0; } - int operator()(const std::string&, std::string&&) const { return 1; } // NOLINT - int operator()(std::string&&, const std::string&) const { return 2; } // NOLINT - int operator()(std::string&&, std::string&&) const { return 3; } // NOLINT + int operator()(const std::string&, std::string&&) const { + return 1; + } // NOLINT + int operator()(std::string&&, const std::string&) const { + return 2; + } // NOLINT + int operator()(std::string&&, std::string&&) const { return 3; } // NOLINT }; EXPECT_FALSE(absl::visit(Visitor{}, v)); EXPECT_TRUE(absl::visit(Visitor{}, absl::move(v))); @@ -1807,9 +1893,9 @@ TEST(VariantTest, VisitVariadic) { EXPECT_THAT(absl::visit(Visitor(), A(std::string("BBBBB")), B(std::unique_ptr<int>(new int(7)))), ::testing::Pair(5, 7)); - EXPECT_THAT( - absl::visit(Visitor(), A(std::string("BBBBB")), B(absl::string_view("ABC"))), - ::testing::Pair(5, 3)); + EXPECT_THAT(absl::visit(Visitor(), A(std::string("BBBBB")), + B(absl::string_view("ABC"))), + ::testing::Pair(5, 3)); } TEST(VariantTest, VisitNoArgs) { @@ -1978,29 +2064,17 @@ TEST(VariantTest, MonostateHash) { } TEST(VariantTest, Hash) { - static_assert(type_traits_internal::IsHashEnabled<variant<int>>::value, ""); - static_assert(type_traits_internal::IsHashEnabled<variant<Hashable>>::value, + static_assert(type_traits_internal::IsHashable<variant<int>>::value, ""); + static_assert(type_traits_internal::IsHashable<variant<Hashable>>::value, ""); + static_assert(type_traits_internal::IsHashable<variant<int, Hashable>>::value, ""); - static_assert( - type_traits_internal::IsHashEnabled<variant<int, Hashable>>::value, ""); - -#if defined(_MSC_VER) || \ - (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 4000 && \ - _LIBCPP_STD_VER > 11) || \ - defined(__APPLE__) - // For MSVC and libc++ (< 4.0 and c++14), std::hash primary template has a - // static_assert to catch any user-defined type T that doesn't provide a hash - // specialization. So instantiating std::hash<variant<T>> will result - // in a hard error which is not SFINAE friendly. -#define ABSL_STD_HASH_NOT_SFINAE_FRIENDLY 1 -#endif -#ifndef ABSL_STD_HASH_NOT_SFINAE_FRIENDLY - static_assert( - !type_traits_internal::IsHashEnabled<variant<NonHashable>>::value, ""); - static_assert(!type_traits_internal::IsHashEnabled< - variant<Hashable, NonHashable>>::value, +#if ABSL_META_INTERNAL_STD_HASH_SFINAE_FRIENDLY_ + static_assert(!type_traits_internal::IsHashable<variant<NonHashable>>::value, ""); + static_assert( + !type_traits_internal::IsHashable<variant<Hashable, NonHashable>>::value, + ""); #endif // MSVC std::hash<std::variant> does not use the index, thus produce the same @@ -2008,8 +2082,8 @@ TEST(VariantTest, Hash) { #if !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT)) { // same value as different alternative - variant<int, int> v0(in_place_index_t<0>{}, 42); - variant<int, int> v1(in_place_index_t<1>{}, 42); + variant<int, int> v0(in_place_index<0>, 42); + variant<int, int> v1(in_place_index<1>, 42); std::hash<variant<int, int>> hash; EXPECT_NE(hash(v0), hash(v1)); } @@ -2024,11 +2098,10 @@ TEST(VariantTest, Hash) { EXPECT_GT(hashcodes.size(), 90); // test const-qualified + static_assert(type_traits_internal::IsHashable<variant<const int>>::value, + ""); static_assert( - type_traits_internal::IsHashEnabled<variant<const int>>::value, ""); - static_assert( - type_traits_internal::IsHashEnabled<variant<const Hashable>>::value, - ""); + type_traits_internal::IsHashable<variant<const Hashable>>::value, ""); std::hash<absl::variant<const int>> c_hash; for (int i = 0; i < 100; ++i) { EXPECT_EQ(hash(i), c_hash(i)); @@ -2040,7 +2113,8 @@ TEST(VariantTest, Hash) { // Miscellaneous and deprecated tests // //////////////////////////////////////// -// Test that a set requiring a basic type conversion works correctly. +// Test that a set requiring a basic type conversion works correctly +#if !defined(ABSL_HAVE_STD_VARIANT) TEST(VariantTest, TestConvertingSet) { typedef variant<double> Variant; Variant v(1.0); @@ -2050,6 +2124,7 @@ TEST(VariantTest, TestConvertingSet) { ASSERT_TRUE(nullptr != absl::get_if<double>(&v)); EXPECT_DOUBLE_EQ(2, absl::get<double>(v)); } +#endif // ABSL_HAVE_STD_VARIANT // Test that a vector of variants behaves reasonably. TEST(VariantTest, Container) { @@ -2177,7 +2252,8 @@ TEST(VariantTest, TestImplicitConversion) { // We still need the explicit cast for std::string, because C++ won't apply // two user-defined implicit conversions in a row. - EXPECT_TRUE(absl::holds_alternative<std::string>(PassThrough(std::string("foo")))); + EXPECT_TRUE( + absl::holds_alternative<std::string>(PassThrough(std::string("foo")))); } struct Convertible2; @@ -2200,8 +2276,10 @@ struct Convertible2 { }; TEST(VariantTest, TestRvalueConversion) { +#if !defined(ABSL_HAVE_STD_VARIANT) variant<double, std::string> var( - ConvertVariantTo<variant<double, std::string>>(variant<std::string, int>(0))); + ConvertVariantTo<variant<double, std::string>>( + variant<std::string, int>(0))); ASSERT_TRUE(absl::holds_alternative<double>(var)); EXPECT_EQ(0.0, absl::get<double>(var)); @@ -2231,6 +2309,7 @@ TEST(VariantTest, TestRvalueConversion) { variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42)); ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2)); EXPECT_EQ(42, absl::get<uint32_t>(variant2)); +#endif // !ABSL_HAVE_STD_VARIANT variant<Convertible1, Convertible2> variant3( ConvertVariantTo<variant<Convertible1, Convertible2>>( @@ -2243,6 +2322,7 @@ TEST(VariantTest, TestRvalueConversion) { } TEST(VariantTest, TestLvalueConversion) { +#if !defined(ABSL_HAVE_STD_VARIANT) variant<std::string, int> source1 = 0; variant<double, std::string> destination( ConvertVariantTo<variant<double, std::string>>(source1)); @@ -2279,6 +2359,7 @@ TEST(VariantTest, TestLvalueConversion) { variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6); ASSERT_TRUE(absl::holds_alternative<uint32_t>(variant2)); EXPECT_EQ(42, absl::get<uint32_t>(variant2)); +#endif variant<Convertible2, Convertible1> source7((Convertible1())); variant<Convertible1, Convertible2> variant3( @@ -2293,7 +2374,8 @@ TEST(VariantTest, TestLvalueConversion) { TEST(VariantTest, TestMoveConversion) { using Variant = variant<std::unique_ptr<const int>, std::unique_ptr<const std::string>>; - using OtherVariant = variant<std::unique_ptr<int>, std::unique_ptr<std::string>>; + using OtherVariant = + variant<std::unique_ptr<int>, std::unique_ptr<std::string>>; Variant var( ConvertVariantTo<Variant>(OtherVariant{absl::make_unique<int>(0)})); @@ -2301,8 +2383,8 @@ TEST(VariantTest, TestMoveConversion) { ASSERT_NE(absl::get<std::unique_ptr<const int>>(var), nullptr); EXPECT_EQ(0, *absl::get<std::unique_ptr<const int>>(var)); - var = - ConvertVariantTo<Variant>(OtherVariant(absl::make_unique<std::string>("foo"))); + var = ConvertVariantTo<Variant>( + OtherVariant(absl::make_unique<std::string>("foo"))); ASSERT_TRUE(absl::holds_alternative<std::unique_ptr<const std::string>>(var)); EXPECT_EQ("foo", *absl::get<std::unique_ptr<const std::string>>(var)); } @@ -2313,7 +2395,8 @@ TEST(VariantTest, DoesNotMoveFromLvalues) { // whether moving or copying has occurred. using Variant = variant<std::shared_ptr<const int>, std::shared_ptr<const std::string>>; - using OtherVariant = variant<std::shared_ptr<int>, std::shared_ptr<std::string>>; + using OtherVariant = + variant<std::shared_ptr<int>, std::shared_ptr<std::string>>; Variant v1(std::make_shared<const int>(0)); @@ -2341,8 +2424,10 @@ TEST(VariantTest, DoesNotMoveFromLvalues) { } TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { +#if !defined(ABSL_HAVE_STD_VARIANT) variant<double, std::string> var( - ConvertVariantTo<variant<double, std::string>>(variant<std::string, int>(3))); + ConvertVariantTo<variant<double, std::string>>( + variant<std::string, int>(3))); EXPECT_THAT(absl::get_if<double>(&var), Pointee(3.0)); var = ConvertVariantTo<variant<double, std::string>>( @@ -2365,6 +2450,7 @@ TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(variant<uint32_t>(42)); EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42)); +#endif variant<Convertible1, Convertible2> variant3( ConvertVariantTo<variant<Convertible1, Convertible2>>( @@ -2377,6 +2463,7 @@ TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { } TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { +#if !defined(ABSL_HAVE_STD_VARIANT) variant<std::string, int> source1 = 3; variant<double, std::string> destination( ConvertVariantTo<variant<double, std::string>>(source1)); @@ -2384,7 +2471,8 @@ TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { variant<const char*, float> source2 = "foo"; destination = ConvertVariantTo<variant<double, std::string>>(source2); - EXPECT_THAT(absl::get_if<std::string>(&destination), Pointee(std::string("foo"))); + EXPECT_THAT(absl::get_if<std::string>(&destination), + Pointee(std::string("foo"))); variant<int, float> source3(42); variant<double> singleton(ConvertVariantTo<variant<double>>(source3)); @@ -2407,6 +2495,7 @@ TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { variant<uint32_t> source6(42); variant2 = ConvertVariantTo<variant<int32_t, uint32_t>>(source6); EXPECT_THAT(absl::get_if<uint32_t>(&variant2), Pointee(42)); +#endif // !ABSL_HAVE_STD_VARIANT variant<Convertible2, Convertible1> source7((Convertible1())); variant<Convertible1, Convertible2> variant3( @@ -2421,15 +2510,16 @@ TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { TEST(VariantTest, TestMoveConversionViaConvertVariantTo) { using Variant = variant<std::unique_ptr<const int>, std::unique_ptr<const std::string>>; - using OtherVariant = variant<std::unique_ptr<int>, std::unique_ptr<std::string>>; + using OtherVariant = + variant<std::unique_ptr<int>, std::unique_ptr<std::string>>; Variant var( ConvertVariantTo<Variant>(OtherVariant{absl::make_unique<int>(3)})); EXPECT_THAT(absl::get_if<std::unique_ptr<const int>>(&var), Pointee(Pointee(3))); - var = - ConvertVariantTo<Variant>(OtherVariant(absl::make_unique<std::string>("foo"))); + var = ConvertVariantTo<Variant>( + OtherVariant(absl::make_unique<std::string>("foo"))); EXPECT_THAT(absl::get_if<std::unique_ptr<const std::string>>(&var), Pointee(Pointee(std::string("foo")))); } @@ -2599,7 +2689,7 @@ TEST(VariantTest, MoveCtorBug) { }; { using V = absl::variant<TrivialCopyNontrivialMove, int>; - V v1(absl::in_place_index_t<0>{}); + V v1(absl::in_place_index<0>); // this should invoke the move ctor, rather than the trivial copy ctor. V v2(std::move(v1)); EXPECT_TRUE(absl::get<0>(v2).called); @@ -2607,7 +2697,7 @@ TEST(VariantTest, MoveCtorBug) { { // this case failed to compile before our fix due to a GCC bug. using V = absl::variant<int, TrivialCopyNontrivialMove>; - V v1(absl::in_place_index_t<1>{}); + V v1(absl::in_place_index<1>); // this should invoke the move ctor, rather than the trivial copy ctor. V v2(std::move(v1)); EXPECT_TRUE(absl::get<1>(v2).called); @@ -2615,5 +2705,5 @@ TEST(VariantTest, MoveCtorBug) { } } // namespace -} // inline namespace lts_2018_12_18 +} // inline namespace lts_2019_08_08 } // namespace absl diff --git a/absl/utility/BUILD.bazel b/absl/utility/BUILD.bazel index c01b49bc..d41317e3 100644 --- a/absl/utility/BUILD.bazel +++ b/absl/utility/BUILD.bazel @@ -1,6 +1,7 @@ load( - "//absl:copts.bzl", + "//absl:copts/configure_copts.bzl", "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", "ABSL_TEST_COPTS", ) @@ -12,6 +13,7 @@ cc_library( name = "utility", hdrs = ["utility.h"], copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ "//absl/base:base_internal", "//absl/base:config", @@ -23,6 +25,7 @@ cc_test( name = "utility_test", srcs = ["utility_test.cc"], copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":utility", "//absl/base:core_headers", diff --git a/absl/utility/CMakeLists.txt b/absl/utility/CMakeLists.txt index dc3a6319..e1edd19a 100644 --- a/absl/utility/CMakeLists.txt +++ b/absl/utility/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,39 +14,31 @@ # limitations under the License. # - -list(APPEND UTILITY_PUBLIC_HEADERS - "utility.h" -) - -absl_header_library( - TARGET - absl_utility - PUBLIC_LIBRARIES - absl::base - EXPORT_NAME +absl_cc_library( + NAME utility + HDRS + "utility.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::base_internal + absl::config + absl::type_traits + PUBLIC ) - -# -## TESTS -# - -# test utility_test -set(UTILITY_TEST_SRC "utility_test.cc") -set(UTILITY_TEST_PUBLIC_LIBRARIES - absl::base - absl::memory - absl::strings - absl::utility -) - -absl_test( - TARGET +absl_cc_test( + NAME utility_test - SOURCES - ${UTILITY_TEST_SRC} - PUBLIC_LIBRARIES - ${UTILITY_TEST_PUBLIC_LIBRARIES} + SRCS + "utility_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::utility + absl::core_headers + absl::memory + absl::strings + gmock_main ) diff --git a/absl/utility/utility.h b/absl/utility/utility.h index 66e22dc2..bc9af048 100644 --- a/absl/utility/utility.h +++ b/absl/utility/utility.h @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -25,6 +25,7 @@ // * index_sequence_for<Ts...> == std::index_sequence_for<Ts...> // * apply<Functor, Tuple> == std::apply<Functor, Tuple> // * exchange<T> == std::exchange<T> +// * make_from_tuple<T> == std::make_from_tuple<T> // // This header file also provides the tag types `in_place_t`, `in_place_type_t`, // and `in_place_index_t`, as well as the constant `in_place`, and @@ -32,10 +33,9 @@ // // References: // -// http://en.cppreference.com/w/cpp/utility/integer_sequence -// http://en.cppreference.com/w/cpp/utility/apply +// https://en.cppreference.com/w/cpp/utility/integer_sequence +// https://en.cppreference.com/w/cpp/utility/apply // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html -// #ifndef ABSL_UTILITY_UTILITY_H_ #define ABSL_UTILITY_UTILITY_H_ @@ -51,7 +51,7 @@ #include "absl/meta/type_traits.h" namespace absl { -inline namespace lts_2018_12_18 { +inline namespace lts_2019_08_08 { // integer_sequence // @@ -115,6 +115,20 @@ struct Gen<T, 0> { using type = integer_sequence<T>; }; +template <typename T> +struct InPlaceTypeTag { + explicit InPlaceTypeTag() = delete; + InPlaceTypeTag(const InPlaceTypeTag&) = delete; + InPlaceTypeTag& operator=(const InPlaceTypeTag&) = delete; +}; + +template <size_t I> +struct InPlaceIndexTag { + explicit InPlaceIndexTag() = delete; + InPlaceIndexTag(const InPlaceIndexTag&) = delete; + InPlaceIndexTag& operator=(const InPlaceIndexTag&) = delete; +}; + } // namespace utility_internal // Compile-time sequences of integers @@ -164,6 +178,7 @@ ABSL_INTERNAL_INLINE_CONSTEXPR(in_place_t, in_place, {}); #endif // ABSL_HAVE_STD_OPTIONAL #if defined(ABSL_HAVE_STD_ANY) || defined(ABSL_HAVE_STD_VARIANT) +using std::in_place_type; using std::in_place_type_t; #else @@ -173,10 +188,14 @@ using std::in_place_type_t; // be specified, such as with `absl::any`, designed to be a drop-in replacement // for C++17's `std::in_place_type_t`. template <typename T> -struct in_place_type_t {}; +using in_place_type_t = void (*)(utility_internal::InPlaceTypeTag<T>); + +template <typename T> +void in_place_type(utility_internal::InPlaceTypeTag<T>) {} #endif // ABSL_HAVE_STD_ANY || ABSL_HAVE_STD_VARIANT #ifdef ABSL_HAVE_STD_VARIANT +using std::in_place_index; using std::in_place_index_t; #else @@ -186,7 +205,10 @@ using std::in_place_index_t; // be specified, such as with `absl::any`, designed to be a drop-in replacement // for C++17's `std::in_place_index_t`. template <size_t I> -struct in_place_index_t {}; +using in_place_index_t = void (*)(utility_internal::InPlaceIndexTag<I>); + +template <size_t I> +void in_place_index(utility_internal::InPlaceIndexTag<I>) {} #endif // ABSL_HAVE_STD_VARIANT // Constexpr move and forward @@ -235,25 +257,33 @@ auto apply_helper(Functor&& functor, Tuple&& t, index_sequence<Indexes...>) // // Example: // -// class Foo{void Bar(int);}; -// void user_function(int, string); -// void user_function(std::unique_ptr<Foo>); +// class Foo { +// public: +// void Bar(int); +// }; +// void user_function1(int, std::string); +// void user_function2(std::unique_ptr<Foo>); +// auto user_lambda = [](int, int) {}; // // int main() // { -// std::tuple<int, string> tuple1(42, "bar"); -// // Invokes the user function overload on int, string. -// absl::apply(&user_function, tuple1); +// std::tuple<int, std::string> tuple1(42, "bar"); +// // Invokes the first user function on int, std::string. +// absl::apply(&user_function1, tuple1); // -// auto foo = absl::make_unique<Foo>(); -// std::tuple<Foo*, int> tuple2(foo.get(), 42); -// // Invokes the method Bar on foo with one argument 42. -// absl::apply(&Foo::Bar, foo.get(), 42); -// -// std::tuple<std::unique_ptr<Foo>> tuple3(absl::make_unique<Foo>()); +// std::tuple<std::unique_ptr<Foo>> tuple2(absl::make_unique<Foo>()); // // Invokes the user function that takes ownership of the unique // // pointer. -// absl::apply(&user_function, std::move(tuple)); +// absl::apply(&user_function2, std::move(tuple2)); +// +// auto foo = absl::make_unique<Foo>(); +// std::tuple<Foo*, int> tuple3(foo.get(), 42); +// // Invokes the method Bar on foo with one argument, 42. +// absl::apply(&Foo::Bar, tuple3); +// +// std::tuple<int, int> tuple4(8, 9); +// // Invokes a lambda. +// absl::apply(user_lambda, tuple4); // } template <typename Functor, typename Tuple> auto apply(Functor&& functor, Tuple&& t) @@ -287,7 +317,34 @@ T exchange(T& obj, U&& new_value) { return old_value; } -} // inline namespace lts_2018_12_18 +namespace utility_internal { +template <typename T, typename Tuple, size_t... I> +T make_from_tuple_impl(Tuple&& tup, absl::index_sequence<I...>) { + return T(std::get<I>(std::forward<Tuple>(tup))...); +} +} // namespace utility_internal + +// make_from_tuple +// +// Given the template parameter type `T` and a tuple of arguments +// `std::tuple(arg0, arg1, ..., argN)` constructs an object of type `T` as if by +// calling `T(arg0, arg1, ..., argN)`. +// +// Example: +// +// std::tuple<const char*, size_t> args("hello world", 5); +// auto s = absl::make_from_tuple<std::string>(args); +// assert(s == "hello"); +// +template <typename T, typename Tuple> +constexpr T make_from_tuple(Tuple&& tup) { + return utility_internal::make_from_tuple_impl<T>( + std::forward<Tuple>(tup), + absl::make_index_sequence< + std::tuple_size<absl::decay_t<Tuple>>::value>{}); +} + +} // inline namespace lts_2019_08_08 } // namespace absl #endif // ABSL_UTILITY_UTILITY_H_ diff --git a/absl/utility/utility_test.cc b/absl/utility/utility_test.cc index 3c447b20..f044ad64 100644 --- a/absl/utility/utility_test.cc +++ b/absl/utility/utility_test.cc @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -135,7 +135,7 @@ struct PoorStrCat { template <typename Tup, size_t... Is> std::vector<std::string> TupStringVecImpl(const Tup& tup, - absl::index_sequence<Is...>) { + absl::index_sequence<Is...>) { return {Fmt(std::get<Is>(tup))...}; } @@ -341,5 +341,36 @@ TEST(ExchangeTest, MoveOnly) { EXPECT_EQ(1, *b); } +TEST(MakeFromTupleTest, String) { + EXPECT_EQ( + absl::make_from_tuple<std::string>(std::make_tuple("hello world", 5)), + "hello"); +} + +TEST(MakeFromTupleTest, MoveOnlyParameter) { + struct S { + S(std::unique_ptr<int> n, std::unique_ptr<int> m) : value(*n + *m) {} + int value = 0; + }; + auto tup = + std::make_tuple(absl::make_unique<int>(3), absl::make_unique<int>(4)); + auto s = absl::make_from_tuple<S>(std::move(tup)); + EXPECT_EQ(s.value, 7); +} + +TEST(MakeFromTupleTest, NoParameters) { + struct S { + S() : value(1) {} + int value = 2; + }; + EXPECT_EQ(absl::make_from_tuple<S>(std::make_tuple()).value, 1); +} + +TEST(MakeFromTupleTest, Pair) { + EXPECT_EQ( + (absl::make_from_tuple<std::pair<bool, int>>(std::make_tuple(true, 17))), + std::make_pair(true, 17)); +} + } // namespace |